如果将结构移动到不同的位置,Rust 中是否有任何特性会得到通知?

Is there any trait in Rust that gets notified if a structure is moved to a different location?

在用于微控制器的 Rust 和 C 代码混合中,我有一个 Rust 数据结构,我必须让我的程序的 C 部分知道它。我得到了这个工作。

pub struct SerialPort {
    // ... some attributes ...
}

我尝试重构 Rust 代码并希望在函数中生成 Rust 结构,该函数也在我的 C 代码中注册回调。

pub fn setup_new_port() -> SerialPort {
    let port = SerialPort::new();
    port.register_with_c_code();
    port
}

这行不通,因为 Rust 会在函数 returns 时将我的 SerialPort 类型变量的内存内容移动到不同的位置。在 C 代码中注册的地址指向 setup_new_port() 的(现已释放的)堆栈帧,而它已移动到调用者的堆栈帧,因此我的 C 代码将访问无效地址。

是否有任何我可以实现的特征在移动发生时得到通知?我可以在实现特征时调整在我的 C 代码中注册的地址。

我知道我可以通过在堆上分配我的结构来防止移动发生,但我想避免这种情况,因为代码在微控制器上运行并且动态内存分配可能并不总是存在。

不,设计使然。

Rust 是专门设计的,没有所谓的移动构造函数,在 Rust 中,移动是按位复制,允许许多优化:

  • 如C的使用memcpyrealloc,
  • 基于在移动过程中无法调用 panic! 这一事实的优化,
  • ...

实际上,C++中有提议改造这样的设计(is_relocatable),性能是主要驱动因素。


那又怎么样?

Mutex那样做!也就是说,Box 任何不应该移动的东西,并将其隔离在不透明的 struct 中,这样任何人都无法将内容移出盒子。

如果堆分配不可用...那么您可能需要查看 PinMut,或者借用该结构。借来的不能动

我看到的唯一解决方案(我没有说这是唯一的解决方案)是在堆栈中有一个竞技场,或者另一种占位符:

#[derive(Default)]
pub struct SerialPort {
    _dummy: i32,
}

pub fn setup_new_port(placeholder: &mut Option<SerialPort>) -> &SerialPort {
    let port = SerialPort::default();
    std::mem::replace(placeholder, Some(port));
    let port = placeholder.as_ref().unwrap();
    println!("address: {:p}", port);
    //port.register_with_c_code();
    port
}

fn main() {
    let mut placeholder = None;

    let port = setup_new_port(&mut placeholder);
    println!("address: {:p}", port);
}

这两行 println! 打印相同的值。

我在互联网上搜索了一个在堆栈中实现竞技场的板条箱,但到目前为止一无所获。

我会把它分开。 我会在某个地方(在堆栈上)创建一个 Serialport 的实例,然后调用 register_with_c_code()。此外,我会 impl Drop 用于串行端口,因此当串行端口超出范围时,c_code 将被分离。

对于您的 Serialport 接口,我不接受任何移动,只接受(可变)引用。