Hao Li
Hao Li

Reputation: 1762

Is there a way to check a struct has a field and check its type?

I am trying to write a test to determine whether struct A has a property a and its type is i32.

pub struct A {
    a: i32,
}

#[test]
pub fn test_A() {
    assert!(A.hasattr("a"));
    assert_is_type!(A.a, i32);
}

Upvotes: 3

Views: 6059

Answers (3)

mcarton
mcarton

Reputation: 29981

Note: This answer is insufficient in some cases. See rp123's answer for a better solution.


You can assert that a type has a property of a specific type at compile time by trying to use it in a context that requires that type:

macro_rules! assert_is_type {
    ($t:ty, $i:ident: $ti:ty) => {
        const _: () = {
            fn dummy(v: $t) {
                let _: $ti = v.$i;
            }
        };
    }
}

pub struct A {
    a: i32,
}

assert_is_type!(A, a: i32);

// do not compile
assert_is_type!(A, b: i32);
assert_is_type!(A, a: i64);

(Permalink to the playground)

Upvotes: 3

rp123
rp123

Reputation: 3643

mcarton's answer is close, but in some special cases defeated by implicit type coercions (https://doc.rust-lang.org/reference/type-coercions.html). Here is an example where it gets the field type wrong: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=dd2b2659bc75603f1bd5d0d1d34d5303.

This can be fixed by creating a modification that avoids an implicit coercion context, see playground

pub trait EqType {
    type Itself;
}

impl<T> EqType for T {
    type Itself = T;
}

fn ty_must_eq<T, U>(_: T) where T: EqType<Itself=U> {}

macro_rules! assert_is_type {
    ($t:ty, $i:ident: $ti:ty) => {
        const _: () = {
            #[allow(unused)]
            fn dummy(v: $t) {
                ty_must_eq::<_, $ti>(v.$i);
            }
        };
    }
}

pub struct A {
    a: &'static Box<i32>,
    b: u32,
}

// Correctly accepted:
assert_is_type!(A, a: & Box<i32>);
assert_is_type!(A, b: u32);

// Correctly rejected:
assert_is_type!(A, a: &i32);
assert_is_type!(A, b: i32);

Upvotes: 2

Locke
Locke

Reputation: 8934

In Rust, unlike some languages like Python, the types of all values must be known at compile time. This test doesn't make sense because in order to compile the code you must already know the answer.

If a field in a struct is optional, put it in an Option.

pub struct A {
    a: Option<i32>,
}

If you have common functionality between multiple structs, make a trait for it.

pub trait ATrait {
    fn get_a(&self) -> i32;
}

Upvotes: 7

Related Questions