Is there a way to determine the data type inside structures?

So I have a sample code here:

pub fn main()
{
    struct Rect
    {
        x       : u32,
        y       : u32,
    }
    impl Rect
    {
        fn init() -> Self
        {
            Self{x: 10, y: 20}
        }
    }

    let rect = Rect::init();
}

So to determine the data type of rect.x, is there a way to determine this?

To point out the obvious, you know the data type of rect.x because you wrote the definition for Rect and Rust is a statically typed language. Besides looking at the struct defintion, hovering over rect.x in something like VS Code will show that rect.x is a u32. Another trick is to do something like let () = rect.x to make the compiler error out with something like "Error: expected () but found u32".

If you want to get the type name for an arbitrary value, you can use the nightly-only std::any::type_name_of_val() function. On stable, we can implement our own polyfill (which I've called type_name()).

struct Rect {
    x: u32,
    y: u32,
}

impl Rect {
    fn init() -> Self {
        Self { x: 10, y: 20 }
    }
}

fn type_name<T>(_: &T) -> &'static str {
    std::any::type_name::<T>()
}

pub fn main() {
    let rect = Rect::init();

    println!("rect.x is {}", type_name(&rect.x));
}

(playground)

Prints:

rect.x is u32

thanks for this, I wanted to build something that automatically handles stuff based on data types and so I wanted to capture the data type so yeah this helps thanks.

If that's the case, you are almost certainly looking for traits - the suggestions I gave are for mainly printing the type's name and other human-oriented purposes.

You don't want to try and switch on a type's name because that'll be brittle and not very helpful in the long run. Instead, define a trait so it encapsulates some desired set of behaviour, then implement the trait for the data types you care about.

3 Likes

Traits are like for impl? Sorry I don't know a lot of things about Rust.

Yeah I can look into that :slight_smile:

Think of traits as interfaces or contracts for your code. When you want to say "Rect implements trait Foo" you would write impl Foo for Rect { ... } and fill in the ... with whatever is appropriate.

For example, if you want to print a value with println!("{}", x) we require that x is "printable" by requiring it to implement the std::fmt::Display trait. That's a lot more robust than some sort of if type_of(&x) == "u32" { ... } else if type_of(&x) == "Rect" { ... } chain and it lets the compiler reason about x a lot more (e.g. trying to print something that doesn't implement Display triggers a compiler error instead of blowing up at runtime, or worse, failing silently).

You should read the Traits: Defining Shared Behavior chapter from the book. It explains the concept in more detail.

2 Likes

If you are in a situation where you have one out of many types, I always recommend to use an enum. Traits and trait objects lead often to a lot of subtle issues, whereas enums "just work".

1 Like

Thanks for that mate.

Thanks I could try that out.

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.