为什么Rust中不允许使用此语法:
fn main() {
let a = String::from("ping");
let b = a;
println!("{{{}, {}}}", a, b);
}
当我尝试编译这段代码时,我得到了:
错误[E0382]:使用移动值:
a
-
src / main.rs:5:28
|
3 |设b = a;
| - Value 移到这里
4 |
5 | println!(“{{{},{}}}”,a,b);
| ^移动后使用的值
|
=注意:移动发生是因为a
的类型为std :: string :: String
,它没有实现Copy
特性
实际上,我们可以简单地创建一个引用 - 在运行时它是不一样的:
fn main() {
let a = String::from("ping");
let b = &a;
println!("{{{}, {}}}", a, b);
}
它有效:
{ping,ping}
根据the Rust Book,它是为了避免双重免费错误,因为Rust的变量是通过引用而不是按值复制的 . Rust将使第一个对象无效并使其无法使用......
我们必须做这样的事情:
我喜欢通过引用复制的想法,但为什么会自动使第一个无效?
应该可以通过不同的方法避免双重自由 . 例如,C已经有了一个很好的工具来允许多个空闲调用...只有当没有其他指针指向对象时,shared_ptr调用才会自由 - 它看起来与我们实际做的非常相似,区别在于 shared_ptr
一个柜台 .
例如,我们可以在编译期间计算每个对象的引用数,并仅在最后一个引用超出范围时调用 free
.
但鲁斯特是一种年轻的语言;也许他们没有时间实施类似的东西? Rust是否计划允许第二次引用对象而不使第一个引用无效,或者我们应该习惯只使用引用的引用?
2 回答
Rc或Arc是
shared_ptr
的替代品 . 您选择哪种方法取决于共享数据所需的线程安全级别;Rc
适用于非线程情况,Arc
适用于需要线程的情况:像
shared_ptr
一样, not 复制String
本身 . 它只会在调用clone
时在运行时增加引用计数器,并在每个副本超出范围时减少计数器 .与
shared_ptr
不同,Rc
和Arc
具有更好的线程语义 . shared_ptr is semi-thread-safe .shared_ptr
的引用计数器本身是线程安全的,但共享数据不是"magically"使线程安全 .如果在线程程序中使用
shared_ptr
,仍然需要做更多的工作来确保它不需要's safe. In a non-threaded program, you are paying for some thread-safety you don' .如果您希望允许变更共享值,则还需要切换到运行时借用检查 . 这是由
Cell
,RefCell
,Mutex
等类型提供的._RefCell
适用于String
和Rc
:'s almost exactly what Rust does with references. It doesn'实际上使用了一个计数器,但它只允许您使用对值的引用,同时保证该值保持在同一个内存地址 .
C的
shared_ptr
在编译时做了 not 这样做 .shared_ptr
,Rc
和Arc
都是维护计数器的运行时构造 .这正是Rust对引用所做的,以及你已经做过的事情:
更好的是,只要
a
不再有效,编译器就会阻止您使用b
.这不是真的 . 分配值时,值的所有权将传输到新变量 . 从语义上讲,变量的内存地址已经改变,因此读取该地址可能会导致内存不安全 .
是的,尽可能使用参考是最惯用的选择 . 这些需要零运行时开销,编译器会告诉您有关错误的信息,而不是在运行时遇到错误 .
肯定有
Rc
或Arc
有用的时候 . 通常它们是循环数据结构所必需的 . 如果你不能得到明确的工作参考,你不应该对使用它们感到不舒服 .这有点不利,因为额外的间接是不幸的 . 如果你真的需要,你可以减少它 . 如果您不需要修改字符串,则可以切换到
Rc<str>
:如果你需要为了保持修改
String
的能力,你也可以明确地将&Rc<T>
转换为&T
:也可以看看:
What is the right smart pointer to have multiple strong references and allow mutability?
When I can use either Cell or RefCell, which should I choose?
Situations where Cell or RefCell is the best choice
Why is it discouraged to accept a reference to a String (&String), Vec (&Vec) or Box (&Box) as a function argument?
How to build an Rc<str> or Rc<[T]>?
CppCon 2017: Louis Brandy “Curiously Recurring C++ Bugs at Facebook”
你走在正确的轨道上!这就是Rc的用途 . 它是一种智能指针类型,与C中的
std::shared_ptr
非常相似 . 它只在最后一个指针实例超出范围后才释放内存:由于您只能获得对
Rc
's contents (it'共享的不可变访问权限,并且Rust中禁止共享可变性,因此您需要内部可变性才能更改其内容,通过Cell或RefCell实现:但大多数时候,您根本不需要使用
Rc
(或其线程安全兄弟Arc) . Rust的所有权模型主要允许您通过在一个地方声明String
实例并在其他地方使用对它的引用来避免引用计数的开销,就像您在第二个代码段中所做的那样 . 尝试着重于此并仅在必要时使用Rc
,例如当您实现类似图形的结构时 .