How can I convert a struct name to a String?

Is there macro that would let me do this?

#[derive(IntoString)]
struct MyStruct{
    field: usize
}

let my_struct  = MyStruct{
    field: 1,
};

let name_of_struct: String = my_struct.into();
println!("{}", name_of_struct); // prints "MyStruct"
1 Like

For a type without fields, #[derive(Debug)] will just print the type name. You can get that as a string with format!("{:?}", my_struct). I don't know of a more general solution though.

Yep I should've been more clear, the struct will have fields.

You can use type_name, but read the documentation to be aware of the limitations (you cannot rely on what is returned always being the same over time, etc. etc.)

type_name_of_val is available on nightly, but has a trivial implementation if you need it on stable.

10 Likes

Is there a way to get it without the preludes?

By preludes I assume you mean, path::to::your::Type. As per the documentation, you can't rely on it. Maybe you get preludes, maybe you don't, maybe it changes with the phase of the moon.

You could write a wrapper that attempts to find a suitable substring (or performs a transformation and returns a String), making certain assumptions about what the return will roughly look like. The more exotic the type, the more shaky the assumptions.

2 Likes

Here's a starting point that may be good enough for your needs. I only tried it on a few basic types.

fn my_type_name_of_val<T: ?Sized>(_val: &T) -> String {
    // Off the top of my head
    static SPLITTERS: &'static [char] = &[
        '(', ')', '[', ']', '<', '>', '{', '}',
        // EDIT: ' ' for `Foo as Bar`, ',' for tuples and
        // `Fn` args, `=` for `dyn` associated types, ...
        ' ', ',', '=',
    ];
    type_name::<T>()
        // Split into substrings but preserve the delimiters, and...
        .split_inclusive(SPLITTERS)
        // ...for each substring...
        .flat_map(|component| {
            // ...return the portion after the last "::"
            // (or the entire substring, if there is no "::")...
            component.rsplit("::").next()
        })
        // ...and collect into a `String`
        .collect()
}
2 Likes

If you just need a struct name, that’s a not terrible macro to write if you're so inclined. (It's a bit harder to get it to work on playgrounds :slight_smile: ) Granted this could use some more sanity checks for robustness. I just didn't feel like pulling in the syn dependency which would make it easier to make it robust.

playground

use proc_macro::{TokenStream,TokenTree};

#[proc_macro_derive(AsStr)]
pub fn derive_as_string(item: TokenStream) -> TokenStream {
    let mut it = item.into_iter();
    while let Some(tt) = it.next() {
        match tt {
            TokenTree::Ident(id) => {
                if id.to_string() == "struct" {
                    let struct_name = it.next().unwrap().to_string();
                    return format!(r#"
                        impl {} {{ fn as_str(&self) -> &'static str {{ "{}" }} }}
                    "#, struct_name, struct_name).parse().unwrap()
                }
            }
            _ => {}
        }
    }
    panic!("no ident found")
}

...
#[derive(AsStr)]
struct ABC {}
let abc = ABC {} ;
println!("{}", abc.as_str())

EDIT:
This is a bit different from @quinedot's since he's capturing the whole type instead of just a struct name. I'm not sure which you were asking for looking at your post again

9 Likes

Where do you want to use this?

If for serialization, use Serde. If for debugging, use Debug. If for storing various types in a hash table, use enum or type-map or Any. If for type-generic code, use traits and trait bounds or enum.

Basicaly, even if you could, needing to do such thing in a statically-typed language is a red flag. It's likely that you'll end up with an inefficient/fragile/overcomplicated solution. It's not something that Rust intended to support, and has many features that avoid it.

4 Likes

It’s also possible to (ab-)use serde’s Serialize or Deserialize for this, which comes with preexisting derive-macros.

Take a look: Rust Playground

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.