Rust初步上手
程序在内存中运行,所有权指的也是内存的所有权,这个概念被提出的目的,就是让Rust在编译阶段能够更有效地分析内存资源,并优化管理,相当于是静态的垃圾回收。
Rust中的所有权有以下三条规则:
在Rust中,任何变量都有其可行域,在作用域中创建的变量,无法渗透到外面,这就是所有权的一个体现。比如在调用下面这个函数时就会报错
fn owner(){
{
let a = "micro";
}
println!("{}",a);
}
错误发生在编译期间
>rustc main.rs
error[E0425]: cannot find value `a` in this scope
--> main.rs:10:19
|
10 | println!("{}",a);
| ^
|
help: the binding `a` is available in a different scope in the same function --> main.rs:8:13
|
8 | let a = "micro";
| ^
error: aborting due to previous erro
在编程中难免遇到一些长度不固定的变量,所以程序必须有在执行期间分配内存的能力,不能一切都指望编译期。而且这个分配,既包括新内存的发放,也包括老内存的销毁。
Rust在编译时,会自动在合适的地方添加一些释放资源的函数,从而维护了内存安全。
下面这种写法大家已经司空见惯了,将5绑定给x,然后再将x绑定给y,其编译运行结果没有任何疑问,就是x=5,y=5
fn test_move(){
let x = 5;
let y = x;
println!("x={},y={}", x,y)
}
但换一种数据类型,结果却报错了,说是把x的值移动给了y,所以x自己就没有了。
fn test_move2(){
let x = String::from("micro");
let y = x;
println!("x={},y={}", x,y)
}
并且报错中还给出了温馨提示,说
help: consider cloning the value if the performance cost is acceptable
|
13 | let y = x.clone();
| ++++++++
即需要调用clone方法来将x值克隆给y,否则就相当于是把x的值移动给了y,所以x自己就没有了。二者的区别相当于是复制和剪切。
在rust中,一些小而直接的数据类型,在数据传递过程中,是默认克隆模式的,比如test_move1中演示的整数,这些类型还包括
而其他数据类型,不好意思,默认的就是移动,如果想把其他类型的x复制给y,就要用到下面的形式
fn test_clone(){
let x = String::from("micro");
let y = x.clone();
println!("x={},y={}", x,y);
}
在rust中,要知道任何表达式都可以当成是一个函数,是有返回值的。那么自然地,变量在参数传递过程中的特性,也自然要发生在函数传参时,下面的写法有报错了,要求把test_print(x)
改写为test_print(x.clone())
fn test_print(s:String){
println!("s={}", s);
}
fn test_move3(){
let x = String::from("micro");
test_print(x);
println!("x={}", x);
}
但是同样地,如果传递的参数是一个基础类型的变量,那就完全没问题。
fn test_print_int(i:i32){
println!("i={}", i);
}
fn test_move4(){
let x = 5;
test_print_int(x);
println!("x={}", x);
}
对于复杂变量x,如果想x的值赋予y,要么选择移动,但这样x自己就没了;要么选择克隆,但这个开销就比较大。一个自然的想法就是,能不能把x的地址传给b,这样两人就可以共享一块内存区域。
这种操作rust当然是支持的,名曰引用,只需用到取地址符&,所以下面的函数可以顺利执行
fn test_ref(){
let x = String::from("cool");
let y = &x;
x.push_str("l")
println!("x={},y={}", x,y)
}
函数参数传递的道理一样。
但是,正所谓皮之不存毛将焉附,如果y在引用x的值之后,如果x的值被移动给了另外一个变量,那么y,的引用也自然就作废了。
另一方面,y只是引用到了x的地址,但并没有得到这篇内存的所有权,所以y在理论上是不可写入的,这种感觉就像是借书一样,可以随便看,但不能乱写乱画。所以下面的代码报错就是理所当然的了。
实例
fn test_borrow(){
let x = String::from("cool");
let y = &x;
y.push_str("l");
println!("{}", y);
}
但是,如果非要更改,那么也不是不可以实现,只需采用mut引用,写成下面这样就可以了,y成了一个可变引用类型的数据。
fn test_borrow2(0[
let x = String::from("cool");
let y = &mut x;
y.push_str("l");
println!("{}", y);
])
但可变引用也有一个问题,即只允许可变引用一次。最后,引用不可以作为函数的返回值,因为函数的返回值将不确定这个引用会给谁,可能导致灾难性后果。
这就是Rust语言的风格,严谨而死板,由此带来的好处则是更加安全。