ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

《Rust程序设计》学习笔记四

《Rust程序设计》学习笔记四 ^^《榴芒客服系统》是我们工作室开发的在线客服系统欢迎下载试用https://blog.csdn.net/look4liming/article/details/164755808https://blog.csdn.net/look4liming/article/details/164755808《Rust程序设计》学习笔记四截止到目前为止所有的指针类型都是拥有型指针当拥有者被丢弃时它的引用目标也会随之消失。Rust还有一种名为引用reference的非拥有型指针这种指针对引用目标的生命周期毫无影响。引用的生命周期决不能超出其引用目标。Rust把创建对某个值的引用的操作称为借用borrow那个值。引用能在不影响其所有权的情况下访问值。共享引用允许读取但不能修改其引用目标。可以同时拥有任意数量的对特定值的共享引用。e会产生对e值的共享引用。共享引用时Copy类型。可变引用允许读取和修改值。拥有了可变引用就无法再对该值创建其它任何种类的引用了。mut e会产生一个对e值的可变引用。可变引用不是Copy类型。只要存在共享引用即使是它的拥有者也不能修改它该值会被锁定。如果有某个值的可变引用即使拥有者也根本无法使用该值。当通过将值的所有权转移给函数的方式将这个值传给函数时就可以说按值传递了它。如果改为将值的引用传给函数就可以说按引用传递了它。在Rust中引用时通过运算符显示创建的同时要用*运算符显式解引用let x 10;let r x; // x是对x的共享引用assert!(*r 10); // 对r显式解引用要创建可变引用可以使用mut运算符let mut y 32;let m mut y; // mut y时对y的可变引用*m 32; // 对m显式解引用以设置y的值assert!(*m 64); // 来看看y的新值由于引用在Rust中随处可见因此运算符就会按需对其左操作数隐式解引用let b a;assert!(b.name, abc); //注意这里的b没有显式解引用在进行方法调用时.运算符也可以根据需要隐式借用对其左操作数的引用。所以下面两个调用时等价的let mut v vec![100, 200];v.sort(); // 隐式借用对v的可变引用(mut v).sort(); // 与上一行等价但是更繁琐.运算符会隐式借用和解引用。把引用赋值给某个引用变量会让该变量指向新的地方let x 10;let y 20;let mut r x;if b {r y;}assert!(*r 10 || *r 20);以上代码中引用r最初指向x。但如果b为true则代码会把它改为指向y。Rust允许对引用进行引用let r: Point point;let rr: Point r;let rrr: Point rr;Rust的引用永远都不会为空。与C的NULL和C的nullptr类似的东西是不存在的。在Rust中如果需要用一个值来表示对某个“可能不存在”的事物的引用请使用类型OptionT。在Rust中全局变量的等价物称为静态变量static。每个静态变量都必须初始化。可变静态变量本质上不是线程安全的。static mut STASH: i32 128;fn f(p: i32) {unsafe {STASH p;}}上面这段程序中f的签名实际上是以下内容的简写fn fa(p: a i32) { ... }生命周期a读作“tick a”是f的生命周期参数。函数f能接受对具有任意声明周期a的i32型引用。注意STASH p;由于STASH会存续在程序的整个执行过程中因此它所持有的引用类型必须具有等长的生命周期Rust将此称为“static生命周期”。但是指向p的引用的生命周期是a它可以是任何能涵盖对f调用的生命周期。所以程序编译报错。改造后可以通过编译的程序如下static mut STASH: 32 10;fn f(p: static i32) {unsafe {STASH p;}}在下面的代码中x的生命周期不会超出x并且会涵盖对g的完整调用所以可以编译通过fn ga(p: a i32) { ... }let x 10;g(x);在下面的代码中引用x的生命周期不能超出x但通过将它传给f又限制了它必须和static一样长。没办法做到两全其美编译报错fn f(p: static i32) { ... }let x 10;f(x);函数通常会接受某个数据结构的引用然后返回对该结构的某个部分的引用fn smallest(v: [32]) - i32 {let mut s v[0];for r in v[1..] {if *r *s { s r; }}s}上面这个函数的签名中省略了生命周期。当函数以单个引用作为参数并返回单个引用时Rust会假定两者具有相同的生命周期。加上生命周期参数的函数签名如下fn smallesta(v: a [i32]) - a i32 { ... }Rust对引用的安全约束不会因为将引用藏在结构体中而消失看下面这段程序Struct S {R: i32}let s;{let x 10;s S {r: x}}assert_eq!(*s.r, 10);这段程序编译报错。当一个引用类型出现在另一个类型的定义中时必须写出它的生命周期例如下面这样struct S {r: static i32}这表示r只能引用贯穿程序的整个生命周期的i32值。另一种方法是给类型指定生命周期参数并作用在r上struct Sa {r: a i32}如果函数的参数只有一个生命周期那么Rust会假设返回值具有相同的生命周期。如果函数的参数有多个生命周期Rust会要求明确指定生命周期。如果函数是某个类型的方法并具有引用类型的self参数那么Rust就会假定返回值的生命周期与self参数的生命周期相同。看下面这段程序let v vec![4, 8, 19, 27, 34, 10];let r v;let aside v; // 把向量转移给asider[0]; // 错误这里所用的v此刻是未初始化状态对aside的赋值会移动向量让v回到未初始化状态并将r变为悬空指针。尽管v在r的整个生命周期中都处于作用域内部但问题是v的值已经移动到别处导致v成了未初始化状态而人忍让在引用它。当然会编译报错。在共享引用的这个生命周期中它引用的目标会保持只读状态不能对引用目标赋值或将值移动到别处。将程序改成如下形式就可以编译通过let v vec![4, 8, 19, 27, 34, 10];{let r v;r[0];}let aside v;在这个版本中r作用域范围更小在把v转移给aside之前r的生命周期就结束了所以可以编译通过。看下面这段程序fn extend(vec: mut Vecf64, slice: [f64]) {for elt in slice {vec.push(*elt);}}看下面的错误的函数调用extend(mut wave, wave);既可以借用向量的可变引用也可以借用其元素的共享应用但这两种引用的生命周期不能重叠。共享引用借用你的值是只读的在共享引用的整个生命周期中无论是它的引用目标还是可以不从该引用目标间接访问的任何值都不能被任何代码改变。可变引用借用的值只能通过该引用访问。在可变引用的整个生命周期中无论是它的引用目标还是该引用目标间接访问的任何目标都没有任何其它路径可访问。对可变引用来说唯一能和自己的生命周期重叠的引用就是从可变引用本身借出的引用。例1let mut x 10;let r1 x;let r2 x; // 正确允许多个共享引用x 10; // 错误不能复制给x 因为它已被借出let m mut x; // 错误不能把x借入为可变引用// 因为它涵盖在已借出的不可变引用的生命周期内println!({}, {}, {}, r1, r2, m); // 这些引用是在这里使用的所以它// 们的生命周期至少要存续这么长例2let mut y 20;let m1 mut y;let m2 mut y; // 错误不能多次借入可变引用let z y; // 错误 不能使用y因为它涵盖在已借出的可变引用的生命周期内Println!({}, {}, {}, m1, m2, z);例3let mut w (107, 109);let r w;let r0 r.0; // 正确把共享引用重新借入为共享引用let m1 mut r.1; // 错误不能把共享引用重新借入为可变引用println!({}, r0);例4let mut v (136, 139);let m mut v;let m0 mut m.0; // 正确从可变引用中借入可变引用*m0 137;let r1 m.1; // 正确 从可变引用中借入共享引用并且不和m0重叠v.1; // 错误禁止通过其它路径访问println!({}, r1);^^《榴芒客服系统》是我们工作室开发的在线客服系统欢迎下载试用https://blog.csdn.net/look4liming/article/details/164755808https://blog.csdn.net/look4liming/article/details/164755808
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进