Answer the question
In order to leave comments, you need to log in
Rust unsafe, what are the pitfalls and how to approach C API design?
I wrote this post to find answers to my questions.
They are related to Rust and its unsafe bindings to C.
The further I go, the more painful it is for me to look at something.
Maybe from my stupidity, maybe from something else.
My goal is to build a small binding, parts of Raylib.
The first thing I will note. The bindgen tool doesn't work for me.
I don't like 2,000 lines of constants (a figure from a particularly curious Raylib binding).
Of which 1900 will never be used by me.
Maybe I don’t understand something and he doesn’t sew them somehow?
But it's the same with functions/structs.
Let me remind you again that I'm trying to bind a small part of Raylib.
I have experience developing bindings in Python and Golang from scratch.
I also used this binding in C, C++, D (a little bit).
There are many reasons why I work with this library.
From license to personal interests and experience.
At one point, I decided to think of Rust as a replacement for Go.
I like to make games, for myself, for friends, partly for business.
Looking at Rust, I really want to program on it, but ...
Unsafe poke me so that I would run.
Probably not only in Rust, but I only encountered this in Rust.
Let 's take a simple C example:
RLAPI void DrawRectangle(int posX, int posY, int width, int height, Color color);
Rust:
extern "C" {
fn DrawRectangle(pos_x: c_int, pos_y: c_int, width: c_int, height: c_int, color: Color);
}
All is well, with one amendment. If I change anything. Rust will only praise me.
In Color, I can specify &Color and send a pointer there. This is how it works in many places.
Results.. The most different. From the wrong color that the pointer set (apparently the pointer has become a color), to the working code. By specifying a reference to the Sound structure.
I got a working result. As if everything is fine. And that gets me a little.
Okay, these are the little things. We need to be a little more careful about the C call block itself.
Although how can I specify a link to the structure, but not send the structure, and this works somewhere, but not somewhere?
Well, how? Okay, let's move on ...
When we exported everything correctly. I started to wonder what a safe API to unsafe means.
After reading manuals from Rust and a couple of books. Looking at someone else's code. I completely lost my sense of reality.
In one book, the emphasis was on the fact that the initial function should control the children.
To be more precise, we must make sure that the language itself speaks to us. You can't use function B
until you call A. Sounds logical, in practice.. Something like this.
This is pseudocode, but it +- reflects reality.
// привычное api на всех языках.
fn main() {
window_init(1152,648,"Hello, world!");
set_target_fps(60);
while !window_should_close() {
begin_drawing();
clear_background(&Color{r:0,g:0,b:0,a:255});
draw_texture(&testr,10,10,&Color{r:255,g:255,b:255,a:255});
end_drawing();
}
close_window();
}
// На Rust пытаются сделать так.
fn main() {
window_init(1152,648,"Hello, world!");
set_target_fps(60);
while !window_should_close() {
begin_drawing();
clear_background(&Color{r:0,g:0,b:0,a:255});
draw_texture(&testr,10,10,&Color{r:255,g:255,b:255,a:255});
}
}
window_init(1152,648,"Hello, world!");
bool = is_window_ready()
extern crate raylib;
use raylib::prelude::*;
use structopt::StructOpt;
mod options;
fn main() {
let opt = options::Opt::from_args();
let (mut rl, thread) = opt.open_window("Logo");
let (w, h) = (opt.width, opt.height);
let rust_orange = Color::new(222, 165, 132, 255);
let ray_white = Color::new(255, 255, 255, 255);
rl.set_target_fps(60);
while !rl.window_should_close() {
// Detect window close button or ESC key
let mut d = rl.begin_drawing(&thread);
d.clear_background(ray_white);
d.draw_rectangle(w / 2 - 128, h / 2 - 128, 256, 256, rust_orange);
d.draw_rectangle(w / 2 - 112, h / 2 - 112, 224, 224, ray_white);
d.draw_text("rust", w / 2 - 69, h / 2 + 18, 50, rust_orange);
d.draw_text("raylib", w / 2 - 44, h / 2 + 48, 50, rust_orange);
}
}
Answer the question
In order to leave comments, you need to log in
Don't fool yourself and take a ready-made safe wrapper over raylib
https://crates.io/crates/raylib
And the guide on working with unsafe is rustonomicon
unsafe itself just allows you to use raw pointers + call other unsafe functions.
A safe wrapper is when you, with the help of types and all sorts of validations, guarantee correct use.
Here's an example from the one above:
use raylib::prelude::*;
fn main() {
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title("Hello, World")
.build();
while !rl.window_should_close() {
let mut d = rl.begin_drawing(&thread);
d.clear_background(Color::WHITE);
d.draw_text("Hello, world!", 12, 12, 20, Color::BLACK);
}
}
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question