Rust에서 변수 유형을 어떻게 인쇄합니까?


238

나는 다음을 가지고있다 :

let mut my_number = 32.90;

유형을 my_number어떻게 인쇄 합니까?

사용 type하고 type_of작동하지 않았습니다. 숫자 유형을 인쇄 할 수있는 다른 방법이 있습니까?

답변:


177

변수의 유형 을 찾고 컴파일 타임에 기꺼이하려는 경우 오류가 발생하여 컴파일러가 선택하도록 할 수 있습니다.

예를 들어 변수를 작동하지 않는 유형으로 설정하십시오 .

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

이것들은 유형을 나타내며,이 경우 실제로 완전히 해결되지 않습니다. 첫 번째 예에서는 "부동 소수점 변수" {float}라고하고 세 가지 예에서는 " "입니다. 이것은 사용 방법에 따라 f32또는로 끝날 수있는 부분적으로 해결 된 유형입니다 f64. " {float}나는 완전히 확인이"무엇인지 모르겠어요 "는 의미 자리의 법적 유형의 이름이 아닙니다", 그러나 그것은 이다 부동 소수점 숫자입니다. 부동 소수점 변수의 경우 제한하지 않으면 기본값은 f64¹입니다. 규정되지 않은 정수 리터럴의 기본값은 i32입니다.

또한보십시오:


¹ 여전히 사이에서 결정 할 수 있도록 컴파일러를 이해할 수없는 방식이있을 수 있습니다 f32f64; 잘 모르겠습니다. 이전처럼 간단 32.90.eq(&32.90)했지만 f64현재와 같이 행복하게 취급 되므로 잘 모르겠습니다.


4
:?꽤 오랫동안 오랫동안 수동으로 구현되었습니다. 그러나 더 중요한 것은 숫자 유형에 std::fmt::Debug대한 구현 (그것이 :?사용하는 것)에는 더 이상 유형 이 무엇인지 나타내는 접미사가 포함되지 않습니다.
Chris Morgan

2
식의 형식을 찾으려면 이러한 기술을 많이 사용하지만 특히 형식 매개 변수가 관련된 경우 항상 작동하지는 않습니다. 예를 들어, 컴파일러는 ImageBuffer<_, Vec<_>>이러한 것들 중 하나를 매개 변수로 사용하는 함수를 작성하려고 할 때 크게 도움이되지 않을 것이라고 기대합니다 . 그리고 이것은 그렇지 않으면를 추가 할 때까지 컴파일되는 코드에서 발생합니다 :(). 더 좋은 방법이 없습니까?
Christopher Armstrong

2
이것은 약간 복잡하고 직관적이지 않은 것 같습니다. 코드 편집기에서는 매우 어려울 것입니다. 예를 들어 Emacs는 다른 언어와 같이 커서가 변수에있을 때 유형을 제공합니까? 컴파일러가 오류 발생시 형식을 알 수 있다면 오류가 없을 때 이미 형식을 알고 있어야합니까?
xji

1
@JIXiang : Rust Language Server는이 정보를 IDE에 제공하는 것에 관한 것이지만 아직 성숙하지는 않았습니다. 첫 알파 릴리스는 며칠 전이었습니다. 그렇습니다. 이것은 엘드리치 방식입니다. 예, 목표를 달성하기위한 난해한 방식이 꾸준히오고 있습니다.
Chris Morgan

1
이것은 해킹과 매우 흡사합니다. 이것이 실제로 변수 유형을 확인하는 관용적 방법입니까?
confused00

109

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

@ vbo : 안정화 될 때까지는 아닙니다. 이와 같은 어떤 것이라도 꽤 오랫동안 안정되지는 않을 것입니다. 그리고 결코 안정화되지 않는다면 놀라지 않을 것입니다. 그것은 당신이 정말로해야 할 일이 아닙니다.
Chris Morgan

2
rust-nightly (1.3)에서는 첫 줄을 다음과 같이 변경할 때만 작동했습니다.#![feature(core_intrinsics)]
AT

1
@DmitriNesteruk : 값 ( )이 아닌 print_type_of참조 ( &T)를 가져 오므로 대신 T전달해야합니다 . 즉, 오히려 . &&str&strprint_type_of(&"foo")print_type_of("foo")
Chris Morgan

당신이 옳았 고, 3 년이 지났지 만 여전히 안정화되지 않았습니다.
Anton Kochkov

5
std::any::type_name1.38 이후 안정적 임 : stackoverflow.com/a/58119924
Tim Robinson

66

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 's answer 에서와 같이 특성을 사용해야합니다 .


1
대부분의 개발자는 파싱 오류 인쇄와 같은 디버깅 목적으로 이것을 사용하기를 원합니다.
kaiser

정확히 내가 필요한 것, 이것이 왜 대답이 아닌지 모르겠습니다!
James Poulose

1
@JamesPoulose이 함수는 최근에 사용되었으므로 제 대답은 더 새롭습니다.
Boiethios

53

미리 모든 유형을 알고 있다면 특성을 사용하여 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"
    }
}

//...

친밀감이나 아무것도 아님 '이 없으므로 더 제한적이지만 이것이 문자열을 얻고 안정적으로 만드는 유일한 솔루션입니다. ( 프랑스 령 Boiethios의 답변 참조 ) 그러나 매우 힘들고 유형 매개 변수를 고려하지 않으므로 다음과 같이 할 수 있습니다 ...

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

녹 놀이터


이 답변은 두 가지를 섞지 않기 위해 두 가지 별도의 답변으로 나눌 수 있습니다.
Prajwal Dhatwalia

2
@PrajwalDhatwalia 나는 당신이 말한 것에 대해 생각 해 왔으며 버전이 서로 보완되는 방식에 만족한다고 생각합니다. 특성 버전은 매크로 버전이 후드에서 수행하는 작업을 단순화하여 목표를 명확하게 보여줍니다. 반면에 매크로 버전은 특성 버전을보다 일반적으로 사용하는 방법을 보여줍니다. 그것을 할 수있는 유일한 방법은 아니지만 가능하다는 것을 보여주는 것조차 유리합니다. 요약하면, 이것은 두 가지 답이 될 수 있지만 전체가 부분의 합보다 크다고 생각합니다.
phicr

19

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

이것은 유명한 포맷터 를 구현하기 위해 내부적 으로 사용되는 것입니다 {:?}.


15

** 업데이트 ** 최근에 작동 한 것으로 확인되지 않았습니다.

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

@vbo는 그의 솔루션이 더 이상 작동하지 않는다고 말합니다. 당신은 작동합니까?
안토니 Hatchkins

작동하지 않습니다`error [E0554] : #![feature]안정된 해제 채널에서 사용할 수 없습니다`
Muhammed Moussa

7

에서 변수를 사용하는 간단한 방법을 사용할 수도 있습니다 println!("{:?}", var). Debug유형에 대해 구현되지 않은 경우 컴파일러의 오류 메시지에서 유형을 볼 수 있습니다.

mod some {
    pub struct SomeType;
}

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

( 놀이 펜 )

더럽지 만 작동합니다.


8
Debug구현되지 않은 경우 – 이것은 거의 불가능합니다. 대부분의 구조체에 대해 가장 먼저해야 할 일은 add #[derive(Debug)]입니다. 나는 당신이 원하지 않는 시간 Debug이 매우 작다고 생각합니다 .
셰프 마스터

1
당신은 무슨 일이 일어나고 있는지 설명 할 수 있습니까 println!("{:?}", unknown_var);?? 문자열 보간이지만 왜 :?중괄호 안에 있습니까? @DenisKolodin
Julio Marins

오류가 발생했습니다. 컴파일러가 타입 정보에 오류를 제공하게하는 아이디어. Debug구현되지 않았기 때문에 사용 했지만 사용할 수도 있습니다 {}.
DenisKolodin

4

@ChrisMorgan에있어 대답 안정 녹에 ( "부동") 대략적인 유형을 얻을 수는하고 @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) }
}

약간 짧지 만 읽기 쉽지 않습니다.


이미 알고 있다면 float사이에f32 그리고f64 함께 수행 할 수 있습니다std::mem::size_of_val(&a)
안토니 Hatchkins

1

다른 답변은 작동하지 않지만 typename crate가 작동 한다는 것을 알았습니다 .

  1. 새 프로젝트를 작성하십시오.

    cargo new test_typename
  2. Cargo.toml 수정

    [dependencies]
    typename = "0.1.1"
  3. 소스 코드 수정

    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

설명한 단계를 수행했습니다. 오늘부터 typename선언에서 명시 적 유형이없는 변수는 작동하지 않습니다. my_number 질문에서 실행 하면 다음과 같은 오류가 발생합니다. " type_name_of모호한 숫자 유형에서는 메소드 를 호출 할 수 없습니다 {float}. 도움말 :이 바인딩에 대한 유형을 지정해야합니다 f32"
Antony Hatchkins

나는 테스트 0.65하고 잘 작동합니다 : type of c 0.65 0.65 is f64. 여기 내 버전이 있습니다 :rustc 1.38.0-nightly (69656fa4c 2019-07-13)
Flyq

1

대화 형 개발 중에 변수 유형을 알고 싶다면 편집기 또는 IDE 내부에서 rls (rust language server)를 사용하는 것이 좋습니다 . 그런 다음 단순히 호버링 기능을 영구적으로 활성화하거나 토글하고 커서를 변수 위에 놓을 수 있습니다. 작은 대화 상자에는 유형을 포함한 변수에 대한 정보가 나타납니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.