所有权是Rust最核心的概念。他有三条基本规则:
- Rust中每一个值都有一个被称为其所有者(owner)的变量。
- 值在任意时刻有且只有一个所有者。
- 当所有者(变量)离开作用域,这个值将被丢弃。
rust
fn main() {
let s1=String::from("hello");
let s2=s1;
// println!("{}",s1); 错误s1已无效
println!("{}",s2);
let s3=s2.clone();
println!("s2:{},s3:{}",s2,s3);
}
所有权规则
rust
fn main() {
// 规则1:每个值都有一个所有者
let s=String::from("hello"); // s是hello的所有者
//规则2:值在任一时刻有且只有一个所有者
let s2=s; //s的所有权移动到s2
// println!("{}",s1); 错误s1已无效
println!("{}",s2);
// 规则3:所有者离开作用域,值被丢弃
{
let s3=String::from("world");
// s3在这里有效
} // s3离开作用域,值被自动释放
let s4=String::from("test");
takes_ownership(s4); // s4的所有权转移到函数
//println!("{}",s4); //错误! s4已无效
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
} // some_string 离开作用域,值被丢弃
移动
当将值赋给另一个变量或传递给函数时,会发生所有权的移动。
对于存储在堆上的数据(如 String、Vec),赋值会移动所有权,原变量失效。
对于实现了Copy trait的类型(如整数),会复制而不是移动。
rust
fn main() {
let s1=String::from("hello");
let s2=s1; // s1的所有权移动到s2
// println!("{}",s1); 错误 s1 已无效
// 整数实现了Copy,会复制
let x=5;
let y=x;
println!("{} {}",x,y);
let s3=String::from("world");
take_string(s3); //s3的所有权移动到函数
//println!("{}",s3); 错误
//返回值也会转移所有权
let s4=gives_ownership();
println!("{}", s4);
}
fn take_string(s:String){
println!("{}", s);
} //离开作用域,值被丢弃
fn gives_ownership()->String{
let some_string = String::from("hello");
some_string // 所有权返回给调用者
}
克隆
当需要复制堆上的数据时,可以使用clone方法。clone会创建数据的深拷贝,这意味着会复制堆上的所有数据。这可能会很昂贵,所以要谨慎使用。
rust
fn main() {
let s1=String::from("hello");
let s2=s1.clone(); //深拷贝,s1和s2都有效
println!("s1:{},s2:{}", s1,s2);
// 对于实现了Clone traint 的类型
let v1=vec![1,2,3];
let v2=v1.clone();
println!("v1: {:?},v2: {:?}",v1,v2);
// 结构体也可以实现clone
#[derive(Clone)]
struct Point{
x: i32,
y: i32,
}
let p1=Point{x:0,y:0};
let p2=p1.clone();
println!("p1: ({},{}),p2({},{})",p1.x,p1.y,p2.x,p2.y);
let large_string=String::from("a very long string...");
let copy=large_string.clone(); //复制所有数据
}
引用与借用
rust
fn main() {
let s1= String::from("hello");
let len=calculate_lenth(&s1); // &s1创建引用
println!("The length of '{}' is {}.", s1, len); // s1仍然有效
// 引用默认不可变
let s2= String::from("world");
// change(&s2); 错误!不能通过不可变引用修改值
}
fn calculate_lenth(s: &String)-> usize{
s.len()
} // 离开作用域,但因为它只是引用不会丢弃值
// 这个函数会编译错误
// fn change(some_string: &String){
// some_string.push_str(",world");
// }
可变引用
可变引用允许修改借用的值。使用&mut创建可变引用。
可变引用有一个重要限制:在特定作用域中,对特定数据只能有一个可变引用。这防止了数据竞争。
rust
fn main() {
let mut s=String::from("hello");
// 创建可变引用
change(&mut s);
println!("{}",s);
//可变引用的限制
let mut s2=String::from("test");
let r1=&mut s2;
// let r2=&mut s2; //错误!不能有多个引用
println!("{}",r1);
// 但是可以有多个不可变引用
let s3=String::from("hello");
let r1=&s3;
let r2=&s3;
println!("{}, {}",r1,r2);
//不能同时有可变和不可变引用
let mut s4=String::from("hello");
let r1=&s4;
//let r2=&mut s4;
println!("{}",r1);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
悬垂引用
悬垂引用是指指向已被释放内存的引用。在Rust中,编译器会确保引用永远不会比它指向的数据活得更久。Rust的借用检查器会检查所有引用,确保它们都是有效的。
rust
fn main() {
// 这个函数会编译错误,因为返回了悬垂引用
// fn dangle() -> &String {
// let s = String::from("hello");
// &s 错误! s离开作用域后会被丢弃
// }
//正确的做法:返回所有权
let s=no_dangle();
println!("{}", s);
}
fn no_dangle()->String{
let s = String::from("hello");
s
}
// Rust的借用检查器会组织这样的代码:
//{
// let r;
// {
// let x=5;
// r = &x; //错误!x的生命周期不够长
// }
// println!("{}",r); // r指向x已经不存在
//}
生命周期基础
生命周期是Rust中引用有效的作用域。生命周期确保引用在使用时是有效的。大多情况下生命周期是隐式的,编译器可以推断。但有时需要显式标注生命周期参数。
rust
fn main() {
let string1=String::from("long string is long");
{
let string2=String::from("xyz");
let result= longest(string1.as_str(), string2.as_str());
println!("The longest string is {}", result);
}
fn longest<'a>(x: &'a str,y: &'a str)->&'a str{
if x.len()>y.len(){x}
else{y}
}
}
rust
// 生命周期标注 'a表示
// - 参数x和y的引用至少和'a一样长
// - 返回的引用也至少和'a一样长
// 结构体中的生命周期
use std::ops::Bound::Included;
struct ImportantExcerpt<'a> {
part: &'a str,
}
fn main() {
let novel=String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i=ImportantExcerpt { part: first_sentence };
}