Rust 结构体、属性宏与自定义派生宏

  • 0xE
  • 发布于 2025-03-24 13:19
  • 阅读 350

在 Rust 中,属性宏和自定义派生宏用于在编译时处理代码,通常为结构体添加功能或修改其定义。本文将解析这些宏的工作原理,并介绍与结构体相关的 impl 和 trait。

在 Rust 中,属性宏和自定义派生宏用于在编译时处理代码,通常为结构体添加功能或修改其定义。本文将解析这些宏的工作原理,并介绍与结构体相关的 impl 和 trait。


结构体的实现:impl

以下是一个简单的 Person 结构体:

struct Person {
    name: String,
    age: u8,
}

在 Rust 中,通过 impl 为结构体定义关联函数和方法。例如:

struct Person {
    age: u8,
    name: String,
}

impl Person {
    // 创建新的 Person 实例
    fn new(name: String, age: u8) -> Self {
        Person { name, age }
    }

    // 判断是否达到饮酒年龄
    fn can_drink(&self) -> bool {
        self.age >= 21
    }

    // 返回一年后的年龄
    fn age_in_one_year(&self) -> u8 {
        self.age + 1
    }
}

fn main() {
    let person = Person::new(String::from("Jesserc"), 19);
    println!("{:?}", person.can_drink()); // false
    println!("{:?}", person.age_in_one_year()); // 20
    println!("{:?}", person.name); // "Jesserc"
}

说明

  • new 是关联函数,通过 Person::new 调用。
  • can_drink 和 age_in_one_year 是方法,通过实例调用。

Rust 的 Trait

Trait 定义了类型间的共享行为,类似 Solidity 的接口。例如,为 Car 和 Boat 定义速度转换:

trait Speed {
    fn get_speed_kph(&self) -> f64;
}

struct Car {
    speed_mph: f64,
}

struct Boat {
    speed_knots: f64,
}

impl Speed for Car {
    fn get_speed_kph(&self) -> f64 {
        self.speed_mph * 1.60934 // 英里/小时转公里/小时
    }
}

impl Speed for Boat {
    fn get_speed_kph(&self) -> f64 {
        self.speed_knots * 1.852 // 节转公里/小时
    }
}

fn main() {
    let car = Car { speed_mph: 60.0 };
    let boat = Boat { speed_knots: 30.0 };
    println!("Car Speed: {} km/h", car.get_speed_kph()); // 96.5604 km/h
    println!("Boat Speed: {} km/h", boat.get_speed_kph()); // 55.56 km/h
}

说明:Speed Trait 确保 Car 和 Boat 都实现 get_speed_kph。


宏如何操作结构体

在 Solana 的 Anchor 框架中,常用类函数宏(如 println!)、属性宏(如 #[program])和自定义派生宏(如 #[derive(Accounts)])。以下通过示例展示属性宏的强大能力。

示例 1:属性宏添加字段

我们创建一个属性宏,为结构体添加字段并生成方法。

项目设置

cargo new macro-demo --lib
cd macro-demo
touch src/main.rs

Cargo.toml:

[lib]
proc-macro = true

[dependencies]
syn = {version="1.0.57", features=["full","fold"]}
quote = "1.0.8"

主程序(src/main.rs)

use macro_demo::*;

#[foo_bar_attribute]
struct MyStruct {
    baz: i32,
}

fn main() {
    let demo = MyStruct::default();
    println!("struct is {:?}", demo); // 输出结构体
    println!("double foo: {}", demo.double_foo()); // 调用生成的方法
}

宏定义(src/lib.rs)

extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemStruct};

#[proc_macro_attribute]
pub fn foo_bar_attribute(_metadata: TokenStream, _input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(_input as ItemStruct);
    let struct_name = &input.ident;

    TokenStream::from(quote! {
        #[derive(Debug)]
        struct #struct_name {
            foo: i32,
            bar: i32,
        }

        impl Default for #struct_name {
            fn default() -> Self {
                #struct_name { foo: 10, bar: 20 }
            }
        }

        impl #struct_name {
            fn double_foo(&self) -> i32 {
                self.foo * 2
            }
        }
    })
}

运行 cargo run --bin macro-demo 输出:

struct is MyStruct { foo: 10, bar: 20 }
double foo: 20

说明:宏重定义了 MyStruct,添加 foo 和 bar 字段,并生成了 double_foo 方法。

示例 2:属性宏移除字段

修改 src/lib.rs:

extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemStruct};

#[proc_macro_attribute]
pub fn destroy_attribute(_metadata: TokenStream, _input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(_input as ItemStruct);
    let struct_name = &input.ident;

    TokenStream::from(quote! {
        #[derive(Debug)]
        struct #struct_name {}
    })
}

修改 src/main.rs:

use macro_demo::*;

#[destroy_attribute]
struct MyStruct {
    baz: i32,
    qux: i32,
}

fn main() {
    let demo = MyStruct { baz: 3, qux: 4 }; // 编译失败
    println!("struct is {:?}", demo);
}

编译时会报错,因为宏移除了所有字段。


自定义派生宏 #[derive(…)]

派生宏为结构体添加功能,而非重写它。例如:

struct Foo {
    bar: i32,
}

pub fn main() {
    let foo = Foo { bar: 3 };
    println!("{:?}", foo); // 编译失败:Foo 未实现 Debug
}

添加 #[derive(Debug)]:

#[derive(Debug)]
struct Foo {
    bar: i32,
}

pub fn main() {
    let foo = Foo { bar: 3 };
    println!("{:?}", foo); // Foo { bar: 3 }
}

说明:#[derive(Debug)] 为 Foo 生成了 Debug 实现的 fmt 方法。


总结:Anchor 中的宏应用

Anchor 使用宏简化 Solana 开发:

use anchor_lang::prelude::*;

declare_id!("AcCKUb5GBSGgX1dz6GMTyvZwSBsnnzZJigac9kB7cfXr"); // 类函数宏

#[program] // 属性宏:生成指令路由
pub mod macro_example {
    use super::*;
    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        Ok(())
    }
}

#[derive(Accounts)] // 派生宏:生成账户验证逻辑
pub struct Initialize {}
  • impl:为结构体绑定方法或关联函数。
  • Trait:定义共享行为,强制实现特定方法。
  • 属性宏:可重写结构体定义。
  • 派生宏:增强结构体功能。

你无需精通宏编写即可开发 Solana 程序,但理解其作用能让你更好地掌握框架。


【笔记配套代码】 https://github.com/0xE1337/rareskills_evm_to_solana 【参考资料】 https://learnblockchain.cn/column/119 https://www.rareskills.io/solana-tutorial

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
0xE
0xE
0x59f6...a17e
17年进入币圈,Web3 开发者。刨根问底探链上真相,品味坎坷悟 Web3 人生。