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}れ、3つすべての例では「」と呼ばれます。これは部分的に解決されたタイプであり、使用方法によっては、f32またはになる可能性がありf64ます。「{float}」は有効な型名ではなく、「これが何であるかは完全にはわかりません」という意味のプレースホルダーです、浮動小数点数です。浮動小数点変数の場合、制約しないと、デフォルトでf64¹ になります。(修飾されていない整数リテラルはデフォルトでになりますi32。)

以下も参照してください。


f32andとの間で判断できないようにコンパイラを困惑させる方法がまだあるかもしれませんf64。よく分かりません。以前はと同じくらい単純32.90.eq(&32.90)でしたが、それはf64今と同じように扱い、幸福にも満足しているので、わかりません。


4
:?かなり長い間、今では手動で実装されています。しかし、より重要なこととして、数値型のstd::fmt::Debug実装(それが:?使用するもの)には、それがどの型であるかを示すサフィックスが含まれなくなりました。
Chris Morgan

2
私はこれらの手法を使って式の型を見つけようとしますが、特に型パラメーターが関係している場合は、常に機能するとは限りません。たとえば、コンパイラーは、ImageBuffer<_, Vec<_>>これらのいずれかをパラメーターとして取る関数を記述しようとしているときに、どれがあまり役に立たないことを期待していることを教えてくれます。そしてこれは、私がを追加するまでコンパイルしないコードで発生します:()。より良い方法はありませんか?
Christopher Armstrong

2
これは少し複雑で直感的ではないようです。他の多くの言語のように、カーソルが変数上にあるときにEmacsが型を提供するなど、コードエディターにとって非常に難しいでしょうか?エラー時にコンパイラーがタイプを通知できる場合、エラーが発生していないときに、コンパイラーがタイプも知っているはずです。
xji

1
@JIXiang:Rust言語サーバーは、この情報をIDEに提供することをすべて目的としていますが、まだ成熟していません。最初のアルファリリースはほんの数日前でした。はい、これは大胆なアプローチです。はい、目標を達成する難解な方法は着実に来ています。
Chris Morgan

1
これはハックのように聞こえます。これは実際には変数の型をチェックする慣用的な方法ですか?
confused00

109

std::intrinsics::type_nameRustのナイトリービルドを使用する必要がありますが、型の名前を取得できる不安定な関数があります(これは、安定した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
その最初の行を変更するときにさび毎晩(1.3)にだけに働いていた#![feature(core_intrinsics)]
AT

1
@DmitriNesteruk:print_type_of&T値(T)ではなく参照()をとるので、&&strではなく渡す必要があり&strます。つまり、print_type_of(&"foo")ではありませんprint_type_of("foo")
Chris Morgan

あなたは正しかった、3年が経過しました、そしてそれはまだ安定していません。
アントンコチコフ2018年

5
std::any::type_namerust 1.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の回答のように、特性を使用する必要があります。


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

//...

組み込みやnothinはありません。したがって、より限定的ではありますが、これは文字列を取得して安定させる唯一のソリューションです。フランスのボイエチオスの答えを参照)しかし、それは非常に面倒であり、型パラメーターを考慮に入れていないので、...

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

さび遊び場


この答えは、2つの混乱を避けるために2つの別々の答えに分割できます。
Prajwal Dhatwalia

2
@PrajwalDhatwalia私はあなたが言ったことについて考えてきました、そして私はバージョンがお互いを補完する方法に満足しているように感じます。トレイトバージョンは、マクロバージョンが内部で何をしているのかを単純化して示し、目標をより明確にします。一方、マクロバージョンは、特性バージョンをより一般的に使用できるようにする方法を示しています。それを行う唯一の方法ではありませんが、それが可能であることを示すことも有利です。要約すると、これは2つの答えになる可能性がありますが、全体はその部分の合計よりも大きいと思います。
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は、彼のソリューションはもう機能しないと言います。あなたはうまくいきますか?
アントニーハッチキンズ

`エラー[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実装されていない -とはいえ、これはかなり低い場合です。ほとんどの構造体に対して最初に行うべきことの1つはadd #[derive(Debug)]です。あなたが欲しくない時Debugは非常に少ないと思います。
Shepmaster 2015

1
何が起こっているのprintln!("{:?}", unknown_var);か説明できますか?それは文字列補間:?ですか?なぜ中括弧の内側ですか?@DenisKolodin
Julio Marins、

エラーを引き起こします。コンパイラーにタイプ情報をエラーで提供させるという考え。Debug実装されていないので使用し{}ましたが、使用することもできます。
DenisKolodin 2017年

4

安定した錆の近似タイプ( "float")を取得するための@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間で伝える、f32f64して達成することができますstd::mem::size_of_val(&a)
アントニーHatchkins

1

他のいくつかの回答は機能しませんが、typenameのクレートは機能します。

  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
アントニー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.