Rust所有权机制

前言

所有权机制是Rust的核心功能之一,对于初学者算是一个重难点,它保证了程序无需GC,即可自动化的释放内存
希望这篇博客可以帮助你快速理解所有权机制

概念

Rust的值都只有一个变量是该值的所有者
所有者可以切换,但是值在任一时刻有且仅有一个所有者
当所有者离开作用域,该值将被丢弃

错误示例

在变量的作用域之外访问该变量

fn main() {
    {
        let s = String::from("hello"); // 创建字符串,该字符串的所有者是变量s
    } // 离开作用域,该字符串被丢弃
    println!("{}", s); // 编译报错,此时变量s已被释放,无法访问变量s
}

访问一个所有权被转移的变量

fn main() {
	let s = String::from("hello"); // 创建字符串,该字符串的所有者是变量s
    let a = s; // 字符串的所有权从变量s传递给变量a
	println!("{}",s); // 编译报错,此时字符串的所有权已被转移,无法在通过变量s访问字符串
}

访问一个所有权被转移的变量

fn main() {
	let s = String::from("hello"); // 创建字符串,该字符串的所有者是变量s
    takes_ownership(s); // 字符串的所有权从变量s传递给函数takes_ownership
	println!("{}",s); // 编译报错,此时字符串的所有权已被转移,无法在通过变量s访问字符串
}

fn takes_ownership(s: String) {
	println!("我拿到了字符串\"{}\"的所有权", s); // 拿到字符串的所有权
} // 离开作用域,该字符串被丢弃

借用

在上述错误示例中,我们不难发现,如果我们把String字符串的所有权传递给函数,那么等到函数结束,我们就无法在使用该字符串了,对于我们,这显然是无法接受的,那么几种解决方法解决这个问题,例如

fn main() {
    let mut s = String::from("hello");
    s = takes_ownership(s);
    println!("{}", s);
}

fn takes_ownership(s: String) -> String {
	println!("我拿到了字符串\"{}\"的所有权", s); // 拿到字符串的所有权
    s // 返回该字符串
}
fn main() {
    let mut s = String::from("hello");
    s = takes_ownership(s);
    println!("{}", s);
}

fn takes_ownership(s: String) -> String {
	println!("我拿到了字符串\"{}\"的所有权", s); // 拿到字符串的所有权
    s // 返回该字符串
}

但是上述做法并不优雅,Rust推荐的做法是我们通过借用来解决这个问题,借用就是创建一个引用的行为,例如

fn main() {
	let s = String::from("hello");
    takes_ownership(&s); // 将字符串的引用传递给函数
    println!("{}", s);
}

fn takes_ownership(s: &String) { // 借用到字符串的引用
	println!("我借用到了字符串\"{}\"的引用", s);
} // 此时字符串的所有权并没传递给函数,应此该函数没有资格释放该字符串

如果我们需要在函数内部修改字符串,那么我们可以借用字符串的可变引用

fn main() {
	let mut s = String::from("hello");
    takes_ownership(&mut s); // 将字符串的引用传递给函数
    println!("{}", s);
}

fn takes_ownership(s: &mut String) { // 借用到字符串的可变引用
	println!("我借用到了字符串\"{}\"的可变引用", s);
	s.push_str(" world"); // 修改该字符串
} // 此时字符串的所有权并没传递给函数,应此该函数没有资格释放该字符串

可变引用虽好,但是有个注意事项,就是同一时刻可变引用只能有一个,当存在可变引用的时候,就不能有不可变引用,但是当可变引用不存在的时候,就可以存在数个不可变引用,例如

fn main() {
	let mut s = String::from("hello");
    let (a, b) = (&s, &s); // 借用两个字符串的不可变引用
    println!("不可变借用a:{}, 不可变借用b:{}", a, b);
    let c = &mut s; // 借用字符串的可变引用,此时前面的两个不可变引用不再生效
    c.push_str(" world");
    println!("{}", c);
}

还有一个注意事项就是,引用的内容必须合法,例如下面的错误示范

fn main() {
    let s = dangle();
}

fn dangle() -> &String {
    let s = String::from("hello"); // 创建字符串,该字符串的所有者是变量s
    &s // 返回字符串的引用,但是此时字符串要已经离开作用域被释放了,因此当函数结束,这个引用将是一个非法的悬垂引用,不过还好,在rust中这种行为无法通过编译
}

总结一下引用的规则

  1. 在任意时刻只能有一个可变引用,要么只能有多个不可变引用
  2. 引用必须有效

你可能感兴趣的:(rust,开发语言,后端)