接触rust是在一个前端的群里,群主推荐的。混群很久都没有引起我的注意,直到有一次百度了解,才发现它的魅力。而且它是WebAssembly首推的编程语言,自然有它的独特之处。
rust标榜内存安全和线程安全,并且并不是虚拟机语言,不存在垃圾回收。基本上就是可以兼顾虚拟机语言的自由和非虚拟机语言的速度,当然有得有失,带来的缺点就是语法丑陋,难以掌握,这对于它带来的好处来说都不是事儿。
rust的内存安全和线程安全实现机制就是独占内存资源的所有权,任何时候只允许只有一个变量(可以是变量的引用)对内存进行修改,并且引用是有生命周期的,编译器会约束引用的使用,它在原始变量释放内存之前必须归还,以杜绝垂悬指针,不按规则使用,就会编译不通过。
第一篇rust博客就是关于引用生命周期的,直接贴一段错误代码,从数据库操作中分离出来的。
1 struct Person { 2 pub name: String, 3 pub addr: String 4 } 5 fn addVal<'a>(vals: &'a mut Vec<&'a str>, person: &'a Person) { 6 vals.push(&person.name); 7 vals.push(&person.addr); 8 } 9 fn print(vals: &[&str]) { 10 } 11 fn main() { 12 let person = Person { 13 name: String::from("name"), 14 addr: String::from("addr") 15 }; 16 let mut vals: Vec<&str> = Vec::new(); 17 addVal(&mut vals, &person); 18 print(&vals); 19 }
按此代码编译不通过,编译报错如下:
Compiling train v0.1.0 (E:codegit rain) warning: unused variable: `vals` --> srcmain.rs:9:10 | 9 | fn print(vals: &[&str]) { | ^^^^ help: consider prefixing with an underscore: `_vals` | = note: `#[warn(unused_variables)]` on by default error[E0502]: cannot borrow `vals` as immutable because it is also borrowed as mutable --> srcmain.rs:18:11 | 17 | addVal(&mut vals, &person); | --------- mutable borrow occurs here 18 | print(&vals); | ^^^^^ | | | immutable borrow occurs here | mutable borrow later used here error: aborting due to previous error For more information about this error, try `rustc --explain E0502`. error: could not compile `train`. To learn more, run the command again with --verbose.
此处报错addVal调用的时候借用了一个可变引用,之后的print方法又借用了一个共享引用,但是并不是同时借用的,一先一后,按常理,addVal处的借用应该在调用之后就归还所有权,并不违反rust借用规则检查,但是编译就是不过。在排查问题的时候把问题的焦点放到了addVal方法的vals和person参数上了,看漏了一个vals的元素也是引用,由于vals的元素是引用,所以vals向量的生命周期应该小于借用关系的生命周期,而vals到main函数的结尾都还存活,所以借用关系至少要持续到函数结尾,又由于vals的借用和vals的元素声明成了相同的生命周期,所以vals的借用关系也是持续到main函数结尾的,所以,一直处于借用状态,并没有归还,不能再借用共享引用,导致编译报错。
最后经过大佬点播,把vals引用参数和vals的元素生命周期声明成不同的生命周期,就通过编译了。
最后addVal函数的签名为:
fn addVal<'a, 'b>(vals: &'a mut Vec<&'b str>, person: &'b Person)
rust是我目前遇到的最值得学习的编程语言,我对它的喜爱超过了之前的lua,路漫漫其修远兮,继续探索之路。