Rust奇妙的玩出天际的花活儿

文摘   2024-11-27 11:03   美国  

点击上方蓝字 江湖评谈设为关注/星标




前言

Rust有各种各样的花活儿,奇妙的操作。官方搞这些东西当然不是搞着玩的,主要是为了更契合Rust主打内存安全这一特性,也即是说为了万无一失的确保内存安全,才整出的这些奇妙的操作。主要是分为编译器魔法特性,DSL自定义,Unsafe不安全内存,极简线程池,生命周期等等,本篇分别看下。

奇妙操作之旅

一.编译器魔法特性

超级强大的编译器魔法特性,将为你展示零开销操作,生命周期绑定,泛型类型确定,自定义关联等四大特征。

先上代码(一套代码浓缩了以上四大特征):

use std::mem;use std::marker::PhantomData;
struct MyType<T> { _marker: PhantomData<T>,}
struct MyLifetime<'a, T> { _marker: PhantomData<&'a T>,}
struct MyContainer<T> { _marker: PhantomData<T>,}
impl<T> MyType<T> { fn new() -> Self { MyType { _marker: PhantomData } }}
fn main() { //零开销 println!("Size of MyType<u32>: {}", mem::size_of::<MyType<u32>>()); println!("Size of PhantomData<u32>: {}", mem::size_of::<PhantomData<u32>>()); //生命周期绑定 let value = 42; let _instance = MyLifetime::<i32> { _marker: PhantomData }; //泛型类型确认 let _instance2: MyContainer<u32> = MyContainer { _marker: PhantomData };    //自定义关联 let _instance3: MyType<String> = MyType::new();}

以上代码完整的展示了零开销,生命周期绑定,泛型类型确定,自定义关联四大特征。下面一一解析下:

1.零开销

PhantomData<T> 是 Rust 标准库中提供的一个零大小类型(Zero-Sized Type, ZST),它的主要作用是告诉编译器你的结构体实际上与泛型参数 T 有关联,即使这个泛型参数没有被实际存储或使用。

确认这个零开销,我们直接使用以下代码打印出了其结构体大小,结果为0,完全没有任何代码。

println!("Size of MyType<u32>: {}", mem::size_of::<MyType<u32>>());println!("Size of PhantomData<u32>: {}", mem::size_of::<PhantomData<u32>>());
结果:Size of MyType<u32>: 0Size of PhantomData<u32>: 0

2.生命周期默认绑定

_instance变量对i32的类型,引用了'a的生命周期。由于没有显式为 'a 指定生命周期来源,编译器会尽量推导出合理的结果:它发现 MyLifetime 的生命周期 'a 没有被直接约束到任何数据。然而,_markerPhantomData<&'a T>,意味着它可以与某个实际数据(如 value)的生命周期相关联。因此,_instance 的生命周期会默认绑定到 value

let value = 42;let _instance = MyLifetime::<i32> { _marker: PhantomData };

3.泛型类型确定

_instance2通过 PhantomData 为泛型类型参数 T 建立一种逻辑上的联系,即使 MyContainer<T> 并不实际存储 T 类型的值。

即使没有存储 u32PhantomData<u32> 表明 MyContainer 是与 u32 绑定的。

let _instance2: MyContainer<u32> = MyContainer { _marker: PhantomData };

4.自定义关联

编译器知道 MyType 和 String 是关联的,即使没有存储数据。

let _instance3: MyType<String> = MyType::new();

二.DSL操作

DSL(Domain-Specific Language, 领域特定语言)是一种专注于特定问题领域的语言或语法,它通常嵌入在 Rust 代码中,用来更简洁、直观地描述领域问题。Rust 的 DSL 并非语言本身的内建特性,而是通过其强大的语法扩展能力(例如宏、属性宏、编译器插件等)实现的。

1.自定义DSL,在Rust里面嵌入类似于Html的语法操作,像 yew 或 maud 这样的框架使用宏创建 HTML DSL,这里的 DSL 模仿了 HTML 的结构,让开发者能以直观的方式编写模板。

//Cargo.toml引入[dependencies]maud = "0.24"  # 确保使用最新的稳定版本
代码:use maud::html;
fn main() { let markup = html! { html { head { title { "Hello, world!" } } body { h1 { "This is a title" } p { "This is a paragraph" } } } }; println!("{}", markup.into_string());}

运行的结果:

<html><head><title>Hello, world!</title></head><body><h1>This is a title</h1><p>This is a paragraph</p></body></html>

2.自定义命令行解析 ,这里的链式方法调用(如 .arg()、.about())构造了一个 DSL,描述命令行工具的配置。

运行输出结果:

#cargo run -- --config my_config.tomlwarning: `/root/.cargo/config` is deprecated in favor of `config.toml`note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`warning: `/root/.cargo/config` is deprecated in favor of `config.toml`note: if you need to support cargo 1.38 or earlier, you can symlink `config` to `config.toml`    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s     Running `target/debug/minglinghang --config my_config.toml`Using config file: my_config.toml

3.自定义 DSL,实现一套自己认为美观的加减法:

macro_rules! calculate {    // 匹配加法    ($a:expr, +, $b:expr) => {        $a + $b    };    // 匹配减法    ($a:expr, -, $b:expr) => {        $a - $b    };}
fn main() { // 加法 let result_add = calculate!(5, +, 3); println!("Result of 5 + 3: {}", result_add); // 输出: Result of 5 + 3: 8
// 减法 let result_sub = calculate!(10, -, 4); println!("Result of 10 - 4: {}", result_sub); // 输出: Result of 10 - 4: 6}

三.unsafe不安全操作

Rust虽然安全,但可以通过unsafe操作不安全的代码,没错它其实非常灵活。

fn main() {    let mut x = 5;    let ptr = &mut x as *mut i32;
unsafe { *ptr = 10; } println!("x is now: {}", x);}

三.极简线程池

利用迭代器和闭包,简化并发代码的结构。

use std::thread;
fn main() { let handles: Vec<_> = (0..4).map(|i| { thread::spawn(move || { println!("Thread {} is running", i); }) }).collect();
for handle in handles { handle.join().unwrap(); }}

四:生命周期

通过生命周期标注,可以避免不必要的数据拷贝并保证安全:

fn longest<'a>(s1: &'a str, s2: &'a str) -> &'a str {    if s1.len() > s2.len() {        s1    } else {        s2    }}
fn main() { let s1 = String::from("Rust"); let s2 = String::from("World"); println!("Longest: {}", longest(&s1, &s2));}

结尾

以上列举了编译器魔法特性,DSL自定义,Unsafe不安全操作,极简线程池,生命周期等几大特性,但是实际上Rust里面深藏不露的特性远非这么多,不计以其数。通过这些特性,可以更好的,更快速的,更固安全的程序,

往期精彩回顾

Rust会取代C++吗?

Rust安全指针,如何回收堆?


江湖评谈
记录,分享,自由。
 最新文章