面试题答案
一键面试-
自定义derive特性的一般步骤:
- 定义特性(trait):首先定义一个trait,它包含需要实现的方法。
- 定义过程宏(procedural macro):使用
proc_macro
crate来编写一个过程宏,该宏会自动为实现了derive特性的类型生成代码。 - 使用
#[derive(...)]
语法:在需要自动实现该特性的结构体或枚举上使用#[derive(YourDeriveTrait)]
语法。
-
示例:自定义
Debug
风格的derive特性:- 定义特性:
pub trait MyDebug {
fn my_debug(&self) -> String;
}
- 定义过程宏:
首先在
Cargo.toml
中添加依赖:
[lib]
proc-macro = true
[dependencies]
syn = "1.0"
quote = "1.0"
然后编写过程宏代码(通常在src/lib.rs
):
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(MyDebug)]
pub fn my_debug_derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let name = &ast.ident;
let gen = match &ast.data {
syn::Data::Struct(s) => {
let fields = &s.fields;
let field_debugs = fields.iter().map(|f| {
let name = &f.ident;
quote! {
let #name_debug = format!("{:?}", self.#name);
}
});
let field_names = fields.iter().filter_map(|f| f.ident.as_ref()).map(|i| i.to_string());
let field_debug_strings = field_names.iter().map(|n| format!("{}: {}", n, n.clone() + "_debug"));
let debug_string = field_debug_strings.collect::<Vec<_>>().join(", ");
quote! {
impl MyDebug for #name {
fn my_debug(&self) -> String {
#( #field_debugs )*
format!( "{} {{ {} }}", stringify!(#name), #debug_string )
}
}
}
}
_ => panic!("MyDebug can only be derived for structs"),
};
gen.into()
}
- 使用
#[derive(MyDebug)]
:
#[derive(MyDebug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 10, y: 20 };
println!("{:?}", p.my_debug());
}
在上述代码中,我们定义了MyDebug
特性,并编写了一个过程宏my_debug_derive
来为结构体自动生成MyDebug
特性的实现,最后在Point
结构体上使用#[derive(MyDebug)]
来自动获得特性的实现。