How to call a static method on a statically typed variable?

Consider the following code:

use std::marker::PhantomData;

struct MyStruct<T> {
    _p: PhantomData<T>
}

impl<T> MyStruct<T> {
    pub const fn new() -> Self {
        Self { _p: PhantomData }
    }

    pub const fn type_check<B>() -> bool {
        do_something_with_T_and_B::<T, B>()
    }
}

fn main() {
    let value = MyStruct::<i32>::new();
    const _: bool = unimplemented!(); // I want to use value::T to run `type_check`
}

The actual variable types are too complex and fragmented, and not all of them are publicly available, so I can't use turbofish to specify the type. Since value's type is known at compile time, is it possible to use the T(i32) to run type_check<B>?

Yes. You can create a generic function that takes a value, and type inference will give you the right T in there:

const fn infers_the_type<B, T>(_value: &MyStruct<T>) -> bool {
   MyStruct::<T>::type_check::<B>()
}

However, it won't work in const contexts with run-time values. It will work in const contexts with compile-time values.

3 Likes

Yes, I've tried this. But my codebase requires this to be evaluated at compile-time for any type.

I managed to come up with this, but it requires the nightly compiler with #![feature(type_alias_impl_trait)]:

trait TypeCheck<B> {
    const OUT: bool;
}

impl<T,B> TypeCheck<B> for MyStruct<T> {
    const OUT: bool = Self::type_check::<B>();
}

fn main() {
    let value = MyStruct::<i32>::new();
    
    type T = impl TypeCheck<()>;
    let _:&T = &value;

    const _: bool = T::OUT;
}
3 Likes

Use the tait and let the compiler to evaluate the actual type? A nice work around, but ... I thinks things could be easier, at least theoretically.

As far as I can work out, TAIT is currently the only way for a non-const value to influence anything inside a const context. Maybe I'm missing something, though.

Looks like you want typeof, basically.

1 Like

Wildcard bindings are no-ops, so this works less disruptively:

    type T = impl TypeCheck<()>;
    let _: T = value;

By less disruptively, I mean this still compiles, for example:

    let mut value = MyStruct::<i32>::new();
    let mr = &mut value; // -----------+
    //                                 | Exclusive borrow
    type T = impl TypeCheck<()>; //    | is not "disturbed"
    let _: T = value;  //              |
    //                                 |
    const _: bool = T::OUT;            |
    //                                 |
    mr; // ----------------------------+

(Just pondering how one could macro this...)

1 Like

I think this functionality could theoretically be implemented internally by the compiler, e.g. by implementing a type_of!(foobar), but there's no hope of that being stable in the short term (though I guess it should be stable sooner than tait if someone wants to implement it). Sadly I'm not familiar with rustc.

Yeah, compiler/language is the place for it, but there's been resistance to it for reasons I can't recall off the top of my head.

Supporting full expressions (versus variables) might be somewhat problematic as the same expression at different code points can be inferred to be different types (e.g. due to lifetimes, and due to annotation context).

Maybe it's just capturing an identifier, because usually we can always bind something to a variable. The resistance I've read about before is mostly about being a keyword operator or function, but macros can circumvent these issues somewhat.