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"
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"
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.
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.
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()
}
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 ) 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.
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
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.
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.