面试题答案
一键面试所有权分析
- 初始化
outer1
:- 创建
outer1
时,Vec<i32>
在堆上分配内存存储[1, 2, 3]
。Inner
结构体拥有这个Vec<i32>
,而Outer
结构体拥有Inner
结构体。所以outer1
拥有整个数据结构。
- 创建
let outer2 = outer1;
:- 这里发生了移动语义。
outer1
的所有权被转移给outer2
。此时outer1
不再拥有数据,尝试访问outer1
会导致编译错误,因为outer1
已被移动。
- 这里发生了移动语义。
- 创建
outer3
:outer3
被创建并拥有自己独立的Vec<i32>
[4, 5, 6]
。这里的Vec
内存分配与outer1
和outer2
无关。
outer1 = outer3;
:- 由于
outer1
在之前已经被移动,此时outer1
是未初始化状态,不能直接赋值。这行代码会导致编译错误。
- 由于
可能出现的编译错误及其原因
编译错误为:
error[E0382]: use of moved value: `outer1`
--> src/main.rs:10:5
|
6 | let mut outer1 = Outer { inner: Inner { data: vec![1, 2, 3] } };
| ------ move occurs because `outer1` has type `Outer`, which does not implement the `Copy` trait
7 | let outer2 = outer1;
| ---- value moved here
...
10 | outer1 = outer3;
| ^^^^^ value used here after move
|
= note: move occurs because `outer3` has type `Outer`, which does not implement the `Copy` trait
原因是 Outer
结构体没有实现 Copy
trait(Rust 结构体默认不实现 Copy
),所以 outer1
在赋值给 outer2
时发生移动,之后再使用 outer1
就会导致错误。
修改代码以避免所有权相关问题
- 使用
Clone
trait:- 如果
Inner
和Outer
结构体中的数据可以被克隆,我们可以实现Clone
trait 来复制数据而不是移动。
- 如果
struct Inner {
data: Vec<i32>
}
impl Clone for Inner {
fn clone(&self) -> Inner {
Inner { data: self.data.clone() }
}
}
struct Outer {
inner: Inner
}
impl Clone for Outer {
fn clone(&self) -> Outer {
Outer { inner: self.inner.clone() }
}
}
fn main() {
let mut outer1 = Outer { inner: Inner { data: vec![1, 2, 3] } };
let outer2 = outer1.clone();
let outer3 = Outer { inner: Inner { data: vec![4, 5, 6] } };
outer1 = outer3.clone();
}
- 使用
Option
处理未初始化状态:- 可以用
Option
来处理outer1
可能的未初始化状态。
- 可以用
struct Inner {
data: Vec<i32>
}
struct Outer {
inner: Inner
}
fn main() {
let mut outer1: Option<Outer> = Some(Outer { inner: Inner { data: vec![1, 2, 3] } });
let outer2 = outer1.take();
let outer3 = Outer { inner: Inner { data: vec![4, 5, 6] } };
outer1 = Some(outer3);
}
在这个版本中,outer1
初始化为 Some(Outer {... })
,然后通过 take
方法将其值取出并转移给 outer2
,之后可以安全地将 outer3
重新赋值给 outer1
。