Rust 基本功 -- Option 与 Box 的 as_mut 实现
Rust 基本功 -- Option 与 Box 的 as_mut 实现
Rust 基本功
一、关于 Prelude 机制
在使用 Rust 的 Option
和 Box
时, 我们不需要手动导入相关的模块, 它们都是 Rust 标准库的一部分, 但是它们有着不同的可见性机制。 事实上这些数据结构是通过 Prelude 机制进行自动导入的
std::prelude::v1
包含的内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 标准库位置:std::prelude::v1
pub use crate::marker::{Send, Sync, Sized, Unpin};
pub use crate::ops::{Drop, Fn, FnMut, FnOnce};
pub use crate::mem::drop;
pub use crate::boxed::Box; // 这就是为什么 Box 不需要导入
pub use crate::borrow::ToOwned;
pub use crate::clone::Clone;
pub use crate::cmp::{PartialEq, PartialOrd, Eq, Ord};
pub use crate::convert::{AsRef, AsMut, Into, From};
pub use crate::default::Default;
pub use crate::iter::{Iterator, Extend, IntoIterator};
pub use crate::option::Option::{self, Some, None}; // Option 及其变体
pub use crate::result::Result::{self, Ok, Err}; // Result 及其变体
pub use crate::string::{String, ToString};
pub use crate::vec::Vec;
举个具体的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 实际位置(你通常不需要这样写)
use std::boxed::Box;
use std::option::Option;
use std::vec::Vec;
use std::collections::HashMap; // 这个需要手动导入!
// HashMap 不在 prelude 中,需要手动导入
use std::collections::HashMap;
fn example() {
let map = HashMap::new(); // 需要上面的 use 语句
let opt = Option::Some(42); // 或直接用 Some(42)
let boxed = Box::new(42);
}
二、Option::as_mut() 源码详解
1
2
3
4
5
6
7
8
9
10
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "const_option", since = "1.83.0")]
pub const fn as_mut(&mut self) -> Option<&mut T> {
match *self {
// ref mut 含义: 在模式匹配中创建可变引用, 而不是移动值
Some(ref mut x) => Some(x),
None => None,
}
}
模式匹配中 ref mut
是什么呢? ref mut
是一个模式匹配时的借用操作符, 作用是在模式匹配中创建可变引用, 而不是移动值.
下面分别针对三种情况来探讨下编译器的表现:
情况一: 从源码中删除掉 ref mut (编译错误)
1
2
3
4
5
6
7
8
9
// ❌ 这样写会编译失败
impl<T> Option<T> {
pub fn as_mut_broken(&mut self) -> Option<&mut T> {
match *self {
Some(x) => Some(&mut x), // x 是 T 类型, 此处发生编译错误!❌
None => None,
}
}
}
❌ 编译错误的原因:
x
是通过 move 移动获得的T
类型值&mut x
创建了对局部变量x
的引用- 该局部变量引用在函数结束时就失效了
情况二: 只使用 ref (只读引用), 不加 mut
1
2
3
4
5
6
// ✅ 可以编译,但返回类型不匹配
match *self {
Some(ref x) => Some(x), // 此处 x 类型是 &T,不是 &mut T
None => None,
}
// 返回 Option<&T>,不是我们想要的 Option<&mut T>
情况三: 正确使用 ref mut
1
2
3
4
5
6
// ✅ 正确的实现
match *self {
Some(ref mut x) => Some(x), // 此时 x 的类型是 &mut T
None => None,
}
// 返回 Option<&mut T>
三、Box::as_mut() 源码详解
1
2
3
4
5
6
#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")]
impl<T: ?Sized, A: Allocator> AsMut<T> for Box<T, A> {
fn as_mut(&mut self) -> &mut T {
&mut **self
}
}
逐步分析表达式 &mut **self
:
self
的类型:&mut Box<T, A>
, 表示对Box
的可变引用- 第一个
*
用来解引用Box
的引用:&mut Box<T, A>
->Box<T,A>
- 第二个
*
用来解引用Box
本身:Box<T,A>
->T
(Box
指向的实际数据) &mut
创建可变引用:T
->&mut T
下面是 AI 绘制的简单的内存视图:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 内存布局示意
//
// 栈上:
// boxed_value: Box<i32>
// ┌─────────────┐
// │ ptr: 0x1000 │ ──┐
// │ alloc: ... │ │
// └─────────────┘ │
// │
// 堆上: │
// 0x1000: │
// ┌───────────┐ ←───┘
// │ 42 │
// └───────────┘
//
// &mut **self 的执行:
// 1. self: &mut Box<i32> (指向栈上的 Box)
// 2. *self: Box<i32> (栈上的 Box 值)
// 3. **self: i32 (堆上的实际值)
// 4. &mut **self: &mut i32 (指向堆上值的可变引用)
Box::as_mut()
使用方式:
1
2
let mut boxed: Box<T> = Box::new(T::new());
let res: &mut T = boxed.as_mut();
boxed
的类型是可变的Box<T>
as_mut()
需要&mut self
, 即&mut Box<T>
- Rust 自动从
mut boxed
创建&mut boxed
本文由作者按照 CC BY 4.0 进行授权