Semantics
Rust实现了所谓的Affine Type System:
仿射类型是施加较弱约束的线性类型的一个版本,对应于仿射逻辑.An affine resource can only be used once,而线性的必须使用一次.
不是Copy
并且因此被移动的类型是仿射类型:可以使用它们一次,也可以不使用,其他什么都不用.
Rust在其以所有权为中心的世界观(*)中将其列为transfer of ownership.
(*)一些研究Rust 的人比我在CS中更合格,他们故意实施了仿射类型系统;然而,与Haskell揭露math-y/cs-y概念相反,Rust倾向于揭露更务实的概念.
Note: it could be argued that Affine Types returned from a function tagged with 100 are actually Linear Types from my reading.
Implementation
视情况而定.请记住,Rust是一种为速度而构建的语言,这里有许多优化过程,这取决于所使用的compiler个优化过程(在我们的例子中,是rustc+LLVM).
在功能体(playground)内:
fn main() {
let s = "Hello, World!".to_string();
let t = s;
println!("{}", t);
}
如果判断LLVM IR(在调试中),您将看到:
%_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从"Hello, World!".to_string()
到s
再到t
的结果中调用memcpy
.虽然它可能看起来效率很低,但在发布模式下判断相同的IR时,您会意识到LLVM已完全删除了副本(意识到s
未使用).
调用函数时也会出现同样的情况:理论上,您将对象"移动"到函数堆栈框架中,但是在实践中,如果对象很大,rustc编译器可能会转而传递指针.
另一种情况是函数的返回值为returning,但即使如此,编译器也可能应用"返回值优化",并直接在调用方的堆栈框架中构建——也就是说,调用方传递一个指针,将返回值写入其中,该指针在没有中间存储的情况下使用.
Road的所有权/borrow 约束使得优化难以在C++中实现(也有RVO,但在很多情况下不能应用).
因此,摘要版:
- 移动大型对象效率很低,但有许多优化措施可能会完全避免移动
- 移动涉及
std::mem::size_of::<T>()
个字节中的memcpy
个字节,因此移动一个大的String
是有效的,因为它只复制几个字节,而不管它们持有的分配缓冲区大小如何