Как напечатать тип переменной в Rust?
У меня есть следующее:
let mut my_number = 32.90;
Как мне распечатать тип my_number
?
С помощью type
а также type_of
не работал. Есть ли другой способ, которым я могу напечатать тип номера?
19 ответов
Вы можете использовать std::any::type_name
функция. Для этого не нужен ночной компилятор или внешний ящик, и результаты вполне правильные:
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}}
}
Будьте осторожны: как сказано в документации, эта информация должна использоваться только в целях отладки:
Это предназначено для диагностического использования. Точное содержимое и формат строки не указаны, за исключением описания типа максимально возможного.
Если вы хотите, чтобы ваше представление типа оставалось неизменным между версиями компилятора, вы должны использовать трейт, как в ответе phicr.
Если вы просто хотите выяснить тип переменной и хотите сделать это во время компиляции, вы можете вызвать ошибку и заставить компилятор ее забрать.
Например, установите для переменной тип, который не работает (let () = x;
будет работать тоже)
error[E0308]: mismatched types
--> <anon>:2:29
|
2 | let mut my_number: () = 32.90;
| ^^^^^ expected (), found floating-point variable
|
= note: expected type `()`
= note: found type `{float}`
error: aborting due to previous error
Или в большинстве случаев вызовите неверный метод или получите неверное поле:
error: no method named `what_is_this` found for type `{float}` in the current scope
--> <anon>:3:15
|
3 | my_number.what_is_this();
| ^^^^^^^^^^^^
error: aborting due to previous error
error: attempted access of field `what_is_this` on type `{float}`, but no field with that name was found
--> <anon>:3:5
|
3 | my_number.what_is_this
| ^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
Они показывают тип, который в этом случае фактически не полностью решен. Это называется "переменная с плавающей точкой" в первом примере, и " {float}
Во всех трех примерах; это частично разрешенный тип, который может закончиться f32
или же f64
в зависимости от того, как вы его используете. " {float}
"Не является допустимым именем типа, это заполнитель, означающий" я не совсем уверен, что это такое ", но это число с плавающей запятой. В случае переменных с плавающей запятой, если вы не ограничите их, по умолчанию f64
¹. (Безусловный целочисленный литерал по умолчанию будет i32
.)
Still Там могут быть способы сбить с толку компилятор, чтобы он не мог решить между f32
а также f64
; Я не уверен. Раньше было так просто, как 32.90.eq(&32.90)
, но это относится как к f64
сейчас и счастливо пыхтит, так что я не знаю.
Есть нестабильная функция std::intrinsics::type_name
это может дать вам имя типа, хотя вы должны использовать ночную сборку Rust (это вряд ли когда-либо будет работать в стабильном Rust). Вот пример:
#![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"
}
Если вы знаете все типы заранее, вы можете использовать черты, чтобы добавить type_of
метод:
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"
}
}
//...
Никаких интрижек или ничего, поэтому, хотя и более ограниченный, это единственное решение, которое дает вам строку и является стабильным. Однако это очень трудоемко и не учитывает параметры типа, поэтому мы могли бы...
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() };
}
Давайте использовать это:
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());
}
выход:
i32
&i32
&&i32
&mut i32
&&mut i32
&mut &i32
f64
&str
&&str
String
Vec<i32>
Result<String,i64>
&i32
&str
Я пишу макрос
type_of!()
для отладки, которая является оригинальной из 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);
}
Выход:
[src/main.rs:32] 32.90: f64
[src/main.rs:33] my_number: f64
Обновлять
Как насчет функции черты характера
type_name
, что полезно для быстрого получения имени типа.
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());
}
Выход:
f64
UPD Следующее больше не работает. Проверьте ответ Shubham для исправления.
Проверять, выписываться std::intrinsics::get_tydesc<T>()
, Сейчас он находится в "экспериментальном" состоянии, но это нормально, если вы просто взламываете систему типов.
Посмотрите на следующий пример:
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>"
}
Это то, что используется внутри для реализации известного {:?}
форматировщик.
Я собрал небольшой ящик, чтобы сделать это, основываясь на ответе VBO. Это дает вам макрос для возврата или распечатки типа.
Поместите это в ваш файл Cargo.toml:
[dependencies]
t_bang = "0.1.2"
Тогда вы можете использовать это так:
#[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"
}
Недавно добавленный в версии 1.38 std::any::type_name
use std::any::type_name;
fn type_of<T>(_: T) -> &'static str {
type_name::<T>()
}
fn main() {
let x = 21;
let y = 2.5;
println!("{}", type_of(&y));
println!("{}", type_of(x));
}
Вы также можете использовать простой подход использования переменной в println!("{:?}", var)
, Если Debug
не реализован для типа, вы можете увидеть тип в сообщении об ошибке компилятора:
mod some {
pub struct SomeType;
}
fn main() {
let unknown_var = some::SomeType;
println!("{:?}", unknown_var);
}
Это грязно, но это работает.
Есть ответ @ChrisMorgan, чтобы получить приблизительный тип ("float") в стабильной ржавчине, и есть ответ @ShubhamJain, чтобы получить точный тип ("f64") через нестабильную функцию в ночной ржавчине.
Теперь вот способ получить точный тип (т.е. выбрать между f32 и f64) в стабильной ржавчине:
fn main() {
let a = 5.;
let _: () = unsafe { std::mem::transmute(a) };
}
приводит к
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)
Обновить
Вариант турбофиша
fn main() {
let a = 5.;
unsafe { std::mem::transmute::<_, ()>(a) }
}
немного короче, но менее читаем.
Если вы просто хотите узнать тип вашей переменной во время интерактивной разработки, я настоятельно рекомендую использовать rls (сервер языка ржавчины) внутри вашего редактора или ide. Затем вы можете просто навсегда включить или переключить возможность наведения и просто навести курсор на переменную. В небольшом диалоговом окне должна появиться информация о переменной, включая тип.
Лучше использовать это:
fn print_type_of<T>(_: &T) -> String {
format!("{}", std::any::type_name::<T>())
}
fn main() {
let s = &"hello world".to_string();
let cloned_s = s.clone();
println!("{:?}", print_type_of(&s));
println!("{:?}", print_type_of(&cloned_s));
}
Взял вывод из /questions/41840093/kak-napechatat-tip-peremennoj-v-rust/41840121#41840121
Некоторые другие ответы не работают, но я считаю, что ящик с именем типа работает.
Создайте новый проект:
cargo new test_typename
Измените Cargo.toml
[dependencies] typename = "0.1.1"
Измените исходный код
use typename::TypeName; fn main() { assert_eq!(String::type_name(), "std::string::String"); assert_eq!(Vec::<i32>::type_name(), "std::vec::Vec<i32>"); assert_eq!([0, 1, 2].type_name_of(), "[i32; 3]"); let a = 65u8; let b = b'A'; let c = 65; let d = 65i8; let e = 65i32; let f = 65u32; let arr = [1,2,3,4,5]; let first = arr[0]; println!("type of a 65u8 {} is {}", a, a.type_name_of()); println!("type of b b'A' {} is {}", b, b.type_name_of()); println!("type of c 65 {} is {}", c, c.type_name_of()); println!("type of d 65i8 {} is {}", d, d.type_name_of()); println!("type of e 65i32 {} is {}", e, e.type_name_of()); println!("type of f 65u32 {} is {}", f, f.type_name_of()); println!("type of arr {:?} is {}", arr, arr.type_name_of()); println!("type of first {} is {}", first, first.type_name_of()); }
Результат:
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
Это упрощенная версия ответа @Boiethios . Я удалил некоторые символы «&» из исходного решения.
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}}
}
#![feature(type_name_of_val)]
use std::any::type_name_of_val;
fn main() {
let mut my_number = 32.90;
println!("{}", type_name_of_val(&my_number));
}
короткий рассказ;
fn tyof<T>(_: &T) -> String {
std::any::type_name::<T>().into()
}
длинная история;
trait Type {
fn type_of(&self) -> String;
}
macro_rules! Type {
($($ty:ty),*) => {
$(
impl Type for $ty {
fn type_of(&self) -> String {
stringify!($ty).into()
}
}
)*
}
}
#[rustfmt::skip]
Type!(
u8, i8, u16, i16, u32, i32, i64, u64, i128, String, [()], (), Vec<()>, &u8, &i8, &u16, &i16, &u32, &i32, &i64, &u64, &i128, &str, &[()], &Vec<()>, &()
// add any struct, enum or type you want
);
macro_rules! tyof {
($var: expr) => {{
$var.type_of()
}};
}
fn main() {
let x = "Hello world!";
println!("{}", tyof!(x));
// or
println!("{}", x.type_of());
let x = 5;
println!("{}", tyof!(x));
// or
println!("{}", x.type_of());
}
Мне очень нравится предыдущий ответ @Coautose, но на случай, если кому-то нужно, например, просто имя типа без пространства именC
вместоa::b::C
, вот модифицированная версия макроса, которая работает как положено:
macro_rules! ty {
($type:ty) => {{
let result = std::any::type_name::<$type>();
match result.rsplit_once(':') {
Some((_, s)) => s,
None => result,
}
}};
}
Использование:
debug!("Testing type name: {}", ty!(A));
Форма макроса допускает использование «везде», в то время как функции требуется объект для разбора.
Форма макроса (один вкладыш):
macro_rules! ty {($type:ty) => {std::any::type_name::<$type>()}}
Форма макроса сформирована:
macro_rules! ty {
($type:ty) => {
std::any::type_name::<$type>()
};
}
Форма функции (заимствование не разрушает разобранный 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>()
}
Пример:
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));
}
Возвращает:
DontMater<usize>(5)
DontMater<&str>("¤")
char
"[alloc::vec::Vec<alloc::string::String>; 7]"
env_vars::DontMater<i8>
"f64"
Вы можете использовать std::any::type_name
. Ниже приведены примеры примитивных типов данных, которые можно использовать без&
.
use std::any::type_name;
fn type_of<T>(_: T) -> &'static str {
type_name::<T>()
}
fn main() {
let str1 = "Rust language";
let str2 = str1;
println!("str1 is: {}, and the type is {}.", str1, type_of(str1));
println!("str2 is: {}, and the type is {}.", str2, type_of(str2));
let bool1 = true;
let bool2 = bool1;
println!("bool1 is {}, and the type is {}.", bool1, type_of(bool1));
println!("bool2 is {}, and the type is {}.", bool2, type_of(bool2));
let x1 = 5;
let x2 = x1;
println!("x1 is {}, and the type is {}.", x1, type_of(x1));
println!("x2 is {}, and the type is {}.", x2, type_of(x2));
let a1 = 'a';
let a2 = a1;
println!("a1 is {}, and the type is {}.", a1, type_of(a1));
println!("a2 is {}, and the type is {}.", a2, type_of(a2));
let tup1= ("hello", 5, 'c');
let tup2 = tup1;
println!("tup1 is {:?}, and the type is {}.", tup1, type_of(tup1));
println!("tup2 is {:?}, and the type is {}.", tup2, type_of(tup2));
let array1: [i32; 3] = [0; 3];
let array2 = array1;
println!("array1 is {:?}, and the type is {}.", array1, type_of(array1));
println!("array2 is {:?}, and the type is {}.", array2, type_of(array2));
let array: [i32; 5] = [0, 1, 2, 3, 4];
let slice1 = &array[0..3];
let slice2 = slice1;
println!("slice1 is {:?}, and the type is {}.", slice1, type_of(slice1));
println!("slice2 is {:?}, and the type is {}.", slice2, type_of(slice2));
}
На выходе
str1 is: Rust language, and the type is &str.
str2 is: Rust language, and the type is &str.
bool1 is true, and the type is bool.
bool2 is true, and the type is bool.
x1 is 5, and the type is i32.
x2 is 5, and the type is i32.
a1 is a, and the type is char.
a2 is a, and the type is char.
tup1 is ("hello", 5, 'c'), and the type is (&str, i32, char).
tup2 is ("hello", 5, 'c'), and the type is (&str, i32, char).
array1 is [0, 0, 0], and the type is [i32; 3].
array2 is [0, 0, 0], and the type is [i32; 3].
slice1 is [0, 1, 2], and the type is &[i32].
slice2 is [0, 1, 2], and the type is &[i32].