1、引用(reference)
fn main() {
let _s1 = String::from("我爱海鲸");
let s = calculate_length(&_s1);
println!("{}",s)
}
fn calculate_length(s:&String) ->usize{
let len = s.len();
len
}
“&”表示取变量的地址,也就是引用的意思,上面的代码中也就是我们没有把变量的所有权传递过去,而是传递了变量的引用,所有权还是保留在main函数中。
参数的类型是&String 而不是String
&符号就表示引用:允许你引用某些值而不取得其所有权
2、借用
我们把引用作为函数参数这个行为叫做借用
那我们怎么通过引用来修改变量的值呢?上代码
fn main() {
let mut _s1 = String::from("我爱海鲸");
let s = calculate_length(&mut _s1);
println!("{}",s)
}
fn calculate_length(s: &mut String) ->usize{
s.push_str(",ok");
let len = s.len();
len
}
可变引用有一个重要的限制:在特定作用域内,对某一块数据,只能有一个可变的引用。
例如:
let mut s:String = String::from("Hello World")
let s1 = &s;
let s2 = &s;
这样就有问题了。
这样做的好处是可在编译时防止数据竞争。
以下三种行为下会发生数据竞争:
一两个或多个指针同时访问同一个数据
至少有一个指针用于写入数据
没有使用任何机制来同步对数据的访问
可以通过创建新的作用域,来允许非同时的创建多个可变引用
fn main() {
let mut _s = String::from("我爱海鲸");
{
let _s1 = &mut_s;
}
let _s2 = &mut_s;
}
不可以同时拥有一个可变引用和一个不变的引用
多个不变的引用是可以的
fn main() {
let mut st = String::from("我爱海鲸");
let r1 = &st;
let r2 = &st;
let r3 = &mut st;
println!("{},{},{}",r1,r2,r3)
}
悬空引用 Dangling References
悬空指针(Dangling Pointer) :一个指针引用了内存中的某个地址,而这块内存可能已经释放并分配给其它人使用了。
在Rust里,编译器可保证引用永远都不是悬空引用:
一如果你引用了某些数据,编译器将保证在引用离开作用域之前数据不会离开作用域
fn main() {
let s = dangle();
}
fn dangle() ->&String {
let st = String::from("我爱海鲸");
&st
}
这里它在编译时期就会报错,那是因为st在离开dangle函数的时候就已经失效了,但是这个函数有返回了一个引用,那么这个引用指向的内存空间就是被释放后的内存空间,这样就形成了悬垂引用
我们可以看一下这个报错:缺少一个生命周期的声明符
引用的规则
在任何给定的时刻,只能满足下列条件之一:
一个可变的引用
―任意数量不可变的引用
引用必须一直有效