Testing the inferred type of a variable

In documentation, it is useful to show and check the inferred type of a variable.

  • For TypeScript, there are tools that provide this functionality – e.g. eslint-plugin-expect-type:

    9001;
    // ^? number
    
    // $ExpectType number
    9001;
    
  • Is there anything similar for Rust? If not, would it be complicated to implement (e.g.) via rust-analyzer?

Conceivable workaround: produce an error and extract the type information from stderr (after = note):

error[E0308]: mismatched types
  --> src/main.rs:18:9
   |
18 |     let () = HashMap::<String, &str>::new();
   |         ^^   ------------------------------ this expression has type `HashMap<String, &str>`
   |         |
   |         expected struct `HashMap`, found `()`
   |
   = note: expected struct `HashMap<String, &str>`
           found unit type `()`

I'm not sure that it would be useful or wise to write documentation examples which assert the inferred type, rather than simply declaring an ordinary explicit type and letting compilation fail if it doesn't match. (Remember that unlike TypeScript, Rust has no routinely usable “anything” type and no significant subtyping.)

But supposing you really want to assert on the result of type inference, here's a way I thought of to do it:

use std::any::{type_name, TypeId};
use std::fmt::Debug;

#[track_caller]
fn assert_type<T: 'static>(value: impl Debug + 'static) {
    inner_assert::<T, _>(value);
    // inner function is used to hide the type parameter U, which
    // we want to take from the value only and not declare
    #[track_caller]
    fn inner_assert<T: 'static, U: 'static + Debug>(value: U) {
        if TypeId::of::<T>() != TypeId::of::<U>() {
            panic!(
                "expected {value:?} to be of type {t}, not {u}",
                t = type_name::<T>(),
                u = type_name::<U>()
            )
        }
    }
}

#[test]
fn example_1() {
    assert_type::<i32>(100);
}

#[test]
fn example_2() {
    assert_type::<std::slice::Iter<i32>>((&[1, 2, 3]).into_iter());
}

#[test]
fn example_failing() {
    // Vec::extend does not return a result.
    assert_type::<Vec<i32>>(vec![1, 2, 3].extend([4]));
}

Note that due to the use of TypeId, this will not work with non-'static types such as short-lived references. One could instead compare the type names only, but that is imprecise because type_name() does not guarantee uniqueness or any other properties.

2 Likes

There are some hints in Testing that code does not compile

1 Like

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.