Rust 引用与借用

作者: kaiv2 | 来源:发表于2020-03-20 11:18 被阅读0次

    引用与借用

    fn main() {
      let s1 = String::from("abc");
      let l = f1(&s1);
      println!("s1 = {}, len = {}", s1, l);
    }
    
    fn f1(s: &String) -> usize {
      s.len()
    }
    

    这里将s1传给了 f1函数,使用的是&s1,f1定义的形参类型是&String。这些&符号就是引用,它们允许你使用值但不获取所有权。

    &s语法 让我们创建了一个指向s1的引用,但是不拥有它。因为不拥有这个值,在离开作用域时也不需要drop特殊操作。

    我们将获取引用作为函数参数称为借用。正如变量默认是不可边的,应用也是一样。

    fn f1(s: &String) -> usize {
      s.push_str("hello"); // 这里将发生错误,尝试修改不可变的借用的值
      s.len();
    }
    

    与运算符 & 引用相反的操作是*解引用

    可变引用

    fn main() {
      let mut s = String:from("hello");
      change(&mut s);
    }
    
    fn change(s: &mut String) {
      s.push_str(", world");
    }
    

    首选将s改为 mut的,然后必须创建一个可变引用&mut s和接受一个可变引用s: &mut String

    不过可边引用有很大的限制,在特定作用域的特定数据有且只有一个可变引用

    let mut s1 = String::from("hello");
    let s2 = &mut s1;
    let s3 = &mut s1;
    println!("{}, {}", s2, s3);
    

    这里将报错,这个限制的好处是Rust在编译时就避免数据竞争数据竞争类似于竞态条件。它由这三个条件造成:

    • 两个或更多指针同时访问同一数据
    • 至少有一个指针被用来写数据
    • 没有同步数据访问的机制

    数据竞争会导致未定义行为,难以在运行时追踪,且难以诊断和恢复。Rust避免了这种情况发生,因为它甚至不会编译存在数据竞争的代码。

    let mut s = String::from("hello");
    {
      let r1 = &mut s;
    } // r1 离开了作用域,可以创建一个新的引用
    let r2 = &mut s;
    

    类似的规则也存在于可变引用和不可变引用中。

    let mut s = String::from("hello");
    
    let r1 = &s; // 可以
    let r2 = &s; // 可以
    let r3 = &mut s; // 不行
    

    Rust不允许不可变引用和可变引用同时指向一个值,不可变引用不希望再读取值时被修改。多个不可变引用是可以的。

    悬垂引用

    在具有指针的语言中,很容易变量指向一个已释放的内存,而错误的产生一个 悬垂指针。尝试创建一个悬垂指针:

    fn main() {
      let s  = f1();
    }
    
    fn f1() -> &String {
      let s = String::from("hello"); // 创建字符串,分配内存
      &s // 返回字符串的引用
    } // s 离开作用域,释放内存
    

    s离开作用域时,s将被释放。尝试返回s的引用,Rust不允许这么做,这里将发生错误。

    引用的规则

    总结:

    • 在任意给定时间,要么只能有一个可变应用,要么只能是多个不可变引用
    • 引用必须总是有效

    相关文章

      网友评论

        本文标题:Rust 引用与借用

        本文链接:https://www.haomeiwen.com/subject/bkicyhtx.html