面试题答案
一键面试// 定义结构体Node
struct Node {
value: i32,
next: Option<Box<Node>>,
}
// 创建链表的函数
fn create_linked_list(values: Vec<i32>) -> Box<Node> {
let mut head = None;
for value in values.into_iter().rev() {
let new_node = Box::new(Node {
value,
next: head.take(),
});
head = Some(new_node);
}
head.unwrap()
}
Rust生命周期和所有权机制分析
-
所有权机制:
Vec<i32>
作为参数传递给create_linked_list
函数时,其所有权转移到函数内部。在函数内部,使用values.into_iter().rev()
将Vec
转化为迭代器,并通过迭代器逐个消费Vec
中的元素。这里Vec
的所有权被完全消耗,不会造成内存泄漏。Box<Node>
类型用于在堆上分配内存来存储Node
实例。每个Box<Node>
拥有其内部Node
实例的所有权。当创建新的Node
实例时,使用Box::new
分配内存并获取Node
的所有权。例如let new_node = Box::new(Node {... });
,此时new_node
拥有新创建的Node
的所有权。Option<Box<Node>>
类型用于处理链表中可能存在的空节点。Option
类型本身不拥有其内部值(如果有的话)的所有权,真正的所有权由Box
持有。例如head = Some(new_node);
,这里new_node
的所有权转移到了Option
内部。当head.take()
调用时,Option
内部的Box<Node>
所有权被取出,使得Option
变为None
。
-
生命周期机制:
- 结构体
Node
中的next
字段类型为Option<Box<Node>>
,Box
类型会自动管理其内部Node
实例的生命周期。只要Box<Node>
存在,其内部的Node
实例就会一直存在,不会出现悬垂指针的情况。 - 在构建链表的过程中,通过
head.take()
和head = Some(new_node)
操作,确保了每个Node
实例的生命周期与链表的整体生命周期相匹配。当head
为None
时,说明链表为空;当head
为Some(new_node)
时,new_node
的生命周期与head
的生命周期相关联。只要head
存在(即在链表存在期间),new_node
(及其后续节点)也会存在。 - 当函数
create_linked_list
返回Box<Node>
时,调用者获得了链表头节点的所有权,并且链表中所有节点的生命周期由调用者负责管理。当调用者释放头节点(Box<Node>
)时,Rust的内存管理系统会自动释放链表中所有节点占用的内存,从而防止内存泄漏。
- 结构体