Làm cách nào để in loại biến trong Rust?


237

Tôi có những điều sau đây:

let mut my_number = 32.90;

Làm thế nào để tôi in các loại my_number?

Sử dụng typetype_ofkhông hoạt động. Có cách nào khác để tôi có thể in loại số không?

Câu trả lời:


177

Nếu bạn chỉ muốn tìm ra loại biến và sẵn sàng thực hiện nó trong thời gian biên dịch, bạn có thể gây ra lỗi và yêu cầu trình biên dịch chọn nó.

Ví dụ: đặt biến thành loại không hoạt động :

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}`

Hoặc gọi một phương thức không hợp lệ :

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();
  |               ^^^^^^^^^^^^

Hoặc truy cập vào một trường không hợp lệ :

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
  |               ^^^^^^^^^^^^

Chúng tiết lộ loại, trong trường hợp này thực sự không được giải quyết đầy đủ. Nó được gọi là biến số dấu phẩy động nổi trong một ví dụ đầu tiên và {float}trong số ba ví dụ; đây là một loại được giải quyết một phần có thể kết thúc f32hoặc f64, tùy thuộc vào cách bạn sử dụng nó. “ {float}” Không phải là tên loại quy phạm pháp luật, đó là một giữ chỗ có nghĩa là “Tôi không hoàn toàn chắc chắn đây là những gì”, nhưng nó một số dấu chấm động. Trong trường hợp biến số dấu phẩy động, nếu bạn không giới hạn nó, nó sẽ mặc định là f64. (Một số nguyên không đủ tiêu chuẩn sẽ được mặc định là i32.)

Xem thêm:


Vẫn có thể có những cách gây trở ngại cho trình biên dịch để nó không thể quyết định giữa f32f64; Tôi không chắc. Nó đã từng đơn giản như 32.90.eq(&32.90), nhưng nó đối xử với cả hai như f64bây giờ và vui vẻ, vì vậy tôi không biết.


4
:?đã khá lâu nay mới được thực hiện thủ công. Nhưng quan trọng hơn, việc std::fmt::Debugtriển khai (cho đó là những gì :?sử dụng) cho các loại số không còn bao gồm một hậu tố để chỉ ra nó thuộc loại nào.
Chris Morgan

2
Tôi sử dụng các kỹ thuật này rất nhiều để cố gắng tìm loại biểu thức, nhưng nó không phải lúc nào cũng hoạt động, đặc biệt là khi có các tham số loại liên quan. Ví dụ, trình biên dịch sẽ cho tôi biết rằng nó đang mong đợi một ImageBuffer<_, Vec<_>>điều không giúp ích cho tôi rất nhiều khi tôi đang cố gắng viết một hàm lấy một trong những điều này làm tham số. Và điều này xảy ra trong mã mà nếu không thì biên dịch cho đến khi tôi thêm :(). Có cách nào tốt hơn không?
Christopher Armstrong

2
Điều này có vẻ là một chút phức tạp và không trực quan. Sẽ rất khó cho trình soạn thảo mã, ví dụ Emacs cung cấp kiểu khi con trỏ nằm trên biến, giống như trong nhiều ngôn ngữ khác? Nếu trình biên dịch có thể báo loại khi có lỗi, chắc chắn nó cũng đã biết loại khi không có lỗi?
xji

1
@JIXiang: Máy chủ ngôn ngữ Rust là tất cả về việc cung cấp thông tin này cho IDE, nhưng nó chưa hoàn thiện nhưng bản phát hành alpha đầu tiên của nó chỉ mới vài ngày trước. Vâng, đây là một cách tiếp cận eldritch; vâng, những cách ít bí truyền hơn để đạt được mục tiêu đang dần đến.
Chris Morgan

1
Điều này nghe có vẻ rất giống một hack. Đây thực sự là cách thành ngữ để kiểm tra loại biến?
nhầm

109

Có một chức năng không ổn định std::intrinsics::type_namecó thể giúp bạn biết tên của một loại, mặc dù bạn phải sử dụng bản dựng Rust hàng đêm (điều này khó có thể hoạt động trong Rust ổn định). Đây là một ví dụ:

#![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"
}

@vbo: không phải đến khi nó ổn định. Một cái gì đó như thế này khó có thể được ổn định trong một thời gian dài, nếu có bao giờ, và nó sẽ không làm tôi ngạc nhiên nếu nó không bao giờ ổn định; nó không phải là thứ mà bạn nên làm
Chris Morgan

2
Trên rỉ sét hàng đêm (1.3), nó chỉ hoạt động khi thay đổi dòng đầu tiên thành#![feature(core_intrinsics)]
AT

1
@DmitriNesteruk: print_type_ofđang lấy tham chiếu ( &T), không phải giá trị ( T), vì vậy bạn phải vượt qua &&strchứ không phải &str; đó là, print_type_of(&"foo")hơn là print_type_of("foo").
Chris Morgan

Bạn đã đúng, 3 năm trôi qua và nó vẫn chưa ổn định.
Anton Kochkov

5
std::any::type_nameổn định kể từ khi rỉ sét 1.38: stackoverflow.com/a/58119924
Tim Robinson

66

Bạn có thể sử dụng std::any::type_namechức năng. Điều này không cần trình biên dịch hàng đêm hoặc thùng bên ngoài và kết quả hoàn toàn chính xác:

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}}
}

Được cảnh báo: như đã nói trong tài liệu, thông tin này phải được sử dụng cho mục đích gỡ lỗi:

Điều này là dành cho sử dụng chẩn đoán. Nội dung và định dạng chính xác của chuỗi không được chỉ định, ngoài việc là một mô tả nỗ lực tốt nhất của loại.

Nếu bạn muốn biểu diễn kiểu của mình giữ nguyên giữa các phiên bản trình biên dịch, bạn nên sử dụng một đặc điểm, như trong câu trả lời của phicr .


1
Câu trả lời tốt nhất cho tôi, vì hầu hết các nhà phát triển muốn sử dụng điều này cho mục đích gỡ lỗi, như in lỗi phân tích cú pháp
kaiser

Chính xác những gì tôi cần, tôi không biết tại sao đây không phải là câu trả lời được đánh dấu!
James Poulose

1
@JamesPoulose Vì chức năng này là gần đây nên câu trả lời của tôi mới hơn.
Boiethios

53

Nếu bạn biết tất cả các loại trước đó, bạn có thể sử dụng các đặc điểm để thêm một type_ofphương thức:

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"
    }
}

//...

Không có nội tâm hay không có gì cả, vì vậy mặc dù hạn chế hơn nhưng đây là giải pháp duy nhất ở đây giúp bạn có được một chuỗi và ổn định. (xem câu trả lời của Boiethios của Pháp ) Tuy nhiên, nó rất tốn công và không tính đến các tham số loại, vì vậy chúng tôi có thể ...

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() };
}

Hãy sử dụng nó:

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());
}

đầu ra:

i32
&i32
&&i32
&mut i32
&&mut i32
&mut &i32
f64
&str
&&str
String
Vec<i32>
Result<String,i64>
&i32
&str

Sân chơi Rust


Câu trả lời này có thể được chia thành hai câu trả lời riêng biệt để tránh trộn lẫn hai câu trả lời.
Prajwal Dhatwalia

2
@PrajwalDhatwalia Tôi đã suy nghĩ về những gì bạn nói và tôi cảm thấy như mình hài lòng với cách các phiên bản bổ sung cho nhau. Phiên bản đặc điểm cho thấy sự đơn giản hóa những gì phiên bản macro đang thực hiện dưới mui xe, làm cho mục tiêu của nó rõ ràng hơn. Mặt khác, phiên bản macro cho thấy cách làm cho phiên bản đặc điểm thường có thể sử dụng hơn; đó không phải là cách duy nhất để làm điều đó, nhưng thậm chí cho thấy rằng nó có thể là lợi thế. Tóm lại, đây có thể là hai câu trả lời nhưng tôi cảm thấy toàn bộ lớn hơn tổng số phần của nó.
phicr

19

CẬP NHẬT Sau đây không hoạt động nữa. Kiểm tra câu trả lời của Shubham để sửa chữa.

Kiểm tra std::intrinsics::get_tydesc<T>(). Nó đang ở trạng thái "thử nghiệm" ngay bây giờ, nhưng sẽ ổn nếu bạn chỉ hack xung quanh hệ thống loại.

Kiểm tra ví dụ sau:

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>"
}

Đây là những gì được sử dụng trong nội bộ để thực hiện các {:?}định dạng nổi tiếng .


15

** CẬP NHẬT ** Điều này đã không được xác minh để làm việc bất cứ lúc nào gần đây.

Tôi tập hợp một cái thùng nhỏ để làm điều này dựa trên câu trả lời của vbo. Nó cung cấp cho bạn một macro để trả về hoặc in ra loại.

Đặt cái này trong tập tin Cargo.toml của bạn:

[dependencies]
t_bang = "0.1.2"

Sau đó, bạn có thể sử dụng nó như vậy:

#[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"
}

@vbo nói giải pháp của anh ấy không còn hiệu quả nữa. Bạn có làm việc không?
Antony Hatchkins

không hoạt động `error [E0554]: #![feature]có thể không được sử dụng trên kênh phát hành ổn định`
Muhammed Moussa

7

Bạn cũng có thể sử dụng cách tiếp cận đơn giản của việc sử dụng biến trong println!("{:?}", var). Nếu Debugkhông được triển khai cho loại, bạn có thể thấy loại trong thông báo lỗi của trình biên dịch:

mod some {
    pub struct SomeType;
}

fn main() {
    let unknown_var = some::SomeType;
    println!("{:?}", unknown_var);
}

( chơi )

Nó bẩn nhưng nó hoạt động.


8
Nếu Debugkhông được thực hiện - đây là một trường hợp khá khó xảy ra. Một trong những điều đầu tiên bạn nên làm đối với hầu hết mọi cấu trúc là thêm #[derive(Debug)]. Tôi nghĩ rằng những lúc bạn không muốn Debuglà rất nhỏ.
Người quản lý

1
bạn có thể giải thích những gì đang xảy ra trong println!("{:?}", unknown_var);?? Có phải là một phép nội suy chuỗi nhưng tại sao :?bên trong dấu ngoặc nhọn? @DenisKolodin
Julio Marins

Tôi kích động lỗi. Ý tưởng để cho trình biên dịch cung cấp thông tin loại có lỗi. Tôi đã sử dụng Debugvì nó không được thực hiện, nhưng bạn cũng có thể sử dụng {}.
DenisKolodin

4

câu trả lời @ChrisMorgan để có được loại gần đúng ("float") trong rỉ sét ổn định và có câu trả lời @ShubhamJain để có được loại chính xác ("f64") thông qua chức năng không ổn định trong rỉ sét hàng đêm.

Bây giờ đây là một cách người ta có thể có được loại chính xác (nghĩa là quyết định giữa f32 và f64) trong tình trạng rỉ sét ổn định:

fn main() {
    let a = 5.;
    let _: () = unsafe { std::mem::transmute(a) };
}

kết quả trong

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)

Cập nhật

Sự biến đổi của tuabin

fn main() {
    let a = 5.;
    unsafe { std::mem::transmute::<_, ()>(a) }
}

ngắn hơn một chút nhưng hơi khó đọc


Nếu bạn đã biết float, hãy nói giữa f32f64có thể được thực hiện vớistd::mem::size_of_val(&a)
Antony Hatchkins

1

Một số câu trả lời khác không làm việc, nhưng tôi thấy rằng các typename thùng hoạt động.

  1. Tạo một dự án mới:

    cargo new test_typename
  2. Sửa đổi Cargo.toml

    [dependencies]
    typename = "0.1.1"
  3. Sửa đổi mã nguồn của bạn

    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());
    }

Đầu ra là:

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

Tôi đã làm theo các bước bạn mô tả. Cho đến hôm nay, typenamekhông hoạt động với các biến mà không có loại rõ ràng trong khai báo. Chạy nó với my_number câu hỏi sẽ đưa ra lỗi sau "không thể gọi phương thức type_name_oftrên kiểu số mơ hồ {float}. Trợ giúp: bạn phải chỉ định một loại cho liên kết này, như f32"
Antony Hatchkins

Tôi kiểm tra 0.65và nó hoạt động tốt : type of c 0.65 0.65 is f64. đây là phiên bản của tôi:rustc 1.38.0-nightly (69656fa4c 2019-07-13)
Flyq

1

Nếu bạn chỉ muốn biết loại biến của mình trong quá trình phát triển tương tác, tôi rất khuyên bạn nên sử dụng rls (máy chủ ngôn ngữ rỉ sét) bên trong trình soạn thảo hoặc ide của bạn. Sau đó, bạn có thể chỉ cần bật hoặc chuyển vĩnh viễn khả năng di chuột và chỉ cần đặt con trỏ lên biến. Một hộp thoại nhỏ sẽ đưa ra thông tin về biến bao gồm loại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.