所有权 2 引用与借用
本人大蒟蒻,作为一个学习者稍微记录一下自己的理解。如有错误,请多多指教!
一、所有权与函数
所有权移动到函数参数
现在,我们来看看这个程序:
fn main() {
let s = String::from("hello");
print_string(s);
println!("{}", s);
}
fn print_string(s: String) {
println!("{}", s);
}
编译失败:
PS L:\Projects-Rust\rust-playground> cargo run Compiling rust-playground v0.1.0 (L:\Projects-Rust\rust-playground) error[E0382]: borrow of moved value: `s` --> src\main.rs:6:20 | 2 | let s = String::from("hello"); | - move occurs because `s` has type `String`, which does not implement the `Copy` trait 3 | 4 | print_string(s); | - value moved here 5 | 6 | println!("{}", s); | ^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info) For more information about this error, try `rustc --explain E0382`. error: could not compile `rust-playground` due to previous error
会发现它无法编译,给出的错误提示和 [所有者 1 所有者、移动、克隆](./所有者 1 所有者、移动、克隆.md) 中 **例子2 **的编译失败提示一样,也是 borrow of moved value
。
其实,在将 s
作为参数传给 printString()
的过程中也发生了一次 移动,而且没有再移动回来。
这不难理解,因为函数的参数本质上其实也是个 变量 嘛。
那么怎么办呢?
欸嘿,我再给它移回来不就好了!
通过返回值归还所有权
进行如下修改:
fn main() {
let s = String::from("hello");
let s = print_string(s);
println!("{}", s);
}
fn print_string(s: String) -> String {
println!("{}", s);
s
}
输出:
PS L:\Projects-Rust\rust-playground> cargo run Compiling rust-playground v0.1.0 (L:\Projects-Rust\rust-playground) Finished dev [unoptimized + debuginfo] target(s) in 0.67s Running `target\debug\rust-playground.exe` hello hello
成功运行。
然而,这么写多少有点 呆,有没有更好的办法?答案是有,就是使用 引用。
二、引用与借用
引用 其实像是一个指针,因为其实它是个地址,但是它又和指针不同,因为 它始终保证指向一个有效值。
在声明 函数参数 的时候,可以为 参数类型 前添加一个 &
,表明参数是 引用类型,
对应的,传入参数 时也要在前面加一个 &
,来创建一个对应的 引用:
fn main() {
let s = String::from("hello");
print_string(&s);
println!("{}", s);
}
fn print_string(s: &String) {
println!("{}", s);
}
输出:
PS L:\Projects-Rust\rust-playground> cargo run Compiling rust-playground v0.1.0 (L:\Projects-Rust\rust-playground) Finished dev [unoptimized + debuginfo] target(s) in 0.70s Running `target\debug\rust-playground.exe` hello hello
成功运行,没有问题。
此外,如果想要修改原变量,声明参数 以及 传入参数 时需要把 &
写为 &mut
,这就是 可变引用。
但是要注意,对一个变量,同时只能存在 一个 可变引用 或者 多个 不可变引用。
而什么是 **借用(borrow)**呢?就是指 创建一个引用的行为。
三、悬垂引用
看下面的程序:
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}
编译失败:
PS L:\Projects-Rust\rust-playground> cargo run Compiling rust-playground v0.1.0 (L:\Projects-Rust\rust-playground) error[E0106]: missing lifetime specifier --> src\main.rs:5:16 | 5 | fn dangle() -> &String { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime | 5 | fn dangle() -> &'static String { | ~~~~~~~~ For more information about this error, try `rustc --explain E0106`. error: could not compile `rust-playground` due to previous error
会发现它无法编译,为什么?
记不记得 引用 与 指针 的重要区别?它始终保证指向一个有效值。
关键还是在这一句话里面。
拥有者 s
在函数结束后便退出了作用域,会被销毁,但是返回的是对它的引用,这会导致 引用 指向一片 可能已经被分配给其它持有者 的 内存。那么 Rust 肯定不允许这种事情发生咯。