V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dangoron
V2EX  ›  Rust

关于借用的一个小问题

  •  
  •   dangoron · 2019-01-14 19:18:31 +08:00 · 4765 次点击
    这是一个创建于 2170 天前的主题,其中的信息可能已经有所发展或是发生改变。

    刚看完 rust 官方教程,感觉非常新鲜,上手就写了一个 two sum 的题( leetcode 居然支持 rust 了)。 写的过程中发现一个不解之处,在对Vector和列表中的元素进行引用的时候一般会写let x = &vec[0];,不过如果直接写let x = vec[0]同样是可以使用的,只是x的类型不同,如果vec里的元素都是i32的话,不加&的写法输出的x会是i32类型。而且,在let x = vec[0]之后,vec[0]仍然可以访问,也就是说并没有失去所有权,于是怀疑这里做了克隆。继续进行测试:

    #[derive(Debug)]
    struct Val {
        val: i32,
    }
    
    fn main() {
        let vec = vec![Val{ val: 0 }, Val{ val: 1 }, Val{ val: 2 }];
        let x = vec[0];
        println!("x: {:?}", x);
    }
    

    let x = vec[0]处会提示cannot move out of borrowed content错误,反而let x = &vec[0]是可行的,给Val结构添加#[derive(Clone)]标注依然会有错误,所以好像这也并不是在做克隆。rust book 上没有关于这两者区别的讨论,只是写了借用的做法。希望有人可以指教一下这两者的区别~

    7 条回复    2019-01-15 16:58:25 +08:00
    quinoa42
        1
    quinoa42  
       2019-01-15 10:35:25 +08:00   ❤️ 1
    let x = vec[0]; 是 move,move 之后 ownership 交给了 x
    rust 里 trait 分 Copy 和 Clone,这俩的区别就是 Copy 默认不需要很耗时的调用而 Clone 默认需要,所以 Copy 会自动调用而 Clone 不会(而 scalar,比如 i32,都实现了 Copy )

    换个角度:
    ```rust
    #[derive(Debug, Clone, Copy)]
    struct Val {
    val: i32,
    }

    fn main() {
    let vec = vec![Val{ val: 0 }, Val{ val: 1 }, Val{ val: 2 }];
    let x = vec[0];
    println!("x: {:?}", x);
    }
    ```
    这样就不会报错,因为 let x = vec[0]自动调用了 Copy::clone()

    更进一步的理解首先需要参考 Index::index():
    https://doc.rust-lang.org/std/vec/struct.Vec.html#impl-Index%3CI%3E
    但是仔细看会发现,index()返回的是个 reference …………不能直接 let x = vec[0]的原因是,
    如果用了[]这个语法糖,rust 会在调用 index()之后再调用一次 deref
    所以直接调用 index()也能编译:
    ```rust
    use std::ops::Index;
    #[derive(Debug)]
    struct Val {
    val: i32,
    }

    fn main() {
    let vec = vec![Val{ val: 0 }, Val{ val: 1 }, Val{ val: 2 }];
    let x = vec.index(0);
    println!("x: {:?}", x);
    }
    ```
    quinoa42
        2
    quinoa42  
       2019-01-15 10:39:10 +08:00
    忘了说了,所以楼主的代码不能编译通过的原因是 vec.index(0)是个 reference,reference 不能转移 ownership
    而后一个版本(直接调用 index())的时候,x 的类型是&Val,这里是个 borrow,所以编译可以通过
    dangoron
        3
    dangoron  
    OP
       2019-01-15 15:05:44 +08:00
    @quinoa42 谢谢你的释疑。感觉 rust 的所有权问题还是很复杂的,尤其是看到链表的实现之后。。
    dangoron
        4
    dangoron  
    OP
       2019-01-15 16:04:25 +08:00
    @quinoa42 还有个可能很小白的问题。。as_ref 和 as_mut 和普通的 & 和 &mut 的主要区别在哪里呢
    quinoa42
        5
    quinoa42  
       2019-01-15 16:35:56 +08:00
    @dangoron
    比方说 as_mut 的 type 是 fn as_ref(&self) -> &T,也就是说 as_mut 和 as_ref 的返回的 reference 包含的类型不一定要和 self 的类型一致
    书里唯一一次提到 as_ref ( https://doc.rust-lang.org/1.30.0/book/2018-edition/ch17-03-oo-design-patterns.html?highlight=as_ref#adding-the--approve--method-that-changes-the-behavior-of--content )是 Option::as_ref,效果是从 Option<T>变成 Option<&T>,但其实 Option 貌似没实现 AsRef trait...(毕竟和要求的 signature 不一样)
    https://doc.rust-lang.org/std/option/enum.Option.html#method.as_ref
    但 Arc::as_ref 可以作为一个典型例子:
    https://doc.rust-lang.org/src/alloc/sync.rs.html#1946-1948
    quinoa42
        6
    quinoa42  
       2019-01-15 16:36:43 +08:00
    比方说 as_mut -> 比方说 as_ref
    dangoron
        7
    dangoron  
    OP
       2019-01-15 16:58:25 +08:00
    @quinoa42 多谢!也就是说,在类型不需要变化的时候用 & 和 &mut 就够了,需要变化的时候可以用 as_ref 或者 as_mut。涉及到指针还真是头疼 0.0
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1055 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 20:08 · PVG 04:08 · LAX 12:08 · JFK 15:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.