刚看完 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 上没有关于这两者区别的讨论,只是写了借用的做法。希望有人可以指教一下这两者的区别~
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); } ``` |
2
quinoa42 2019-01-15 10:39:10 +08:00
忘了说了,所以楼主的代码不能编译通过的原因是 vec.index(0)是个 reference,reference 不能转移 ownership
而后一个版本(直接调用 index())的时候,x 的类型是&Val,这里是个 borrow,所以编译可以通过 |
4
dangoron OP @quinoa42 还有个可能很小白的问题。。as_ref 和 as_mut 和普通的 & 和 &mut 的主要区别在哪里呢
|
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 |
6
quinoa42 2019-01-15 16:36:43 +08:00
比方说 as_mut -> 比方说 as_ref
|
7
dangoron OP @quinoa42 多谢!也就是说,在类型不需要变化的时候用 & 和 &mut 就够了,需要变化的时候可以用 as_ref 或者 as_mut。涉及到指针还真是头疼 0.0
|