在Rust中借用 在变量前加& 就变成了借用 不会产生所有权的转移, 在其他语言我们称这样的变量是引用, 但是Rust解释器中明确表明 就称其为借用, Rust通过借用Borrow概念达成减少所有权传递程序复杂的目的: **获取变量的引用, 称之为借用 **, 可以很好的理解, 我们上学忘记带铅笔, 可以跟朋友同学去借, 但是在使用完成后, 要物归原主.这里排除老赖等极端情况...
引用与解引用常规的引用是一个指针类型, 指向了对象存储的内存地址。 在下面我们创建一个x i32值的引用 y, 然后使用解引用得到内存中真实的数据
let x: i32 = 5; let y = &x assert_eq!(5, x) assert_eq!(5, *y) // y 是 5这个i32类型的数据内存地址 *y就是反引用得到的就是内存中的真实的数据5当然这个时候 x 和 y也都可以正常打印出来因为引用不会涉及到所有权转移的问题 x 的不会出现失效的情况
不可变引用 fn main() { let s1 = String::from("hello"); let len = calculate_length(&s1); // 将s1的引用传递给函数 println!("The length of '{}' is {}.", s1, len); } // 函数接受 String的引用 返回一个 usize类型 usize就是无符号的根据操作系统位数生成的整数类型 例如我们操作系统是64位 那就是u64 fn calculate_length(s: &String) -> usize { s.len() }// 因为传入的是引用类型 所以函数执行完成后不会释放drop掉s 什么也不会发生, 通过下面看一下类型引用的整体结构 s s1 ptr -> ptr -> 0 h len 1 e cap 2 l 3 l 4 o上述场景我们函数传参的简易性有了, 我们不觉的想到如果想修改 数据的值可以吗, 接下来我们看下面的代码:
fn main() { let s1 = String::from("hello"); calculate_length(&s1); // 将s1的引用传递给函数 } fn calculate_length(s: &String) { s.push_str(" world!"); // 再此处修改数据 }push_str处就会报错。因为在rust中定义的引用 都是不可以更改原来的数据的 就好像我们去图书馆借书 看可以 但是如果在毁坏书籍 乱涂乱画是不被允许的, 那如何我想画就画呢? Rust 也帮我们解决了, 那就是定义引用的时候声明他是一个可变引用
可变引用 fn main() { let mut s1 = String::from("hello"); // 声明s1为可变参数 calculate_length(&mut s1); // 将s1的引用传递给函数 } fn calculate_length(s: &mut String) { // 声明传递的参数必须是一个可变的String类型参数 s.push_str(" world!"); // 再此处修改数据 }这段代码就可以完美的运行了
但是可变引用必须遵从Rust的一个原则:可变引用同时只能存在一个, 也就是在同一个作用域中, 一个数据只能有一个可变的引用, 同时不可变可以拥有多个
也就是说 一本书我借给多个人 , 你们一堆人可以一起看, 其中只能有一个人可以对这本书 修改 , 这样的好处就是 Rust在编译时就避免了数据的竞争, 下面这段代码就出现了多引用:
fn main() { let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s; println!("{}, {}", r1, r2); } // 这段代码就会报错 因为声明了两个可变引用 且他们在同一个作用域main函数中,第一个可变引用r1声明周期必须持续到print完成后 在r1的声明周期内又尝试创建了一个可变引用r2 引起了数据的竞争 fn main() { let mut s = String::from("hello"); let r1 = &s; println!("{}", r1); let r2 = &mut s; // 如果想要 一段代码中同时引用可变引用和不可变引用 他们的生命周期必须没有交集 println!("{},", r2); } // 可变引用和不可变引用在新版本的编译器中是可以同时存在的, 1.31之前不可以 // 对于这种编译器的优化Rust专门去了一个名字NLL - Non-Lexical Lifetimes(NLL),, 就是专门找出某一个引用在作用域 } 结束之前就不在被使用的引用的位置 悬垂引用 (出现悬空指针、 也可称迷途指针 、 野指针)悬空指针 就是 指针指向实际的数据, 但是这个值在使用之前之前就已经被释放掉了, 但是 指针 也就是引用存在, 释放掉的内存可能不存在任何值, 或者被其他程序变新使用了, 造成了数据污染 , 而Rust编译器可以永远保证 引用不悬垂。
发生悬垂的场景:
fn main() { let mut testStr = String::from("testing"); let result = overhang(testStr); // 将String数据传给overhang函数 此时String的所有权转移到overhang函数当中 println!("{}",result); // 悬空指针产生了因为引用真正数据已经被释放了 找不到原本你的数据了 } fn overhang(mut s: String) -> &String { // s.push_str("123"); // 修改String &s // 返回String 的引用 } // 在此处 s 离开当前作用域 s 被drop掉 内存释放 , 返回&s 危险 error : error[E0106]: missing lifetime specifier