在Rust中,有两种可能性可供引用
&
运算符从值中借用所有权。 &mut
运算符从值中可变地借用所有权。 Rust documentation about borrowing rules说:
First, any borrow must last for a scope no greater than that of the owner. Second, you may have one or the other of these two kinds of borrows, but not both at the same time:
- one or more references (
&T
) to a resource,- exactly one mutable reference (
&mut T
).
我相信引用是创建指向该值的指针并通过该指针访问该值。如果存在更简单的等效实现,则可以由编译器对其进行优化。
但是,我不了解 move 的含义以及它的实现方式。
对于实现
Copy
特征的类型,它意味着复制例如通过从源代码或memcpy()
逐级分配struct。对于小型结构或基元,此副本非常有效。对于,请 move 吗?
这个问题不是What are move semantics?的重复,因为Rust和C++是不同的语言,并且两者之间的 move 语义也不同。
最佳答案
语义
Rust实现了所谓的Affine Type System:
Affine types are a version of linear types imposing weaker constraints, corresponding to affine logic. An affine resource can only be used once, while a linear one must be used once.
不是
Copy
并因此被 move 的类型是仿射类型:您可以使用一次,也可以永不使用它们,别无其他。在以所有权为中心的世界观(*)中,Rust将其视为所有权转移。
(*)一些从事Rust的人员比我在CS方面的资格高得多,他们有意识地实现了仿射类型系统;然而,与Haskell揭露数学-y/cs-y概念相反,Rust倾向于揭露更多实用的概念。
注意:从我的阅读中可以看出,从用
#[must_use]
标记的函数返回的仿射类型实际上是线性类型。实现
这取决于。请记住,Rust是一种为速度而设计的语言,这里有许多优化途径在起作用,这取决于所使用的编译器(在我们的示例中为Rustc + LLVM)。
在函数体内(playground):
fn main() {
let s = "Hello, World!".to_string();
let t = s;
println!("{}", t);
}
如果检查LLVM IR(在Debug中),则会看到:
%_5 = alloca %"alloc::string::String", align 8
%t = alloca %"alloc::string::String", align 8
%s = alloca %"alloc::string::String", align 8
%0 = bitcast %"alloc::string::String"* %s to i8*
%1 = bitcast %"alloc::string::String"* %_5 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* %0, i64 24, i32 8, i1 false)
%2 = bitcast %"alloc::string::String"* %_5 to i8*
%3 = bitcast %"alloc::string::String"* %t to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %3, i8* %2, i64 24, i32 8, i1 false)
在幕后,rustc从
memcpy
的结果调用"Hello, World!".to_string()
到s
,然后再调用t
。尽管看起来效率低下,但在Release模式下检查相同的IR,您会发现LLVM已完全删除了副本(意识到s
未被使用)。调用函数时会发生相同的情况:理论上,您将对象“move ”到函数堆栈框架中,但是实际上,如果对象较大,则rustc编译器可能会改为传递指针。
另一种情况是从函数返回,但是即使这样,编译器仍可能会应用“返回值优化”并直接在调用者的堆栈框架中进行构建-也就是说,调用者传递了一个用于写入返回值的指针,中间存储。
Rust的所有权/借用限制使优化难以在C++中实现(C++也具有RVO,但在很多情况下无法应用)。
因此,摘要版本:
memcpy
字节的std::mem::size_of::<T>()
字节,因此 move 较大的String
是有效的,因为它仅复制了几个字节,无论它们保存在关于rust - Rust中的 move 语义是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60527138/