struct Foo { ... }
fn create_foo() -> Foo {
let foo = Foo { ... };
foo
}
let f = create_foo(); // Perform `memcpy`!
The addresses of f
and foo
are different since let f = create_foo();
performs memcpy
. The whole data of foo
will be copied to a new place pointed by f
and then the memory of foo
in stack will be abandoned. That's how normal function calls works, if it doesn't use any variable in heap.
struct Foo { ... }
fn create_foo() -> Foo {
let foo = Foo { ... };
register_pointer(&foo); // Cast to *const Foo automatically.
foo
}
fn register_pointer(ptr: *const Foo) {
// Save the `ptr` to somewhere and
// use this `ptr` to get a Foo instance later.
}
fn get_foo_ref() -> &Foo {
// Convert the `ptr` saved in register_pointer
// to a reference of the Foo instance.
}
let f = create_foo(); // Perform `memcpy`!
let f_ref = get_foo_ref(); // Likely to cause a segmentation fault!
Therefore, the above code is very likely to cause a segmentation fault since get_foo_ref()
will try to convert a invalid pointer pointing a memory that was marked unused after executing let f = create_foo();
to a Foo
instance.
The runnable sample code for this problem is init_in_new_wrong.rs. The solution for init_in_new_wrong.rs is init_in_new.rs. Clone this gist repo, go to the repo folder and run ```$ make`` to see the results.
- makefile: Commands to run the examples.
- new.rs: Prove the idea above with struct method.
- new.c: Compare results of new.rs with how similar code works in C.
- new.cpp: Compare results of new.rs how similar code works in C++.
- init_in_new_wrong.rs: The sample code to demonstrate the idea above.
- init_in_new.rs: The solution for init_in_new_wrong.rs.
The reason I create this gist repo is to simplify what the problem I have when I misuse the ::new(...)
struct method in my strcut
. I have a strcut
and it has a struct method called ::new(...)
. In the ::new(...)
, I create a struct instance and use the pointer of this created instance as the callback target whose type is *const c_void
to an external C library, then the created struct instance is returned outside. When callback is fired from the external C library, it will convert the registered target pointer to a struct instance so that it knows which variable calls it. It looks logically reasonable, but it doesn't work. It causes a memory error when converting the registered target pointer to a struct instance. The memory pointed by the registered target pointer is abandoned after finishing executing ::new(...)
. That's why it doesn't work.
The sample code itself explains this problem well. See the code here.