Rust 为什么一个对象的地址会随着方法的不同而变化?

Rust 为什么一个对象的地址会随着方法的不同而变化?,rust,Rust,我有C代码要桥接。我选择使用mem::uninitialized首先声明内存,然后调用C函数(UserInit)进行初始化,然后使用它(在UserDoSomething中) 奇怪的是对象的地址在UserInit和UserDoSomething中不同。为什么它会这样 C代码: typedef struct { char* name; int32_t age; } User; void UserInit(User* u){ printf("in init: user add

我有C代码要桥接。我选择使用
mem::uninitialized
首先声明内存,然后调用C函数(
UserInit
)进行初始化,然后使用它(在
UserDoSomething
中)

奇怪的是对象的地址在
UserInit
UserDoSomething
中不同。为什么它会这样

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);
}
英国外交部:

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);
}
锈蚀试验:

mod ffi;

use ffi::User;

fn main() {
    let mut u = User::new();
    u.do_something();
}
理论上,它应该输出相同的地址,但它不:

>货物运输
运行“目标/调试/学习”`
在init中:用户地址:0x7fff5b948b80
在do something用户地址中:0x7fff5b948ba0

锈就是这样起作用的。对象没有标识;将它们移出函数(例如
User::new
)会将它们移动到新位置,从而更改它们的地址

如果您的C代码需要稳定的地址,则需要使Rust
用户
包含一个
或原始指针


锈就是这样起作用的

除此之外,C也是这样工作的:

通常,您不关心容器的地址,只关心它包含的内容

如果我堆分配
User
,地址不会更改,但是如果我使用
Box
让u=Box::new(User::new())
),地址仍然会更改

Rust和C中也会发生同样的情况。
用户*
本身的地址会改变。
用户*
的值(指向的对象)将保持一致

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();
}
如果在
移动到
之前将对
用户
的引用传递到C,则是的,地址将在移动到
时更改。这相当于:

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);
}

请注意,Rust(和其他语言)允许执行。但是,我相信打印出地址将取消此优化的资格,因为如果启用RVO,行为将发生变化。您需要查看调试器或生成的程序集。

谢谢!我发现同一范围内obj的引用具有相同的地址。可能我必须编写一个显式方法
User::Init(&mut self)
来处理它。你如何看待这种方法?为什么在C端的地址很重要?一个后续问题:好的观点!一个主要的区别是,如果我堆alloc User,地址就不会改变。但如果我像这样在rust中使用Box:
让u=Box::new(User::new())
,它仍然会发生变化。回答很好。非常感谢你的详细解释。
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);
}