Generating an Implementation

Walk the fields, build the impl block, and handle generics correctly.

Part 2 of 3Updated
let fields = match &input.data {
    Data::Struct(DataStruct { fields: Fields::Named(f), .. }) => &f.named,
    _ => return syn::Error::new_spanned(&input, "Describe only supports structs with named fields")
        .to_compile_error()
        .into(),
};

let lines = fields.iter().map(|f| {
    let name = f.ident.as_ref().unwrap();
    let label = name.to_string();
    quote! { out.push_str(&format!("{}: {:?}\n", #label, self.#name)); }
});

let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

quote! {
    impl #impl_generics Describe for #name #ty_generics #where_clause {
        fn describe(&self) -> String {
            let mut out = String::new();
            #(#lines)*
            out
        }
    }
}.into()

split_for_impl is the thing people forget. Without it your macro breaks on any generic struct. #(#lines)* is quote’s repetition syntax β€” it splices an iterator of token streams.

Refer to items by full path (::std::string::String) so your macro works in a crate that shadowed the name.