gpt4 book ai didi

scope - C++ 的 shared_ptr 在 Rust 中的等价物是什么?

转载 作者:行者123 更新时间:2023-11-29 08:01:25 26 4
gpt4 key购买 nike

为什么这种语法在 Rust 中不允许:

fn main() {
let a = String::from("ping");
let b = a;

println!("{{{}, {}}}", a, b);
}

当我尝试编译这段代码时,我得到:

error[E0382]: use of moved value: `a`
--> src/main.rs:5:28
|
3 | let b = a;
| - value moved here
4 |
5 | println!("{{{}, {}}}", a, b);
| ^ value used here after move
|
= note: move occurs because `a` has type `std::string::String`, which does not implement the `Copy` trait

事实上,我们可以简单地做一个引用——它在运行时并不相同:

fn main() {
let a = String::from("ping");
let b = &a;

println!("{{{}, {}}}", a, b);
}

它有效:

{ping, ping}

根据 the Rust Book这是为了避免双重错误,因为 Rust 的变量是通过引用而不是通过值来复制的。 Rust 只会使第一个对象无效并使其无法使用...

enter image description here

我们必须做这样的事情:

enter image description here

我喜欢按引用复制的想法,但为什么自动使第一个无效?

应该可以用不同的方法避免双重释放。例如,C++ 已经有一个很棒的工具可以允许多次免费调用... shared_ptr仅当没有其他指针指向该对象时才调用 free - 这似乎与我们实际所做的非常相似,不同之处在于 shared_ptr 有一个计数器。

例如,我们可以在编译期间统计每个对象的引用次数,只有当最后一个引用超出范围时才调用free

但是 Rust 是一门年轻的语言;也许他们没有时间实现类似的东西? Rust 是否计划允许对对象的第二次引用而不会使第一个引用无效,或者我们应该养成只使用引用的引用的习惯?

最佳答案

或者 Rc Arc shared_ptr 的替代品.选择哪种取决于共享数据所需的线程安全级别; Rc适用于非线程外壳和 Arc是你需要线程的时候:

use std::rc::Rc;

fn main() {
let a = Rc::new(String::from("ping"));
let b = a.clone();

println!("{{{}, {}}}", a, b);
}

shared_ptr , 这复制 String本身。当 clone 时,它只会在运行时增加一个引用计数器。被调用并在每个副本超出范围时减少计数器。

不同于shared_ptr , RcArc有更好的线程语义。 shared_ptr is semi-thread-safe . shared_ptr的引用计数器本身是线程安全的,但共享数据并不是“神奇地”使线程安全的。

如果您使用 shared_ptr在线程程序中,您还有更多工作要做以确保其安全。在非线程程序中,您正在为一些不需要的线程安全付出代价。

如果您希望允许更改共享值,您还需要切换到运行时借用检查。这是由 Cell 等类型提供的, RefCell , MutexRefCell适用于 StringRc :

use std::cell::RefCell;
use std::rc::Rc;

fn main() {
let a = Rc::new(RefCell::new(String::from("ping")));
let b = a.clone();

println!("{{{}, {}}}", a.borrow(), b.borrow());

a.borrow_mut().push_str("pong");
println!("{{{}, {}}}", a.borrow(), b.borrow());
}

we can count the number of references to each object during the compilation time and call free only when the last reference goes out of the scope.

这几乎正是 Rust 对引用所做的。它实际上不使用计数器,但它只允许您使用对值的引用,同时保证该值保持在相同的内存地址。

C++ 的 shared_ptr 不会在编译时执行此操作。 shared_ptr , Rc , 和 Arc都是维护计数器的运行时构造。

Is it possible to make a reference to the object without invalidate the first reference?

这正是 Rust 对引用所做的,以及您已经完成的:

fn main() {
let a = String::from("ping");
let b = &a;

println!("{{{}, {}}}", a, b);
}

更好的是,编译器会阻止你使用 b尽快a不再有效。

because Rust's variables are copied by reference instead of by value

这不是真的。当您分配一个值时,该值的所有权将转移给新变量。从语义上讲,变量的内存地址已更改,因此读取该地址可能会导致内存不安全。

should we take the habit to only work with a reference

是的,尽可能使用引用是最惯用的选择。这些需要零运行时开销,编译器会告诉您错误,而不是在运行时遇到它们。

肯定有的时候RcArc很有用。循环数据结构通常需要它们。如果您无法获得工作的简单引用,您不应该对使用它们感到难过。

with a reference of a reference?

这有点不利,因为额外的间接寻址是不幸的。如果你真的需要,你可以减少它。如果你不需要修改字符串,你可以切换到一个Rc<str>相反:

use std::rc::Rc;

fn main() {
let a: Rc<str> = Rc::from("ping");
let b = a.clone();

println!("{{{}, {}}}", a, b);
}

如果需要保留修改String的能力有时,您还可以显式转换 &Rc<T>&T :

use std::rc::Rc;

fn main() {
let a = Rc::new(String::from("ping"));
let b = a.clone();

let a_s: &str = &*a;
let b_s: &str = &*b;

println!("{{{}, {}}}", a_s, b_s);
}

另见:

关于scope - C++ 的 shared_ptr 在 Rust 中的等价物是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49834414/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com