关于借用的一个小问题

2019-01-14 19:18:31 +08:00
 dangoron

刚看完 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 上没有关于这两者区别的讨论,只是写了借用的做法。希望有人可以指教一下这两者的区别~

4523 次点击
所在节点    Rust
7 条回复
quinoa42
2019-01-15 10:35:25 +08:00
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
2019-01-15 10:39:10 +08:00
忘了说了,所以楼主的代码不能编译通过的原因是 vec.index(0)是个 reference,reference 不能转移 ownership
而后一个版本(直接调用 index())的时候,x 的类型是&Val,这里是个 borrow,所以编译可以通过
dangoron
2019-01-15 15:05:44 +08:00
@quinoa42 谢谢你的释疑。感觉 rust 的所有权问题还是很复杂的,尤其是看到链表的实现之后。。
dangoron
2019-01-15 16:04:25 +08:00
@quinoa42 还有个可能很小白的问题。。as_ref 和 as_mut 和普通的 & 和 &mut 的主要区别在哪里呢
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
2019-01-15 16:36:43 +08:00
比方说 as_mut -> 比方说 as_ref
dangoron
2019-01-15 16:58:25 +08:00
@quinoa42 多谢!也就是说,在类型不需要变化的时候用 & 和 &mut 就够了,需要变化的时候可以用 as_ref 或者 as_mut。涉及到指针还真是头疼 0.0

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/527008

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX