如果将结构移动到不同的位置,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的使用
memcpy
和realloc
,
- 基于在移动过程中无法调用
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 接口,我不接受任何移动,只接受(可变)引用。
在用于微控制器的 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的使用
memcpy
和realloc
, - 基于在移动过程中无法调用
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 接口,我不接受任何移动,只接受(可变)引用。