You can use the std::any::type_name
function. This doesn't need a nightly compiler or an external crate, and the results are quite correct:
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
fn main() {
let s = "Hello";
let i = 42;
print_type_of(&s); // &str
print_type_of(&i); // i32
print_type_of(&main); // playground::main
print_type_of(&print_type_of::<i32>); // playground::print_type_of<i32>
print_type_of(&{ || "Hi!" }); // playground::main::{{closure}}
}
Be warned: as said in the documentation, this information must be used for a debug purpose only:
This is intended for diagnostic use. The exact contents and format of the string are not specified, other than being a best-effort description of the type.
If you want your type representation to stay the same between compiler versions, you should use a trait, like in the phicr's answer.
If you merely wish to find out the type of a variable and are willing to do it at compile time, you can cause an error and get the compiler to pick it up.
For example, set the variable to a type which doesn't work:
let mut my_number: () = 32.90;
// let () = x; would work too
error[E0308]: mismatched types
--> src/main.rs:2:29
|
2 | let mut my_number: () = 32.90;
| ^^^^^ expected (), found floating-point number
|
= note: expected type `()`
found type `{float}`
let mut my_number = 32.90;
my_number.what_is_this();
error[E0599]: no method named `what_is_this` found for type `{float}` in the current scope
--> src/main.rs:3:15
|
3 | my_number.what_is_this();
| ^^^^^^^^^^^^
let mut my_number = 32.90;
my_number.what_is_this
error[E0610]: `{float}` is a primitive type and therefore doesn't have fields
--> src/main.rs:3:15
|
3 | my_number.what_is_this
| ^^^^^^^^^^^^
These reveal the type, which in this case is actually not fully resolved. It’s called “floating-point variable” in the first example, and “{float}
” in all three examples; this is a partially resolved type which could end up f32
or f64
, depending on how you use it. “{float}
” is not a legal type name, it’s a placeholder meaning “I’m not completely sure what this is”, but it is a floating-point number. In the case of floating-point variables, if you don't constrain it, it will default to f64
¹. (An unqualified integer literal will default to i32
.)
See also:
What is the {integer} or {float} in a compiler error message?
¹ There may still be ways of baffling the compiler so that it can’t decide between f32
and f64
; I’m not sure. It used to be as simple as 32.90.eq(&32.90)
, but that treats both as f64
now and chugs along happily, so I don’t know.
:?
has for quite a long time now been manually implemented. But more importantly, the std::fmt::Debug
implementation (for that is what :?
uses) for number types no longer includes a suffix to indicate which type it is of.
ImageBuffer<_, Vec<_>>
which doesn't help me very much when I'm trying to write a function that takes one of these things as a parameter. And this happens in code that otherwise compiles until I add the :()
. Is there no better way?
There is an unstable function std::intrinsics::type_name
that can get you the name of a type, though you have to use a nightly build of Rust (this is unlikely to ever work in stable Rust). Here’s an example:
#![feature(core_intrinsics)]
fn print_type_of<T>(_: &T) {
println!("{}", unsafe { std::intrinsics::type_name::<T>() });
}
fn main() {
print_type_of(&32.90); // prints "f64"
print_type_of(&vec![1, 2, 4]); // prints "std::vec::Vec<i32>"
print_type_of(&"foo"); // prints "&str"
}
#![feature(core_intrinsics)]
print_type_of
is taking references (&T
), not values (T
), so you must pass &&str
rather than &str
; that is, print_type_of(&"foo")
rather than print_type_of("foo")
.
std::any::type_name
is stable since rust 1.38: stackoverflow.com/a/58119924
If you know all the types beforehand, you can use traits to add a type_of
method:
trait TypeInfo {
fn type_of(&self) -> &'static str;
}
impl TypeInfo for i32 {
fn type_of(&self) -> &'static str {
"i32"
}
}
impl TypeInfo for i64 {
fn type_of(&self) -> &'static str {
"i64"
}
}
//...
No intrisics or nothin', so although more limited this is the only solution here that gets you a string and is stable. (see Boiethios's answer) However, it's very laborious and doesn't account for type parameters, so we could...
trait TypeInfo {
fn type_name() -> String;
fn type_of(&self) -> String;
}
macro_rules! impl_type_info {
($($name:ident$(<$($T:ident),+>)*),*) => {
$(impl_type_info_single!($name$(<$($T),*>)*);)*
};
}
macro_rules! mut_if {
($name:ident = $value:expr, $($any:expr)+) => (let mut $name = $value;);
($name:ident = $value:expr,) => (let $name = $value;);
}
macro_rules! impl_type_info_single {
($name:ident$(<$($T:ident),+>)*) => {
impl$(<$($T: TypeInfo),*>)* TypeInfo for $name$(<$($T),*>)* {
fn type_name() -> String {
mut_if!(res = String::from(stringify!($name)), $($($T)*)*);
$(
res.push('<');
$(
res.push_str(&$T::type_name());
res.push(',');
)*
res.pop();
res.push('>');
)*
res
}
fn type_of(&self) -> String {
$name$(::<$($T),*>)*::type_name()
}
}
}
}
impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a T {
fn type_name() -> String {
let mut res = String::from("&");
res.push_str(&T::type_name());
res
}
fn type_of(&self) -> String {
<&T>::type_name()
}
}
impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a mut T {
fn type_name() -> String {
let mut res = String::from("&mut ");
res.push_str(&T::type_name());
res
}
fn type_of(&self) -> String {
<&mut T>::type_name()
}
}
macro_rules! type_of {
($x:expr) => { (&$x).type_of() };
}
Let's use it:
impl_type_info!(i32, i64, f32, f64, str, String, Vec<T>, Result<T,S>)
fn main() {
println!("{}", type_of!(1));
println!("{}", type_of!(&1));
println!("{}", type_of!(&&1));
println!("{}", type_of!(&mut 1));
println!("{}", type_of!(&&mut 1));
println!("{}", type_of!(&mut &1));
println!("{}", type_of!(1.0));
println!("{}", type_of!("abc"));
println!("{}", type_of!(&"abc"));
println!("{}", type_of!(String::from("abc")));
println!("{}", type_of!(vec![1,2,3]));
println!("{}", <Result<String,i64>>::type_name());
println!("{}", <&i32>::type_name());
println!("{}", <&str>::type_name());
}
output:
i32
&i32
&&i32
&mut i32
&&mut i32
&mut &i32
f64
&str
&&str
String
Vec<i32>
Result<String,i64>
&i32
&str
UPD The following does not work anymore. Check Shubham's answer for correction.
Check out std::intrinsics::get_tydesc<T>()
. It is in "experimental" state right now, but it's OK if you are just hacking around the type system.
Check out the following example:
fn print_type_of<T>(_: &T) -> () {
let type_name =
unsafe {
(*std::intrinsics::get_tydesc::<T>()).name
};
println!("{}", type_name);
}
fn main() -> () {
let mut my_number = 32.90;
print_type_of(&my_number); // prints "f64"
print_type_of(&(vec!(1, 2, 4))); // prints "collections::vec::Vec<int>"
}
This is what is used internally to implement the famous {:?}
formatter.
Update, original answer below
How about trait function type_name
, which is useful to get type name quickly.
pub trait AnyExt {
fn type_name(&self) -> &'static str;
}
impl<T> AnyExt for T {
fn type_name(&self) -> &'static str {
std::any::type_name::<T>()
}
}
fn main(){
let my_number = 32.90;
println!("{}",my_number.type_name());
}
Output:
f64
Original answer
I write a macro type_of!()
to debug, which is original from std dbg!()
.
pub fn type_of2<T>(v: T) -> (&'static str, T) {
(std::any::type_name::<T>(), v)
}
#[macro_export]
macro_rules! type_of {
// NOTE: We cannot use `concat!` to make a static string as a format argument
// of `eprintln!` because `file!` could contain a `{` or
// `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
// will be malformed.
() => {
eprintln!("[{}:{}]", file!(), line!());
};
($val:expr $(,)?) => {
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => {
let (type_,tmp) = $crate::type_of2(tmp);
eprintln!("[{}:{}] {}: {}",
file!(), line!(), stringify!($val), type_);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::type_of!($val)),+,)
};
}
fn main(){
let my_number = type_of!(32.90);
type_of!(my_number);
}
Output:
[src/main.rs:32] 32.90: f64
[src/main.rs:33] my_number: f64
** UPDATE ** This has not been verified to work any time recently.
I put together a little crate to do this based off vbo's answer. It gives you a macro to return or print out the type.
Put this in your Cargo.toml file:
[dependencies]
t_bang = "0.1.2"
Then you can use it like so:
#[macro_use] extern crate t_bang;
use t_bang::*;
fn main() {
let x = 5;
let x_type = t!(x);
println!("{:?}", x_type); // prints out: "i32"
pt!(x); // prints out: "i32"
pt!(5); // prints out: "i32"
}
#![feature]
may not be used on the stable release channel `
You can also use the simple approach of using the variable in println!("{:?}", var)
. If Debug
is not implemented for the type, you can see the type in the compiler's error message:
mod some {
pub struct SomeType;
}
fn main() {
let unknown_var = some::SomeType;
println!("{:?}", unknown_var);
}
(playpen)
It's dirty but it works.
Debug
is not implemented — this is a pretty unlikely case though. One of the first things you should do for most any struct is add #[derive(Debug)]
. I think the times where you don't want Debug
are very small.
println!("{:?}", unknown_var);
?? Is it a string interpolation but why the :?
inside the curly brackets? @DenisKolodin
Debug
because it isn't implemented, but you can use {}
as well.
There's a @ChrisMorgan answer to get approximate type ("float") in stable rust and there's a @ShubhamJain answer to get precise type ("f64") through unstable function in nightly rust.
Now here's a way one can get precise type (ie decide between f32 and f64) in stable rust:
fn main() {
let a = 5.;
let _: () = unsafe { std::mem::transmute(a) };
}
results in
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> main.rs:3:27
|
3 | let _: () = unsafe { std::mem::transmute(a) };
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `f64` (64 bits)
= note: target type: `()` (0 bits)
Update
The turbofish variation
fn main() {
let a = 5.;
unsafe { std::mem::transmute::<_, ()>(a) }
}
is slightly shorter but somewhat less readable.
float
, telling between f32
and f64
can be accomplished with std::mem::size_of_val(&a)
Some other answers don't work, but I find that the typename crate works.
Create a new project: cargo new test_typename
Modify the Cargo.toml [dependencies]
typename = "0.1.1"
Modify your source code use typename::TypeName;
fn main() {
assert_eq!(String::type_name(), "std::string::String");
assert_eq!(Vec::
The output is:
type of a 65u8 65 is u8
type of b b'A' 65 is u8
type of c 65 65 is i32
type of d 65i8 65 is i8
type of e 65i32 65 is i32
type of f 65u32 65 is u32
type of arr [1, 2, 3, 4, 5] is [i32; 5]
type of first 1 is i32
typename
does not work with variables without explicit type in the declaration. Running it with my_number
from the question gives the following error "can't call method type_name_of
on ambiguous numeric type {float}
. help: you must specify a type for this binding, like f32
"
0.65
and it works well: type of c 0.65 0.65 is f64
. here is my version: rustc 1.38.0-nightly (69656fa4c 2019-07-13)
If your just wanting to know the type of your variable during interactive development, I would highly recommend using rls (rust language server) inside of your editor or ide. You can then simply permanently enable or toggle the hover ability and just put your cursor over the variable. A little dialog should come up with information about the variable including the type.
This is simplified version of @Boiethios answer. I have removed some '&' symbols from original solution.
fn print_type_of<T>(_: T) {
println!("{}", std::any::type_name::<T>())
}
fn main() {
let s = "Hello";
let i = 42;
print_type_of(s); // &str
print_type_of(i); // i32
print_type_of(main); // playground::main
print_type_of(print_type_of::<i32>); // playground::print_type_of<i32>
print_type_of(|| "Hi!" ); // playground::main::{{closure}}
}
&
s. But sure, this makes even less of a difference when the type implements Copy
(such as in &str
, i32
, and function pointers).
Macro form permits an usage "everywhere" while the function need an object to be parse.
Macro form (one liner):
macro_rules! ty {($type:ty) => {std::any::type_name::<$type>()}}
Macro form formated:
macro_rules! ty {
($type:ty) => {
std::any::type_name::<$type>()
};
}
Function form (borrowing is to not destroy the parsed var):
fn type_of<T>(_: &T) -> &'static str {std::any::type_name::<T>()}
fn type_of<T>(_: &T) -> &'static str {
std::any::type_name::<T>()
}
Example:
macro_rules! ty {($type:ty) => {std::any::type_name::<$type>()}}
fn type_of<T>(_: &T) -> &'static str {std::any::type_name::<T>()}
struct DontMater<T>(T);
impl<T: std::fmt::Debug> std::fmt::Debug for DontMater<T> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.write_fmt(format_args!("DontMater<{}>({:?})", ty!(T), self.0))
}
}
fn main() {
type µ = [Vec<String>; 7];
println!("{:?}", DontMater(5_usize));
println!("{:?}", DontMater("¤"));
println!("{}", ty!(char));
println!("{:?}", ty!(µ));
println!("{}", type_of(&DontMater(72_i8)));
println!("{:?}", type_of(&15_f64));
}
Returns:
DontMater<usize>(5)
DontMater<&str>("¤")
char
"[alloc::vec::Vec<alloc::string::String>; 7]"
env_vars::DontMater<i8>
"f64"
Success story sharing