当变量传递到函数时,它可以通过引用(borrow,通过&或&mut)传递,或者移动(move,没有引用)。
Borrow(借用): 传递的是数据的引用。数据在函数调用后仍然可用,但对于不可变引用,内容不能被修改;对于可变引用,最多只能有一个活动引用。
fn read_length(s: &String) -> usize { s.len() }
Move(移动): 变量会完全“移入”函数。传递后,您将无法使用原始变量——它已被移动,任何尝试引用将导致编译错误。
fn destroy(s: String) { println!("{}", s); } // s将在退出时被销毁 let s = String::from("world"); destroy(s); // s不再可以使用
这可以防止出现双重释放内存和其他所有权错误。
在通过值传递(move)后,我可以使用变量吗?
不可以! 通过值传递变量后——例如String——原始变量变得无效:
let s = String::from("abc"); consume(s); // s在这里不再有效 println!("{}", s); // 编译错误
许多人将其与Copy类型的行为混淆(例如i32),在传递后变量仍然有效。
故事
一位年轻程序员编写了一个字符串处理函数,其参数为String而不是&String。结果是,调用函数后原始字符串变得不可用,导致双重加载和额外的内存分配在日志服务器中。
故事
在一个微服务中,关键资源通过move传递到不同的线程,然后在主线程中访问这个资源时导致编译错误。不得不紧急重构架构,以便通过引用或使用Arc类型的包装传递资源。
故事
在内部事件解析器中,在处理数据时,变量被快速移动到闭包中,之后在闭包外的引用会导致编译错误。这个问题只在审查时被发现——引入了强制使用借用的风格,对于那些应该比局部函数更长生命周期的数据。