我在 Rust 中创建了一些代码,应该允许我设置递归端口、具有源的端口、可以具有源的端口等。但是,由于借用问题,我实现此结构的唯一方法是通过 RefCell,并且我实际上不想使用 RefCell (它允许我编译在运行时可能会出现恐慌的代码)或 std 中的任何其他库。
问题是,由于借用问题,我似乎无法为我的端口设置值。
struct Port<'a> {
value: i32,
source: Option<&'a Port<'a>>,
}
impl<'a> Port<'a> {
fn new(value: i32) -> Port<'a> {
Port { value, source: None }
}
fn link(&mut self, source: &'a Port<'a>) {
self.source = Some(source);
}
fn get_source_value(&self) -> Option<i32> {
match self.source {
Some(port) => {
let inner_source_value = port.get_source_value();
match inner_source_value {
Some(value) => Some(value),
None => Some(port.value),
}
}
None => None,
}
}
}
fn main() {
let mut top_port = Port::new(10);
let mut sub_port = Port::new(20);
let mut sub_sub_port = Port::new(30);
sub_port.link(&top_port);
sub_sub_port.link(&sub_port);
top_port.value = 400; // can't do this because top_port is borrowed
println!("Value of sub_sub_port's source's source's source: {:?}", sub_sub_port.get_source_value());
}
出现问题的原因是当 sub_port 和 sub_sub_port 链接到 top_port 时,top_port 被借用了。 发生这种情况对我来说并不奇怪,我可以使用 RefCell 和 RC 来解决这个问题。
use std::cell::RefCell;
struct Port<'a> {
value: i32,
source: Option<&'a RefCell<Port<'a>>>,
}
impl<'a> Port<'a> {
fn new(value: i32) -> RefCell<Port<'a>> {
RefCell::new(Port { value, source: None })
}
fn link(&mut self, source: &'a RefCell<Port<'a>>) {
self.source = Some(source);
}
fn get_source_value(&self) -> Option<i32> {
match self.source {
Some(port_ref) => {
let port = port_ref.borrow();
let inner_source_value = port.get_source_value();
match inner_source_value {
Some(value) => Some(value),
None => Some(port.value),
}
}
None => None,
}
}
}
fn main() {
let top_port = Port::new(10);
let sub_port = Port::new(20);
let sub_sub_port = Port::new(30);
sub_sub_port.borrow_mut().link(&sub_port);
sub_port.borrow_mut().link(&top_port);
top_port.borrow_mut().value = 400;
println!("Value of sub_sub_port's source's source's source: {:?}", sub_sub_port.borrow().get_source_value());
}
但是,我的目标是不使用任何标准库功能,并且仅以安全的方式使用 Rust。我可以通过什么方式重组我的代码以避免使用 RefCell 或任何其他标准库?或者更好的是,我可以使用更多的生命周期来修复我的原始代码吗?
提前谢谢您!
您可以使用
AtomicI32
代替 i32
,因为它的 .store()
功能不需要 &mut
。use core::sync::atomic::{AtomicI32, Ordering};
struct Port<'a> {
value: AtomicI32,
source: Option<&'a Port<'a>>,
}
impl<'a> Port<'a> {
fn new(value: i32) -> Port<'a> {
Port {
value: AtomicI32::new(value),
source: None,
}
}
fn link(
&mut self,
source: &'a Port<'a>,
) {
self.source = Some(source);
}
fn get_source_value(&self) -> Option<i32> {
match self.source {
Some(port) => {
let inner_source_value = port.get_source_value();
match inner_source_value {
Some(value) => Some(value),
None => Some(port.value.load(Ordering::Relaxed)),
}
}
None => None,
}
}
}
fn main() {
let top_port = Port::new(10);
let mut sub_port = Port::new(20);
let mut sub_sub_port = Port::new(30);
sub_port.link(&top_port);
sub_sub_port.link(&sub_port);
top_port.value.store(400, Ordering::Relaxed);
println!(
"Value of sub_sub_port's source's source's source: {:?}",
sub_sub_port.get_source_value()
);
}
一般来说,原子操作的效率低于常规操作,因为硬件需要小心谨慎,以便始终以一致的状态向所有内核/CPU 公开数据。在 x86(_64) 上,加载/存储没有区别,但在其他架构上可能是这种情况(但是对于添加、子...总是有惩罚)。
这里使用的relaxed内存顺序不应引入内存障碍,但其他顺序也可能不利于性能。
然后,如果代码不涉及并行上下文,也许按照评论中的建议,core::cell::Cell<i32>
会更高效一些。
.get()
在这里起作用,因为
i32
Copy
。use core::cell::Cell;
struct Port<'a> {
value: Cell<i32>,
source: Option<&'a Port<'a>>,
}
impl<'a> Port<'a> {
fn new(value: i32) -> Port<'a> {
Port {
value: Cell::new(value),
source: None,
}
}
fn link(
&mut self,
source: &'a Port<'a>,
) {
self.source = Some(source);
}
fn get_source_value(&self) -> Option<i32> {
match self.source {
Some(port) => {
let inner_source_value = port.get_source_value();
match inner_source_value {
Some(value) => Some(value),
None => Some(port.value.get()),
}
}
None => None,
}
}
}
fn main() {
let top_port = Port::new(10);
let mut sub_port = Port::new(20);
let mut sub_sub_port = Port::new(30);
sub_port.link(&top_port);
sub_sub_port.link(&sub_port);
top_port.value.set(400);
println!(
"Value of sub_sub_port's source's source's source: {:?}",
sub_sub_port.get_source_value()
);
}