proc-macro-workshop:builder-3-5

审题

// Generate methods on the builder for setting a value of each of the struct
// fields.
//
//     impl CommandBuilder {
//         fn executable(&mut self, executable: String) -> &mut Self {
//             self.executable = Some(executable);
//             self
//         }
//
//         ...
//     }

use derive_builder::Builder;

#[derive(Builder)]
pub struct Command {
    executable: String,
    args: Vec<String>,
    env: Vec<String>,
    current_dir: String,
}

fn main() {
    let mut builder = Command::builder();
    builder.executable("cargo".to_owned());
    builder.args(vec!["build".to_owned(), "--release".to_owned()]);
    builder.env(vec![]);
    builder.current_dir("..".to_owned());
}

这道题清晰明了,主要是为XXBuilder实现各种的setter方法。
相关的模板和fields之前都已经获取好了,这里直接实现就行了。

值的注意的是,实现setter方法的时候经常会进行链式调用。
虽然用例里面都是分开调用的,但是在第五关的用例中,会进行链式调用

use  derive_builder::Builder;
#[derive(Builder)]
    pub struct Command {
    executable: String,
    args: Vec<String>,
    env: Vec<String>,
    current_dir: String,
}

fn  main() {
    let command = Command::builder()
        .executable("cargo".to_owned())
        .args(vec!["build".to_owned(), "--release".to_owned()])
        .env(vec![])
        .current_dir("..".to_owned())
        .build()
        .unwrap();
    assert_eq!(command.executable, "cargo");
}

因此,在实现的时候顺便返回自身对象的引用,合二为一更方便。

题解

// solution35.rs
pub(super) fn soultion(
    fields: &crate::common::FieldsType,
    builder_ident: &syn::Ident,
) -> proc_macro2::TokenStream {
    let idents: Vec<_> = fields.iter().map(|f| &f.ident).collect();
    let tys: Vec<_> = fields.iter().map(|f| &f.ty).collect();

    quote::quote! {
        impl #builder_ident {
            #(
                pub fn #idents(&mut self, #idents: #tys) -> &mut Self {
                    self.#idents = std::option::Option::Some(#idents);
                    // 返回自身引用,顺便解决第五题链式调用
                    self
                }
            )*
        }
    }
}

可以看到,除了基本的模板,我们主要依靠解析的信息让单一的模板多样化。

整体

// lib.rs
mod solution2;
mod solution35;

fn solution1(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
    let fields = {
        match common::parse_fields(&ast) {
            Ok(f) => f,
            Err(_e) => std::panic!(std::stringify!(_e)),
        }
    };

    let origin_ident = &ast.ident;
    let builder_ident = &quote::format_ident!("{}Builder", origin_ident);
    let mut token_stream = proc_macro2::TokenStream::new();

    // solution2
    let solution2_stream = solution2::solution(fields, origin_ident, builder_ident);
    token_stream.extend(solution2_stream);

    // solution35
    let solution35_stream = solution35::soultion(fields, builder_ident);
    token_stream.extend(solution35_stream);

    proc_macro::TokenStream::from(token_stream)
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!