Как вывести тип переменной в Rust? (аналог type() из Python)
Использование std::any::type_name — стандартный способ
В Rust нет встроенного макроса type() как в Python, но стандартная библиотека предоставляет функцию std::any::type_name::. Она возвращает строку с именем типа во время компиляции. Это самый простой и рекомендуемый способ для отладки.
Вот как это работает для вашего примера:
fn main() {
let x = 42;
let y = "hello";
let z = vec![1, 2, 3];
println!("x: {}", std::any::type_name::());
println!("y: {}", std::any::type_name::());
println!("z: {}", std::any::type_name::());
}
Однако typeof — это нестабильная фича (на момент Rust 1.75 она доступна только в nightly). Стабильный способ — использовать аннотацию типа или просто передать переменную в функцию:
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>());
}
fn main() {
let x = 42;
let y = "hello";
let z = vec![1, 2, 3];
print_type_of(&x);
print_type_of(&y);
print_type_of(&z);
}
Вывод будет примерно таким:
i32
&str
alloc::vec::Vec<i32>
Важные нюансы
- Это compile-time информация:
type_nameвозвращает имя типа, известное на момент компиляции. Для generic-функций это будет конкретный тип, с которым функция была мономорфизирована. - Формат строки не гарантирован: В разных версиях компилятора и платформах строка может выглядеть по-разному (например,
i32vsint). Не пытайтесь парсить её — только для отладки. - Не путайте с трейтом
Display:type_nameне требует, чтобы тип реализовывалDisplayилиDebug. Это чисто мета-информация. - Для ссылок и заимствований: Если передаёте
&T, функция покажет&T, а неT. В примере выше мы передаём ссылку, поэтому видим&str, а неstr.
Альтернатива: крейт anyhow или кастомный макрос
Если вы часто используете эту возможность, можно создать макрос для удобства:
macro_rules! type_of {
($x:expr) => {
std::any::type_name::()
};
}
fn main() {
let x = 42;
println!("{}", type_of!(x)); // работает только в nightly
}
В стабильном Rust проще всего использовать функцию print_type_of, как показано выше. Её можно вынести в отдельный модуль или крейт-утилиту.
Что делать для сложных случаев?
Если вы работаете с трейт-объектами (dyn Trait), type_name покажет только dyn Trait, а не конкретный тип под капотом. Для этого потребуется Any трейт и downcasting:
use std::any::Any;
fn print_concrete_type(value: &dyn Any) {
if let Some(s) = value.downcast_ref::<String>() {
println!("It's a String: {}", s);
} else if let Some(i) = value.downcast_ref::<i32>() {
println!("It's an i32: {}", i);
} else {
println!("Unknown type: {}", value.type_id());
}
}
Но это уже продвинутая техника, которая требует, чтобы тип реализовывал Any (что верно для всех типов, у которых нет некопируемых ссылок).
Использование трейта Debug и форматного вывода
Если вы просто хотите увидеть, что внутри переменной, а не её тип как строку, можно использовать трейт Debug с форматным спецификатором {:?}. Это не покажет имя типа, но даст представление о структуре данных:
fn main() {
let x = 42;
let y = "hello";
let z = vec![1, 2, 3];
println!("x: {:?} (type: i32)", x);
println!("y: {:?} (type: &str)", y);
println!("z: {:?} (type: Vec<i32>)", z);
}
Этот подход часто используется в сочетании с макросом dbg!, который выводит и выражение, и его значение, и место в коде:
fn main() {
let x = 42;
dbg!(x); // выведет: [src/main.rs:3] x = 42
let result = some_function();
dbg!(result); // покажет и значение, и тип (в формате Debug)
}
dbg! — это отличный инструмент для быстрой отладки, хотя он не показывает имя типа в явном виде. Если вам критично именно имя типа, используйте std::any::type_name из первого ответа.
Также помните: в Rust компилятор сам контролирует типы, и на этапе компиляции вы всегда можете навести курсор на переменную в IDE (VS Code с rust-analyzer, IntelliJ Rust), чтобы увидеть её тип. Это часто быстрее, чем писать код для вывода типа.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги