
文章目录
- [第18章 高级特征](#第18章 高级特征)
第18章 高级特征
Rust语言的设计哲学强调安全性和性能,但同时也提供了在必要时突破安全限制的能力。本章将深入探讨Rust的高级特征,包括不安全Rust操作、高级trait使用技巧、高级类型系统特性以及函数和闭包的高级用法。这些特性让Rust能够在保持内存安全的同时,实现系统级编程所需的底层控制能力。
18.1 不安全的Rust
Rust通过所有权系统、借用检查和类型系统在编译时保证了内存安全。然而,在某些情况下,我们需要绕过这些安全检查来执行一些底层操作。这就是不安全Rust的用武之地。不安全Rust并不是关闭所有的安全检查,而是在特定区域内允许执行一些通常被禁止的操作,同时要求程序员对这些操作的安全性负责。
不安全超能力
在不安全Rust中,我们可以执行五种被称作"不安全超能力"的操作:
- 解引用裸指针
- 调用不安全函数或方法
- 访问或修改可变静态变量
- 实现不安全trait
- 访问union的字段
这些操作被标记为不安全是因为编译器无法验证它们的安全性,需要程序员手动确保操作的正确性。
rust
// 不安全块的基本语法
unsafe {
// 在这里执行不安全操作
}
// 不安全函数
unsafe fn dangerous_operation() {
// 函数体
}
// 调用不安全函数必须在unsafe块中
unsafe {
dangerous_operation();
}
解引用裸指针
裸指针是Rust中的一种原始指针类型,分为不可变裸指针*const T和可变裸指针*mut T。与引用不同,裸指针不受Rust所有权规则的限制,但也不享有编译器的安全性保证。
rust
fn demonstrate_raw_pointers() {
let mut num = 5;
// 创建裸指针是安全的
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
// 但解引用裸指针必须在unsafe块中
unsafe {
println!("r1 points to: {}", *r1);
println!("r2 points to: {}", *r2);
// 通过可变裸指针修改值
*r2 = 10;
println!("After modification, num = {}", num);
}
// 创建指向任意地址的裸指针
let address = 0x012345usize;
let r = address as *const i32;
// 解引用任意地址是极度危险的!
// unsafe { println!("Value at address {:p}: {}", r, *r); }
// 这可能导致段错误或读取垃圾数据
}
// 裸指针与引用的区别
fn pointer_vs_reference() {
let x = 10;
let y = 20;
// 引用必须始终有效且符合借用规则
let ref1 = &x;
// let ref2 = &mut x; // 错误:不能同时存在可变和不可变引用
// 裸指针没有这些限制
let ptr1 = &x as *const i32;
let ptr2 = &mut x as *mut i32; // 注意:这里x不是mut,但转换是允许的
let ptr3 = &y as *const i32;
unsafe {
println!("ptr1: {}", *ptr1);
// *ptr2 = 30; // 这是未定义行为!x不是mut
println!("ptr3: {}", *ptr3);
}
}
裸指针的主要使用场景包括:
- 与C代码交互
- 构建安全抽象
- 性能关键代码
- 实现特殊的数据结构
调用不安全函数或方法
不安全函数或方法的定义以unsafe关键字开头,表示该函数具有编译器无法验证的安全前提条件。调用这些函数必须在unsafe块中,表明调用者已经验证了这些前提条件。
rust
// 不安全函数定义
unsafe fn dangerous_operation(ptr: *mut i32) {
if !ptr.is_null() {
*ptr = 42;
}
}
// 包装不安全函数的安全接口
fn safe_operation(ptr: *mut i32) -> Result<(), &'static str> {
if ptr.is_null() {
Err("Null pointer provided")
} else {
unsafe {
dangerous_operation(ptr);
}
Ok(())
}
}
// 使用外部C函数
extern "C" {
fn abs(input: i32) -> i32;
fn malloc(size: usize) -> *mut std::ffi::c_void;
fn free(ptr: *mut std::ffi::c_void);
}
fn use_foreign_functions() {
unsafe {
// 调用C标准库的abs函数
let result = abs(-10);
println!("Absolute value: {}", result);
// 分配内存
let ptr = malloc(100);
if !ptr.is_null() {
println!("Allocated 100 bytes at {:p}", ptr);
// 使用分配的内存...
free(ptr);
}
}
}
// 不安全方法
struct UnsafeContainer {
data: *mut i32,
length: usize,
}
impl UnsafeContainer {
// 不安全方法
unsafe fn get_unchecked(&self, index: usize) -> i32 {
*self.data.add(index)
}
unsafe fn set_unchecked(&mut self, index: usize, value: i32) {
*self.data.add(index) = value;
}
// 安全包装方法
fn get(&self, index: usize) -> Option<i32> {
if index < self.length {
unsafe { Some(self.get_unchecked(index)) }
} else {
None
}
}
fn set(&mut self, index: usize, value: i32) -> Result<(), &'static str> {
if index < self.length {
unsafe { self.set_unchecked(index, value); }
Ok(())
} else {
Err("Index out of bounds")
}
}
}
创建不安全代码的安全抽象
将不安全代码封装在安全接口后面是Rust的重要设计模式。这样可以在享受不安全操作带来的能力的同时,对外提供安全的API。
rust
use std::slice;
// 安全地分割可变切片
fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = slice.len();
let ptr = slice.as_mut_ptr();
// 运行时检查
assert!(mid <= len, "mid index out of bounds");
unsafe {
(
// 从原始指针创建切片
slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
// 自定义向量类型的安全抽象
struct MyVec<T> {
ptr: *mut T,
cap: usize,
len: usize,
}
impl<T> MyVec<T> {
fn new() -> Self {
Self {
ptr: std::ptr::null_mut(),
cap: 0,
len: 0,
}
}
fn push(&mut self, value: T) {
if self.len == self.cap {
self.grow();
}
unsafe {
// 将值写入当前长度位置
self.ptr.add(self.len).write(value);
}
self.len += 1;
}
fn pop(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
unsafe {
// 读取最后一个元素
Some(self.ptr.add(self.len).read())
}
}
}
fn get(&self, index: usize) -> Option<&T> {
if index < self.len {
unsafe {
// 返回引用
Some(&*self.ptr.add(index))
}
} else {
None
}
}
fn grow(&mut self) {
// 增长逻辑(简化版)
let new_cap = if self.cap == 0 { 1 } else { self.cap * 2 };
let new_ptr = unsafe {
let layout = std::alloc::Layout::array::<T>(new_cap).unwrap();
std::alloc::alloc(layout) as *mut T
};
if !self.ptr.is_null() {
// 复制旧数据到新位置
unsafe {
std::ptr::copy_nonoverlapping(self.ptr, new_ptr, self.len);
// 释放旧内存
let layout = std::alloc::Layout::array::<T>(self.cap).unwrap();
std::alloc::dealloc(self.ptr as *mut u8, layout);
}
}
self.ptr = new_ptr;
self.cap = new_cap;
}
}
// 为MyVec实现Drop trait来防止内存泄漏
impl<T> Drop for MyVec<T> {
fn drop(&mut self) {
if !self.ptr.is_null() {
// 逐个调用元素的析构函数
for i in 0..self.len {
unsafe {
std::ptr::drop_in_place(self.ptr.add(i));
}
}
// 释放内存
unsafe {
let layout = std::alloc::Layout::array::<T>(self.cap).unwrap();
std::alloc::dealloc(self.ptr as *mut u8, layout);
}
}
}
}
使用extern函数调用外部代码
Rust可以通过外部函数接口(FFI)与其他语言进行交互,特别是C语言。这是通过extern块来实现的。
rust
// 声明外部C函数
extern "C" {
// C标准库函数
fn printf(format: *const std::ffi::c_char, ...) -> i32;
fn strlen(s: *const std::ffi::c_char) -> usize;
// 自定义C函数
fn my_c_function(x: i32) -> i32;
}
// 提供Rust函数给C调用
#[no_mangle] // 防止名称修饰
pub extern "C" fn rust_function(x: i32) -> i32 {
x * 2
}
// 安全的C字符串包装
struct CString {
ptr: *mut std::ffi::c_char,
}
impl CString {
fn new(s: &str) -> Option<Self> {
// 将Rust字符串转换为C字符串
let c_string = std::ffi::CString::new(s).ok()?;
let ptr = c_string.into_raw();
Some(Self { ptr })
}
fn as_ptr(&self) -> *const std::ffi::c_char {
self.ptr
}
unsafe fn print(&self) {
// 调用C的printf函数
printf(self.as_ptr());
}
}
impl Drop for CString {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe {
// 重新获取CString来正确释放内存
let _ = std::ffi::CString::from_raw(self.ptr);
}
}
}
}
fn demonstrate_ffi() {
// 创建C字符串
if let Some(c_str) = CString::new("Hello from Rust!\n") {
unsafe {
c_str.print();
// 使用C的strlen函数
let len = strlen(c_str.as_ptr());
println!("String length according to C: {}", len);
}
}
// 调用自定义C函数
unsafe {
let result = my_c_function(10);
println!("Result from C function: {}", result);
}
}
访问或修改可变静态变量
在Rust中,全局变量被称为静态变量。不可变静态变量是安全的,但可变静态变量在多线程环境中可能引发数据竞争,因此访问它们需要unsafe块。
rust
// 不可变静态变量 - 安全的
static HELLO_WORLD: &str = "Hello, world!";
static MAX_CONNECTIONS: u32 = 100;
// 可变静态变量 - 访问需要unsafe
static mut COUNTER: u32 = 0;
static mut GLOBAL_DATA: Vec<String> = Vec::new();
// 线程安全的计数器增加
fn increment_counter(inc: u32) {
unsafe {
COUNTER += inc;
}
}
// 安全的全局数据访问接口
fn add_global_data(item: String) -> Result<(), &'static str> {
unsafe {
// 检查数据有效性等前提条件
if item.is_empty() {
return Err("Cannot add empty string");
}
GLOBAL_DATA.push(item);
Ok(())
}
}
fn get_global_data() -> Vec<String> {
unsafe {
GLOBAL_DATA.clone() // 返回副本以避免并发问题
}
}
// 使用原子操作实现线程安全的全局计数器
use std::sync::atomic::{AtomicU32, Ordering};
static ATOMIC_COUNTER: AtomicU32 = AtomicU32::new(0);
fn increment_atomic_counter() {
ATOMIC_COUNTER.fetch_add(1, Ordering::SeqCst);
}
fn get_atomic_counter() -> u32 {
ATOMIC_COUNTER.load(Ordering::SeqCst)
}
fn demonstrate_statics() {
// 访问不可变静态变量是安全的
println!("{}", HELLO_WORLD);
println!("Max connections: {}", MAX_CONNECTIONS);
// 访问可变静态变量需要unsafe
unsafe {
println!("Initial counter: {}", COUNTER);
}
increment_counter(5);
unsafe {
println!("Counter after increment: {}", COUNTER);
}
// 使用安全的接口
add_global_data("Hello".to_string()).unwrap();
add_global_data("World".to_string()).unwrap();
let data = get_global_data();
println!("Global data: {:?}", data);
// 使用原子计数器
increment_atomic_counter();
increment_atomic_counter();
println!("Atomic counter: {}", get_atomic_counter());
}
实现不安全trait
当trait具有编译器无法验证的不变量时,它必须被标记为unsafe。实现这样的trait也需要使用unsafe impl。
rust
// 不安全trait的例子
unsafe trait UnsafeTrait {
// 这个方法有不安全的前提条件
fn dangerous_operation(&self);
// 这个方法返回的引用必须满足特定的生命周期要求
fn get_data(&self) -> &str;
}
// 标记为Send和Sync需要unsafe impl的情况
struct MyUnsafeType {
data: *mut i32,
len: usize,
}
// 我们需要手动保证MyUnsafeType是线程安全的
unsafe impl Send for MyUnsafeType {}
unsafe impl Sync for MyUnsafeType {}
impl MyUnsafeType {
fn new() -> Self {
Self {
data: std::ptr::null_mut(),
len: 0,
}
}
// 不安全的方法
unsafe fn initialize(&mut self, size: usize) {
let layout = std::alloc::Layout::array::<i32>(size).unwrap();
self.data = std::alloc::alloc(layout) as *mut i32;
self.len = size;
}
}
// 实现不安全trait
unsafe impl UnsafeTrait for MyUnsafeType {
fn dangerous_operation(&self) {
unsafe {
if !self.data.is_null() && self.len > 0 {
println!("Performing dangerous operation on data at {:p}", self.data);
}
}
}
fn get_data(&self) -> &str {
// 返回静态字符串,满足生命周期要求
"Unsafe data"
}
}
// 使用场景:与硬件交互
struct HardwareRegister {
address: usize,
}
unsafe trait HardwareAccess {
fn read(&self) -> u32;
fn write(&mut self, value: u32);
}
unsafe impl HardwareAccess for HardwareRegister {
fn read(&self) -> u32 {
unsafe {
// 从内存映射的硬件寄存器读取
(self.address as *const u32).read_volatile()
}
}
fn write(&mut self, value: u32) {
unsafe {
// 写入内存映射的硬件寄存器
(self.address as *mut u32).write_volatile(value)
}
}
}
fn demonstrate_unsafe_traits() {
let mut register = HardwareRegister { address: 0x1000 };
unsafe {
// 因为实现了HardwareAccess trait,我们可以调用这些方法
register.write(0x1234);
let value = register.read();
println!("Read value: 0x{:x}", value);
}
let mut unsafe_type = MyUnsafeType::new();
unsafe {
unsafe_type.initialize(10);
}
unsafe_type.dangerous_operation();
}
18.2 高级trait
trait是Rust中定义共享行为的核心机制。在前面的章节中我们已经了解了trait的基础知识,现在让我们深入探讨trait的高级用法。
关联类型
关联类型在trait定义中充当类型占位符,允许我们在实现trait时指定具体的类型。这提供了比泛型参数更清晰的接口。
rust
// 使用关联类型的trait
pub trait Iterator {
type Item; // 关联类型
fn next(&mut self) -> Option<Self::Item>;
}
// 自定义集合类型
struct Stack<T> {
items: Vec<T>,
}
impl<T> Stack<T> {
fn new() -> Self {
Self { items: Vec::new() }
}
fn push(&mut self, item: T) {
self.items.push(item);
}
fn pop(&mut self) -> Option<T> {
self.items.pop()
}
}
// 为Stack实现Iterator
impl<T> Iterator for Stack<T> {
type Item = T; // 指定关联类型的具体类型
fn next(&mut self) -> Option<Self::Item> {
self.pop()
}
}
// 更复杂的关联类型示例
trait Graph {
type Node;
type Edge;
fn nodes(&self) -> Vec<Self::Node>;
fn edges(&self, node: &Self::Node) -> Vec<Self::Edge>;
fn connect(&mut self, from: Self::Node, to: Self::Node, edge: Self::Edge);
}
// 具体图实现
struct SimpleGraph {
nodes: Vec<usize>,
edges: Vec<(usize, usize, String)>,
}
impl Graph for SimpleGraph {
type Node = usize;
type Edge = String;
fn nodes(&self) -> Vec<Self::Node> {
self.nodes.clone()
}
fn edges(&self, node: &Self::Node) -> Vec<Self::Edge> {
self.edges
.iter()
.filter(|(from, to, _)| from == node || to == node)
.map(|(_, _, label)| label.clone())
.collect()
}
fn connect(&mut self, from: Self::Node, to: Self::Node, edge: Self::Edge) {
self.edges.push((from, to, edge));
}
}
// 关联类型与泛型参数的对比
trait GenericConverter<T> {
fn convert(&self, value: T) -> T;
}
trait AssociatedConverter {
type Input;
type Output;
fn convert(&self, value: Self::Input) -> Self::Output;
}
// 使用关联类型的优势:更清晰的函数签名
fn process_converter<C>(converter: &C, value: C::Input) -> C::Output
where
C: AssociatedConverter,
{
converter.convert(value)
}
fn demonstrate_associated_types() {
let mut stack = Stack::new();
stack.push(1);
stack.push(2);
stack.push(3);
// 使用迭代器
while let Some(item) = stack.next() {
println!("Popped: {}", item);
}
let mut graph = SimpleGraph {
nodes: vec![1, 2, 3],
edges: Vec::new(),
};
graph.connect(1, 2, "edge_1_2".to_string());
graph.connect(2, 3, "edge_2_3".to_string());
println!("Nodes: {:?}", graph.nodes());
println!("Edges from node 2: {:?}", graph.edges(&2));
}
默认泛型参数和运算符重载
在trait定义中,我们可以为泛型参数指定默认类型。这在运算符重载等场景中特别有用。
rust
use std::ops::Add;
// 使用默认泛型参数的Add trait
// trait Add<Rhs = Self> {
// type Output;
// fn add(self, rhs: Rhs) -> Self::Output;
// }
// 为自定义类型实现Add
#[derive(Debug, PartialEq, Clone, Copy)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
// 使用不同的Rhs类型
struct Meters(f64);
struct Millimeters(f64);
impl Add<Meters> for Millimeters {
type Output = Millimeters;
fn add(self, other: Meters) -> Millimeters {
Millimeters(self.0 + (other.0 * 1000.0))
}
}
// 更复杂的运算符重载
use std::ops::{Mul, Index, IndexMut};
impl Mul<i32> for Point {
type Output = Point;
fn mul(self, scalar: i32) -> Point {
Point {
x: self.x * scalar,
y: self.y * scalar,
}
}
}
// 为矩阵实现索引
struct Matrix {
data: [[f64; 3]; 3],
}
impl Index<usize> for Matrix {
type Output = [f64; 3];
fn index(&self, index: usize) -> &Self::Output {
&self.data[index]
}
}
impl IndexMut<usize> for Matrix {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.data[index]
}
}
fn demonstrate_operator_overloading() {
let p1 = Point { x: 1, y: 2 };
let p2 = Point { x: 3, y: 4 };
let p3 = p1 + p2;
println!("p1 + p2 = {:?}", p3);
let scaled = p3 * 2;
println!("p3 * 2 = {:?}", scaled);
let distance_mm = Millimeters(1500.0) + Meters(2.0);
println!("1500mm + 2m = {}mm", distance_mm.0);
let mut matrix = Matrix {
data: [
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0],
],
};
println!("Matrix[1][2] = {}", matrix[1][2]);
matrix[0][0] = 10.0;
println!("After modification: Matrix[0][0] = {}", matrix[0][0]);
}
完全限定语法:消除歧义
当存在多个具有相同名称的trait方法或关联函数时,我们需要使用完全限定语法来明确指定要调用的是哪个实现。
rust
trait Pilot {
fn fly(&self);
fn name() -> String;
}
trait Wizard {
fn fly(&self);
fn name() -> String;
}
struct Human;
impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
fn name() -> String {
"Human".to_string()
}
}
impl Pilot for Human {
fn fly(&self) {
println!("This is your captain speaking.");
}
fn name() -> String {
"Pilot".to_string()
}
}
impl Wizard for Human {
fn fly(&self) {
println!("Up!");
}
fn name() -> String {
"Wizard".to_string()
}
}
fn demonstrate_fully_qualified_syntax() {
let person = Human;
// 默认调用Human的固有方法
person.fly();
// 使用完全限定语法调用trait方法
Pilot::fly(&person);
Wizard::fly(&person);
// 对于关联函数,必须使用完全限定语法
println!("A baby dog is called a {}", Human::name());
println!("A baby dog is called a {}", <Human as Pilot>::name());
println!("A baby dog is called a {}", <Human as Wizard>::name());
// 更复杂的例子
trait Animal {
type Species;
fn species_name() -> String;
}
struct Dog;
impl Animal for Dog {
type Species = String;
fn species_name() -> String {
"Canis lupus familiaris".to_string()
}
}
// 使用完全限定语法访问关联类型和函数
let _species: <Dog as Animal>::Species = "Labrador".to_string();
println!("Dog species: {}", <Dog as Animal>::species_name());
}
父trait:在另一个trait中使用某个trait的功能
父trait(supertrait)要求实现某个trait的类型也必须实现另一个trait,这允许我们在trait定义中使用其他trait的功能。
rust
use std::fmt;
// 父trait:要求实现OutlinePrint的类型也必须实现Display
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string(); // 使用Display的to_string方法
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {} *", output);
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}
// 为满足条件的类型实现OutlinePrint
struct Point {
x: i32,
y: i32,
}
// 必须先实现Display
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
// 现在可以实现OutlinePrint
impl OutlinePrint for Point {}
// 更复杂的父trait示例
trait Vehicle {
fn get_speed(&self) -> f64;
}
trait Car: Vehicle {
fn get_wheel_count(&self) -> u32;
// 可以使用父trait的方法
fn describe(&self) -> String {
format!(
"A car with {} wheels traveling at {:.1} mph",
self.get_wheel_count(),
self.get_speed()
)
}
}
struct Sedan {
speed: f64,
}
impl Vehicle for Sedan {
fn get_speed(&self) -> f64 {
self.speed
}
}
impl Car for Sedan {
fn get_wheel_count(&self) -> u32 {
4
}
}
fn demonstrate_supertraits() {
let point = Point { x: 3, y: 4 };
point.outline_print();
let sedan = Sedan { speed: 65.5 };
println!("{}", sedan.describe());
// 接受实现Car trait的参数,它自动满足Vehicle
fn print_vehicle_info<T: Car>(vehicle: &T) {
println!("Speed: {:.1}", vehicle.get_speed());
println!("Wheels: {}", vehicle.get_wheel_count());
}
print_vehicle_info(&sedan);
}
newtype模式
newtype模式通过创建新类型来绕过孤儿规则(orphan rule),从而在外部类型上实现外部trait。
rust
use std::fmt;
// 孤儿规则:我们不能为Vec<T>实现Display,因为两者都在外部定义
// impl<T> fmt::Display for Vec<T> { ... } // 错误!
// 使用newtype模式绕过限制
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
// 更实用的newtype示例:类型安全的值域
struct Percentage(f64);
impl Percentage {
fn new(value: f64) -> Option<Self> {
if (0.0..=100.0).contains(&value) {
Some(Self(value))
} else {
None
}
}
fn value(&self) -> f64 {
self.0
}
}
impl fmt::Display for Percentage {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:.1}%", self.0)
}
}
// 为newtype实现Deref来获得内部类型的访问
use std::ops::Deref;
impl Deref for Wrapper {
type Target = Vec<String>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
// 使用newtype实现自定义行为
struct Meters(f64);
impl Meters {
fn to_centimeters(&self) -> Centimeters {
Centimeters(self.0 * 100.0)
}
}
struct Centimeters(f64);
impl Centimeters {
fn to_meters(&self) -> Meters {
Meters(self.0 / 100.0)
}
}
fn demonstrate_newtype_pattern() {
// 基本newtype使用
let w = Wrapper(vec![
"hello".to_string(),
"world".to_string(),
]);
println!("w = {}", w);
// 使用Deref自动解引用
println!("First element: {}", w[0]);
println!("Length: {}", w.len());
// 类型安全的百分比
if let Some(pct) = Percentage::new(75.5) {
println!("Percentage: {}", pct);
}
if let Some(pct) = Percentage::new(150.0) {
println!("This won't print: {}", pct);
} else {
println!("Invalid percentage value");
}
// 单位安全转换
let distance_m = Meters(2.5);
let distance_cm = distance_m.to_centimeters();
println!("{} meters = {} centimeters", distance_m.0, distance_cm.0);
let back_to_m = distance_cm.to_meters();
println!("{} centimeters = {} meters", distance_cm.0, back_to_m.0);
}
18.3 高级类型
Rust的类型系统非常丰富,除了基本类型外,还提供了许多高级类型特性来帮助编写更安全、更表达力的代码。
类型别名
类型别名使用type关键字创建,主要用于简化复杂类型的书写和提高代码可读性。
rust
// 基本类型别名
type Kilometers = i32;
type UserId = u64;
type Result<T> = std::result::Result<T, std::io::Error>;
// 简化复杂类型
type Thunk = Box<dyn Fn() + Send + 'static>;
type ComplexResult<T> = Result<T, Box<dyn std::error::Error>>;
type StringMap<V> = std::collections::HashMap<String, V>;
// 在函数签名中使用类型别名
fn process_thunk(f: Thunk) {
f();
}
fn get_user_name(id: UserId) -> Option<String> {
Some(format!("User_{}", id))
}
fn read_config() -> ComplexResult<String> {
Ok("config content".to_string())
}
// 泛型类型别名
type Pair<T> = (T, T);
type Matrix<T> = Vec<Vec<T>>;
fn demonstrate_type_aliases() {
// 使用类型别名
let distance: Kilometers = 5;
let user_id: UserId = 12345;
println!("Distance: {} km", distance);
println!("User ID: {}", user_id);
// 使用复杂类型别名
let thunk: Thunk = Box::new(|| println!("Hello from thunk!"));
process_thunk(thunk);
let mut map: StringMap<i32> = StringMap::new();
map.insert("key".to_string(), 42);
println!("Map value: {}", map["key"]);
// 使用泛型别名
let point: Pair<f64> = (3.14, 2.71);
let matrix: Matrix<i32> = vec![
vec![1, 2, 3],
vec![4, 5, 6],
vec![7, 8, 9],
];
println!("Point: ({}, {})", point.0, point.1);
println!("Matrix: {:?}", matrix);
// 处理结果别名
match read_config() {
Ok(config) => println!("Config: {}", config),
Err(e) => println!("Error reading config: {}", e),
}
}
永不返回的never type
Rust有一个特殊的!类型,称为never type,表示计算永远不会完成。它在发散函数(不返回的函数)和模式匹配中非常有用。
rust
// 发散函数返回never type
fn forever() -> ! {
loop {
println!("I run forever!");
std::thread::sleep(std::time::Duration::from_secs(1));
}
}
fn panic_with_message(msg: &str) -> ! {
panic!("{}", msg);
}
// 在模式匹配中使用never type
fn process_result(result: Result<i32, &str>) -> i32 {
match result {
Ok(value) => value,
Err(msg) => panic_with_message(msg),
}
}
// never type可以强制转换为任何类型
fn match_with_never() {
let guess = "42";
let number: i32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
// 这个分支的类型是!,可以强制转换为i32
return; // 这里实际返回的是(),但编译器知道这个分支不会产生值
}
};
println!("Parsed number: {}", number);
}
// 实际应用:无限服务器循环
fn run_server() -> ! {
loop {
// 模拟处理请求
println!("Processing request...");
std::thread::sleep(std::time::Duration::from_secs(2));
}
}
// 在泛型代码中使用never type
fn unwrap_or_continue<T>(option: Option<T>) -> T {
match option {
Some(value) => value,
None => {
// continue的类型是!,可以强制转换为T
continue;
}
}
}
fn demonstrate_never_type() {
// 处理Result的例子
let good_result: Result<i32, &str> = Ok(42);
let bad_result: Result<i32, &str> = Err("Something went wrong");
println!("Good result: {}", process_result(good_result));
// println!("Bad result: {}", process_result(bad_result)); // 这会panic
// 模拟服务器运行(注释掉以避免实际运行无限循环)
// run_server();
// 在循环中使用
let mut count = 0;
loop {
count += 1;
if count > 3 {
break;
}
let value = unwrap_or_continue(Some(count));
println!("Processing value: {}", value);
}
// never type在错误处理中的高级用法
fn try_operation() -> Result<i32, Box<dyn std::error::Error>> {
// 模拟可能失败的操作
if std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() % 2 == 0
{
Ok(42)
} else {
Err("Operation failed".into())
}
}
match try_operation() {
Ok(value) => println!("Success: {}", value),
Err(_) => {
eprintln!("Operation failed, exiting...");
std::process::exit(1); // exit的返回类型是!
}
};
}
动态大小类型和Sized trait
动态大小类型(DST)是在编译时大小未知的类型,Rust通过Sized trait来跟踪类型的大小信息。
rust
// 最常见的DST:str
fn process_str(s: &str) {
println!("String slice: '{}' (length: {})", s, s.len());
}
// 另一个DST:[T]
fn process_slice(slice: &[i32]) {
println!("Slice: {:?} (length: {})", slice, slice.len());
}
// 泛型函数默认要求Sized
fn generic_function<T>(t: T) {
// T默认需要是Sized的
println!("Size of T: {}", std::mem::size_of::<T>());
}
// 使用?Sized放宽限制
fn flexible_function<T: ?Sized>(t: &T) {
// 现在T可以是DST,但我们只能通过引用使用它
println!("Working with sized or unsized type");
}
// 自定义DST-like行为
trait TraitForDST {
fn do_something(&self);
}
// 为所有类型实现trait,包括DST
impl<T: ?Sized> TraitForDST for T {
fn do_something(&self) {
println!("Doing something with a type");
}
}
// 使用Box来在堆上存储DST
fn create_dst_on_heap() -> Box<dyn std::fmt::Display> {
Box::new(42)
}
fn demonstrate_sized_trait() {
// 基本类型都是Sized的
let x = 42;
let y = "hello";
let z = vec![1, 2, 3];
generic_function(x);
generic_function(y);
generic_function(z);
// 使用?Sized的函数可以接受DST
flexible_function(&x);
flexible_function(y); // y是&str,str是DST
flexible_function("world");
// 为所有类型调用trait方法
x.do_something();
y.do_something();
// 在堆上存储DST
let boxed_dst = create_dst_on_heap();
println!("Boxed DST: {}", boxed_dst);
// 使用切片(DST)
let array = [1, 2, 3, 4, 5];
let slice = &array[1..4]; // 切片是DST
process_slice(slice);
// 字符串切片(DST)
let string = String::from("Hello, World!");
let string_slice = &string[7..12];
process_str(string_slice);
// trait对象也是DST
let display_vec: &dyn std::fmt::Display = &vec![1, 2, 3];
println!("Display vec: {}", display_vec);
let debug_vec: &dyn std::fmt::Debug = &vec![4, 5, 6];
println!("Debug vec: {:?}", debug_vec);
}
// 高级用法:自定义智能指针处理DST
struct MySmartPointer<T: ?Sized> {
data: Box<T>,
}
impl<T: ?Sized> MySmartPointer<T> {
fn new(data: Box<T>) -> Self {
Self { data }
}
}
impl<T: ?Sized> std::ops::Deref for MySmartPointer<T> {
type Target = T;
fn deref(&self) -> &T {
&self.data
}
}
fn demonstrate_custom_dst_handling() {
// 存储字符串切片
let pointer1 = MySmartPointer::new(Box::new("Hello") as Box<str>);
println!("String: {}", *pointer1);
// 存储trait对象
let pointer2 = MySmartPointer::new(Box::new(42) as Box<dyn std::fmt::Display>);
println!("Display: {}", *pointer2);
// 存储切片
let data = vec![1, 2, 3, 4, 5];
let slice_ptr = MySmartPointer::new(Box::new(data[1..4].to_vec().into_boxed_slice()));
println!("Slice: {:?}", &**slice_ptr);
}
18.4 高级函数和闭包
函数和闭包是Rust中的一等公民,它们具有强大的表达能力。让我们深入探讨它们的高级用法。
函数指针
函数指针允许我们将函数作为值传递,它们在需要动态选择函数或与C代码交互时非常有用。
rust
// 普通函数
fn add_one(x: i32) -> i32 {
x + 1
}
fn multiply_by_two(x: i32) -> i32 {
x * 2
}
fn square(x: i32) -> i32 {
x * x
}
// 接受函数指针作为参数的函数
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(f(arg))
}
// 返回函数指针的函数
fn get_operation(op: &str) -> Option<fn(i32) -> i32> {
match op {
"add_one" => Some(add_one),
"multiply_by_two" => Some(multiply_by_two),
"square" => Some(square),
_ => None,
}
}
// 函数指针与闭包的对比
fn process_with_function(value: i32, func: fn(i32) -> i32) -> i32 {
func(value)
}
fn process_with_closure<F>(value: i32, func: F) -> i32
where
F: Fn(i32) -> i32,
{
func(value)
}
// 函数指针在数据结构中的使用
struct OperationPipeline {
operations: Vec<fn(i32) -> i32>,
}
impl OperationPipeline {
fn new() -> Self {
Self {
operations: Vec::new(),
}
}
fn add_operation(&mut self, op: fn(i32) -> i32) {
self.operations.push(op);
}
fn execute(&self, mut value: i32) -> i32 {
for &op in &self.operations {
value = op(value);
}
value
}
}
fn demonstrate_function_pointers() {
// 基本使用
let result1 = do_twice(add_one, 5);
println!("do_twice(add_one, 5) = {}", result1);
let result2 = do_twice(square, 3);
println!("do_twice(square, 3) = {}", result2);
// 动态选择函数
if let Some(op) = get_operation("multiply_by_two") {
let result = op(10);
println!("multiply_by_two(10) = {}", result);
}
// 函数指针与闭包的对比
let func_result = process_with_function(5, add_one);
let closure_result = process_with_closure(5, |x| x + 1);
println!("Function result: {}", func_result);
println!("Closure result: {}", closure_result);
// 两者都可以传递函数
let func_as_closure = process_with_closure(5, add_one);
println!("Function as closure: {}", func_as_closure);
// 但闭包捕获环境时不能转换为函数指针
let y = 10;
// let closure_with_env = |x| x + y;
// process_with_function(5, closure_with_env); // 错误!
// 使用操作管道
let mut pipeline = OperationPipeline::new();
pipeline.add_operation(add_one);
pipeline.add_operation(multiply_by_two);
pipeline.add_operation(square);
let final_result = pipeline.execute(2);
println!("Pipeline result: {}", final_result);
// 函数指针与C交互
extern "C" {
fn qsort(
base: *mut std::ffi::c_void,
nmemb: usize,
size: usize,
compar: unsafe extern "C" fn(*const std::ffi::c_void, *const std::ffi::c_void) -> i32,
);
}
// 在实际代码中,我们会在这里定义比较函数并调用qsort
println!("FFI example ready (commented out for safety)");
}
返回闭包
由于闭包的类型是匿名的,直接返回闭包需要一些特殊的处理技巧。
rust
// 返回闭包的基本方法:使用Box
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
// 返回捕获环境的闭包
fn make_adder(y: i32) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |x| x + y)
}
// 使用impl Trait返回闭包(更现代的写法)
fn returns_closure_impl() -> impl Fn(i32) -> i32 {
|x| x * 2
}
fn make_multiplier(factor: i32) -> impl Fn(i32) -> i32 {
move |x| x * factor
}
// 复杂的闭包返回类型
fn create_complex_closure(operation: &str) -> Box<dyn Fn(i32, i32) -> i32> {
match operation {
"add" => Box::new(|a, b| a + b),
"multiply" => Box::new(|a, b| a * b),
"max" => Box::new(|a, b| a.max(b)),
_ => Box::new(|a, b| a - b),
}
}
// 闭包在迭代器转换中的应用
fn create_transformer() -> impl Fn(Vec<i32>) -> Vec<String> {
|numbers| {
numbers
.into_iter()
.map(|n| format!("Value: {}", n))
.collect()
}
}
// 高级模式:返回实现了多个trait的闭包
fn create_advanced_closure() -> Box<dyn Fn(i32) -> i32 + Send + Sync> {
Box::new(|x| x * x)
}
fn demonstrate_returning_closures() {
// 基本闭包返回
let closure1 = returns_closure();
println!("returns_closure(5) = {}", closure1(5));
let closure2 = returns_closure_impl();
println!("returns_closure_impl(5) = {}", closure2(5));
// 捕获环境的闭包
let add_five = make_adder(5);
println!("make_adder(5)(10) = {}", add_five(10));
let times_three = make_multiplier(3);
println!("make_multiplier(3)(4) = {}", times_three(4));
// 动态选择闭包
let operations = ["add", "multiply", "max", "unknown"];
for &op in &operations {
let closure = create_complex_closure(op);
let result = closure(5, 3);
println!("{}: 5 {} 3 = {}", op,
match op {
"add" => "+",
"multiply" => "*",
"max" => "max",
_ => "-",
},
result);
}
// 在数据处理中使用返回的闭包
let transformer = create_transformer();
let numbers = vec![1, 2, 3, 4, 5];
let strings = transformer(numbers);
println!("Transformed: {:?}", strings);
// 多线程中的闭包使用
let advanced_closure = create_advanced_closure();
let handle = std::thread::spawn(move || {
println!("Advanced closure in thread: {}", advanced_closure(6));
});
handle.join().unwrap();
// 闭包组合
fn compose<F, G, A, B, C>(f: F, g: G) -> impl Fn(A) -> C
where
F: Fn(A) -> B,
G: Fn(B) -> C,
{
move |x| g(f(x))
}
let add_one = |x| x + 1;
let multiply_by_two = |x| x * 2;
let add_then_multiply = compose(add_one, multiply_by_two);
println!("compose(add_one, multiply_by_two)(3) = {}", add_then_multiply(3));
}
函数和闭包的高级模式
让我们探索一些函数和闭包的高级使用模式。
rust
// 闭包状态机
fn create_counter() -> impl FnMut() -> i32 {
let mut count = 0;
move || {
count += 1;
count
}
}
// 记忆化(缓存)闭包
fn create_cached_closure<F, T, U>(f: F) -> impl FnMut(T) -> U
where
F: Fn(T) -> U,
T: std::hash::Hash + Eq + Clone,
U: Clone,
{
use std::collections::HashMap;
let mut cache = HashMap::new();
move |arg| {
if let Some(result) = cache.get(&arg) {
result.clone()
} else {
let result = f(arg.clone());
cache.insert(arg, result.clone());
result
}
}
}
// 策略模式使用闭包
struct Processor<F> {
strategy: F,
}
impl<F> Processor<F>
where
F: Fn(i32) -> i32,
{
fn new(strategy: F) -> Self {
Self { strategy }
}
fn process(&self, value: i32) -> i32 {
(self.strategy)(value)
}
}
// 闭包作为回调
struct EventHandler<F> {
callback: F,
}
impl<F> EventHandler<F>
where
F: Fn(&str),
{
fn new(callback: F) -> Self {
Self { callback }
}
fn handle_event(&self, event: &str) {
(self.callback)(event);
}
}
// 高阶函数:接受闭包并返回闭包的函数
fn create_logging_wrapper<F, T, U>(f: F, name: &'static str) -> impl Fn(T) -> U
where
F: Fn(T) -> U,
T: std::fmt::Debug,
U: std::fmt::Debug,
{
move |arg| {
println!("Calling {} with argument: {:?}", name, arg);
let result = f(arg);
println!("{} returned: {:?}", name, result);
result
}
}
fn demonstrate_advanced_patterns() {
// 状态闭包
let mut counter = create_counter();
println!("Counter: {}", counter());
println!("Counter: {}", counter());
println!("Counter: {}", counter());
// 记忆化
let expensive_calculation = |x: i32| {
println!("Performing expensive calculation for {}", x);
x * x
};
let mut cached_calc = create_cached_closure(expensive_calculation);
println!("First call: {}", cached_calc(5)); // 计算
println!("Second call: {}", cached_calc(5)); // 从缓存获取
// 策略模式
let double_processor = Processor::new(|x| x * 2);
let increment_processor = Processor::new(|x| x + 1);
println!("Double processor: {}", double_processor.process(5));
println!("Increment processor: {}", increment_processor.process(5));
// 事件处理
let event_handler = EventHandler::new(|event| {
println!("Event received: {}", event);
});
event_handler.handle_event("button_click");
event_handler.handle_event("key_press");
// 高阶函数
let add_one = |x| x + 1;
let logged_add_one = create_logging_wrapper(add_one, "add_one");
let result = logged_add_one(5);
println!("Final result: {}", result);
// 闭包组合和管道
let functions: Vec<Box<dyn Fn(i32) -> i32>> = vec![
Box::new(|x| x + 1),
Box::new(|x| x * 2),
Box::new(|x| x - 3),
];
let initial_value = 5;
let final_value = functions.into_iter().fold(initial_value, |acc, f| f(acc));
println!("Pipeline result: {}", final_value);
// 条件闭包执行
fn execute_if<F>(condition: bool, true_func: F, false_func: F) -> impl FnOnce() -> ()
where
F: FnOnce() -> (),
{
move || {
if condition {
true_func();
} else {
false_func();
}
}
}
let success_handler = || println!("Operation succeeded!");
let error_handler = || println!("Operation failed!");
let operation = execute_if(true, success_handler, error_handler);
operation();
}
总结
本章深入探讨了Rust的高级特性,包括:
- 不安全Rust:如何在必要时绕过编译器的安全检查,同时保持代码的整体安全性
- 高级trait:关联类型、默认泛型参数、完全限定语法、父trait和newtype模式
- 高级类型:类型别名、never type、动态大小类型和Sized trait
- 高级函数和闭包:函数指针、返回闭包的各种技巧以及函数式编程模式
这些高级特性让Rust能够在保持内存安全的同时,提供系统级编程所需的底层控制能力。理解这些特性对于编写高效、安全的Rust代码至关重要,特别是在构建库、框架和系统软件时。
记住,不安全Rust应该谨慎使用,并且总是要将其封装在安全的抽象中。高级trait和类型系统特性则可以帮助我们构建更表达力强、更安全的API。函数和闭包的高级用法则让我们能够编写更灵活、更可复用的代码。