Rust基础03-所有权与函数、引用与借用
所有权与函数
在语义上,将值传递给函数和把值赋给变量是类似的:
- 将值传递给函数将发生移动或赋值
- 函数在返回值的过程中同样也会发生所有权的转移
例:
fn main() {
let s = String::from("Hello World");
take_ownership(s);
let x = 5;
makes_copy(x);
println!("x: {}", x);
}
fn take_ownership(some_string: String) {
println!("{}", some_string)
}
fn makes_copy(some_number: i32) {
println!("{}", some_number);
}
一个变量的所有权总是遵循同样的模式:
- 把一个值赋给其它变量时就会发生移动
- 当一个包含 heap 数据的变量离开作用域时,它的值就会被 drop 函数清理,除非数据的所有权移动到另一个变量上
例:
fn main() {
let s1 = gives_ownership();
let s2 = String::from("hello");
let s3 = takes_and_gives_back(s2);
}
fn gives_ownership() -> String{
let some_string = String::from("hello");
some_string
}
fn takes_and_gives_back(a_string: String) -> String {
a_string
}
引用与借用
让函数使用某个值,但不获得其所有权
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length)
}
以上代码虽然做到了要求,但其过程过于繁琐,因此,实际开发中常使用以下方法
-
引用
fn main() { let s1 = String::from("Hello"); let len = calculate_length(&s1); println!("The length of '{}' is {}.", s1, len); } fn calculate_length(s: &String) -> usize { s.len() }
在上述代码中,参数的类型为 &String 而不是 String
& 符号代表引用:允许引用某些值而不取得其所有权
我们将引用作为函数参数这个行为叫做借用
-
借用
在程序中不可修改,和变量一样,引用默认也是不可变的
但在程序中也可存在可变引用
-
可变引用
在特定作用域内,对某一块数据,只能有一个可变的引用
使得程序在编译时就可防止数据竞争
可以通过创建新的作用域,来允许非同时的创建多个可变引用
fn main() { let mut s =String::from("Hello"); { let s1 = &mut s; } let s2 = &mut s; }
不可同时存在一个可变引用和一个不变的引用
-
数据竞争
同时存在以下三种行为便会发生数据竞争:
- 两个或多个指针同时访问同一个数据
- 至少有一个指针用于写入数据
- 没有使用任何机制来同步对数据的访问
-
悬空引用(Dangling References)
悬空指针(Dangling Pointer):一个指针引用了内存中的某个地址,而这块内存可能已经释放并分配给其它人使用了
在Rust中,编译器可保证引用永远都不是悬空引用:
如果你引用了某些数据,编译器将保证在引用离开作用域前数据不会离开作用域
//以下为悬空引用(此代码无法通过编译) fn main() { let r = dangle(); } fn dangle() -> &String { let s = String::from("hello"); &s } //ERROR: missing lifetime specifier
-
引用的规则
- 一个可变的引用
- 任意数量不可变的引用
在代码中,引用必须一直有效