Why does the object address change in different methods?

I have C code to go to. I decided to use mem::uninitialized

to first request memory, then call the C ( UserInit

) function to initialize, then use it (in UserDoSomething

).

The odd thing is that the object addresses differ in UserInit

and UserDoSomething

. Why does he behave this way?

Code C:

typedef struct {
    char* name;
    int32_t age;
} User;

void
UserInit(User* u){
    printf("in init: user addr: %p\n", u);
}

void
UserDoSomething(User* u){
    printf("in do something user addr: %p\n", u);
}

void
UserDestroy(User* u){
    free(u->name);
}

      

Rust FFI:

use std::mem;
use std::os::raw::c_char;
use std::ffi::CString;


#[repr(C)]
pub struct User{
    pub name:   *const c_char,
    pub age:    i32,
}

impl User {
    pub fn new()-> User {

        let ret: User = unsafe { mem::uninitialized() };

        unsafe {
            UserInit(&mut ret as *mut User)
        }

        ret
    }

    pub fn do_something(&mut self){
        unsafe {
            UserDoSomething(self as *mut User)
        }
    }

}
extern "C" {
    pub fn UserInit(u:*mut User);
    pub fn UserDoSomething(u:*mut User);
    pub fn UserDestroy(u:*mut User);
}

      

Rust tests:

mod ffi;

use ffi::User;

fn main() {
    let mut u = User::new();
    u.do_something();
}

      

In theory, it should output the same address, but it doesn't:

> cargo run
     Running `target/debug/learn`
in init: user addr: 0x7fff5b948b80
in do something user addr: 0x7fff5b948ba0

      

+2


source to share


2 answers


This is how Rusta works.

Moreover, this is how C works:

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct {
    char* name;
    int32_t age;
} User;

void
UserInit(User* u){
    printf("in init: user addr: %p\n", u);
}

void
UserDoSomething(User* u){
    printf("in do something user addr: %p\n", u);
}

void
UserDestroy(User* u){
    free(u->name);
}

User rust_like_new(void) {
    User u;
    UserInit(&u);
    return u;
}

int main(int argc, char *argv[]) {
    User u = rust_like_new();
    UserDoSomething(&u);
}

      

in init: user addr:        0x7fff506c1600
in do something user addr: 0x7fff506c1630

      

Typically, you don't need the address of the container, just what it contains.

If I type in the selection User

, the address doesn't change, but if I use Box

( let u = Box::new(User::new())

) it still changes.



The same thing happens in Rust and C. The address itself Box<User>

or User *

will change. The value (pointed thing) Box<User>

or User *

will remain unchanged.

mod ffi {
    use std::mem;
    use std::os::raw::c_char;

    #[repr(C)]
    pub struct User {
        pub name: *const c_char,
        pub age: i32,
    }

    impl User {
        pub fn new() -> Box<User> {
            let mut ret: Box<User> = Box::new(unsafe { mem::uninitialized() });

            unsafe { UserInit(&mut *ret) }

            ret
        }

        pub fn do_something(&mut self) {
            unsafe { UserDoSomething(self) }
        }
    }

    extern "C" {
        pub fn UserInit(u: *mut User);
        pub fn UserDoSomething(u: *mut User);
    }
}

use ffi::User;

fn main() {
    let mut u = User::new();
    u.do_something();
}

      

in init: user addr:        0x10da17000
in do something user addr: 0x10da17000

      

If you pass a reference to User

C before it moves to Box

, then yes, the address will change when it moves to Box

. This would be equivalent to:

User rust_like_new(void) {
    User u;
    UserInit(&u);
    return u;
}

int main(int argc, char *argv[]) {
    User u = rust_like_new();
    User *u2 = malloc(sizeof(User));
    *u2 = u;
    UserDoSomething(u2);
}

      

Note that Rust (and other languages) allow RVO to be executed. However, I believe that printing out the address would disqualify this optimization because the behavior would change if RVO was enabled. You will need to search in the debugger or in the generated assembly.

+4


source


This is how rust works. Objects have no identity; moving them out of a function (for example User::new

) moves them to a new location, thereby changing their address.



If your C code needs stable addresses, you need to make Rust User

contain Box

or a raw pointer.

+3


source







All Articles