未定义的引用_Rust 引用和借阅
清单4-5中的元组代码的问题在于,我们必须String将调用返回 给调用函数,因此我们仍然可以String在调用to之后使用calculate_length,因为将String移到了 calculate_length。
这是定义和使用calculate_length将对象的引用作为参数的函数而不是获取值所有权的方法:
文件名:src / main.rs
fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len);}fn calculate_length(s: &String) -> usize { s.len()}首先,请注意,变量声明和函数返回值中的所有元组代码都消失了。其次,请注意,我们&s1进入 calculate_length,在定义上,我们接受&String而不是 String。
这些与符号是reference,它们使您可以引用某些值而无需拥有所有权。图4-5显示了一个示意图。
图4-5:&String s指向String s1
注意:使用&进行引用的反义词是解引用,这是通过解引用运算符来完成的*。我们将在第8章中看到解引用运算符的一些用法,并在第15章中讨论解引用的细节。
让我们在这里仔细看一下函数调用:
let s1 = String::from("hello"); let len = calculate_length(&s1);该&s1语法让我们创建一个基准是指它的值s1 ,但不拥有它。因为它不拥有它,所以当引用超出范围时,它所指向的值将不会被删除。
同样,函数的签名&用于指示参数的类型s是引用。让我们添加一些解释性注释:
fn calculate_length(s: &String) -> usize { // s is a reference to a String s.len()} // Here, s goes out of scope. But because it does not have ownership of what // it refers to, nothing happens.变量s有效的作用域与任何函数参数的作用域相同,但是当变量超出作用域时我们不会删除引用指向的内容,因为我们没有所有权。当函数将引用作为参数而不是实际值作为参数时,我们将不需要返回这些值以归还所有权,因为我们从未拥有过所有权。
我们称将引用作为函数参数借用。与现实生活中一样,如果某人拥有某物,则可以向他们借用。完成后,您必须将其归还。
那么,如果我们尝试修改要借用的内容会怎样?尝试清单4-6中的代码。剧透警报:它不起作用!
文件名:src / main.rs
fn main() { let s = String::from("hello"); change(&s);}fn change(some_string: &String) { some_string.push_str(", world");}清单4-6:尝试修改借入的值
这是错误的结果:
$ cargo run Compiling ownership v0.1.0 (file:///projects/ownership)error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference --> src/main.rs:8:5 |7 | fn change(some_string: &String) { | ------- help: consider changing this to be a mutable reference: `&mut std::string::String`8 | some_string.push_str(", world"); | ^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutableerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0596`.error: could not compile `ownership`.To learn more, run the command again with --verbose.正如变量在默认情况下是不可变的一样,引用也是如此。我们不允许修改引用的内容。
可变引用
我们只需稍作调整就可以解决清单4-6中代码中的错误:
文件名:src / main.rs
fn main() { let mut s = String::from("hello"); change(&mut s);}fn change(some_string: &mut String) { some_string.push_str(", world");}首先,我们必须更改s为mut。然后,我们必须使用创建一个可变引用,&mut s并使用接受一个可变引用some_string: &mut String。
但是可变引用有一个很大的限制:您只能在一个特定范围内对一个特定的数据进行一个可变引用。此代码将失败:
文件名:src / main.rs
let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s; println!("{}, {}", r1, r2);这是错误:
$ cargo run Compiling ownership v0.1.0 (file:///projects/ownership)error[E0499]: cannot borrow `s` as mutable more than once at a time --> src/main.rs:5:14 |4 | let r1 = &mut s; | ------ first mutable borrow occurs here5 | let r2 = &mut s; | ^^^^^^ second mutable borrow occurs here6 | 7 | println!("{}, {}", r1, r2); | -- first borrow later used hereerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0499`.error: could not compile `ownership`.To learn more, run the command again with --verbose.该限制允许突变,但是以非常受控的方式。这是新的Rustaceans苦苦挣扎的事情,因为大多数语言都允许您随时更改。
具有此限制的好处是Rust可以防止在编译时发生数据争用。一个数据的比赛相似,竞争条件,当这三种行为的发生情况:
- 两个或多个指针同时访问相同的数据。
- 至少有一个指针用于写入数据。
- 没有用于同步对数据的访问的机制。
数据争用会导致未定义的行为,并且在尝试在运行时进行跟踪时可能会难以诊断和修复。Rust阻止了此问题的发生,因为它甚至不会与数据竞争一起编译代码!
与往常一样,我们可以使用大括号创建新的范围,从而允许多个可变引用,而不能同时引用:
let mut s = String::from("hello"); { let r1 = &mut s; } // r1 goes out of scope here, so we can make a new reference with no problems. let r2 = &mut s;对于组合可变引用和不可变引用,存在类似的规则。此代码导致错误:
let mut s = String::from("hello"); let r1 = &s; // no problem let r2 = &s; // no problem let r3 = &mut s; // BIG PROBLEM println!("{}, {}, and {}", r1, r2, r3);这是错误:
$ cargo run Compiling ownership v0.1.0 (file:///projects/ownership)error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable --> src/main.rs:6:14 |4 | let r1 = &s; // no problem | -- immutable borrow occurs here5 | let r2 = &s; // no problem6 | let r3 = &mut s; // BIG PROBLEM | ^^^^^^ mutable borrow occurs here7 | 8 | println!("{}, {}, and {}", r1, r2, r3); | -- immutable borrow later used hereerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0502`.error: could not compile `ownership`.To learn more, run the command again with --verbose.ew!当我们拥有不变的参考时,我们也不能拥有可变的参考。不变引用的用户不要期望值会突然从它们下面改变!但是,可以使用多个不可变的引用,因为没有人会影响其他任何人对数据的读取。
请注意,引用的范围从引入它的地方开始,一直持续到最后一次使用该引用。例如,此代码将被编译,因为不可变引用的最后一次使用发生在引入可变引用之前:
let mut s = String::from("hello"); let r1 = &s; // no problem let r2 = &s; // no problem println!("{} and {}", r1, r2); // r1 and r2 are no longer used after this point let r3 = &mut s; // no problem println!("{}", r3);不可改变的引用的范围r1和r2结束后println! ,他们最后被使用,这是可变的引用之前r3被创建。这些范围不重叠,因此允许使用此代码。
即使借用错误有时可能令人沮丧,但请记住,Rust编译器尽早(在编译时而不是在运行时)指出了潜在的错误,并确切地指出了问题出在哪里。然后,您不必追踪为什么您的数据与您想象的不一样。
悬挂参考
在带有指针的语言中,很容易错误地创建一个悬空指针,即通过在保留指向该内存的指针的同时释放一些内存来引用可能已分配给他人的内存中某个位置的指针。相比之下,在Rust中,编译器保证引用永远不会成为悬挂引用:如果您对某些数据有引用,则编译器将确保数据不会超出对数据的引用范围。
让我们尝试创建一个悬空的引用,Rust将通过编译时错误防止它:
文件名:src / main.rs
fn main() { let reference_to_nothing = dangle();}fn dangle() -> &String { let s = String::from("hello"); &s}这是错误:
$ cargo run Compiling ownership v0.1.0 (file:///projects/ownership)error[E0106]: missing lifetime specifier --> src/main.rs:5:16 |5 | fn dangle() -> &String { | ^ help: consider giving it a 'static lifetime: `&'static` | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed fromerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0106`.error: could not compile `ownership`.To learn more, run the command again with --verbose.此错误消息引用的是我们尚未涵盖的功能:生存期。我们将在第10章中详细讨论生命周期。但是,如果您忽略有关生命周期的部分,则消息的确包含了导致此代码出现问题的关键:
this function's return type contains a borrowed value, but there is no valuefor it to be borrowed from.让我们仔细看看dangle代码的每个阶段到底发生了什么 :
文件名:src / main.rs
fn dangle() -> &String { // dangle returns a reference to a String let s = String::from("hello"); // s is a new String &s // we return a reference to the String, s} // Here, s goes out of scope, and is dropped. Its memory goes away. // Danger!因为s是在内部创建的dangle,当代码dangle完成时, s将被释放。但是我们试图返回对它的引用。这意味着此引用将指向无效String。那不好!Rust不会让我们这样做。
这里的解决方案是String直接返回:
fn no_dangle() -> String { let s = String::from("hello"); s}这可以正常工作。所有权被移出,没有任何东西被释放。
总结
以上是生活随笔为你收集整理的未定义的引用_Rust 引用和借阅的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 网页设计作业_Dreamweaver简单
- 下一篇: uniapp动态显示数组_uni-app