Rust'ta bir değişkenin türünü nasıl yazdırabilirim?


238

Şunlara sahibim:

let mut my_number = 32.90;

Nasıl yazdırabilirim my_number?

Kullanarak typeve type_ofişe yaramadı. Numaranın türünü yazdırabileceğim başka bir yol var mı?

Yanıtlar:


177

Yalnızca bir değişkenin türünü öğrenmek istiyorsanız ve bunu derleme zamanında yapmaya hazırsanız, bir hataya neden olabilir ve derleyicinin onu almasını sağlayabilirsiniz.

Örneğin , değişkeni çalışmayan bir türe ayarlayın :

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

Veya geçersiz bir yöntem çağırın :

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

Veya geçersiz bir alana erişin :

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

Bunlar, bu durumda aslında tam olarak çözülmeyen türü ortaya çıkarır. Buna ilk örnekte “kayan nokta değişkeni” ve {float}üç örnekte de “ ” denir ; bu, kısmen f32ya da f64nasıl kullandığınıza bağlı olarak çözülebilecek kısmen çözülmüş bir türdür . “ {float}Ben tam olarak emin bu” ne değilim “o anlamına gelen bir yer tutucu olduğunu, yasal bir tür adı değil”, ama olan bir kayan nokta sayısı. Kayan nokta değişkenlerinde, kısıtlamazsanız, varsayılan olarak f64¹ olur. (Niteliksiz bir tam sayı değişmez değeri varsayılan olarak kullanılır i32.)

Ayrıca bakınız:


Still Derleyiciyi f32ve arasında karar veremeyecek şekilde şaşırtmanın yolları olabilir f64; Emin değilim. Eskiden olduğu kadar basitti 32.90.eq(&32.90), ama bu hem f64şimdi hem de mutlu davranıyor , bu yüzden bilmiyorum.


4
:?uzun bir süredir manuel olarak uygulanmaktadır. Ancak daha da önemlisi, sayı türleri std::fmt::Debugiçin uygulama (bunun için kullanılan şeydir :?) artık hangi tipte olduğunu gösteren bir sonek içermemektedir.
Chris Morgan

2
Bir ifadenin türünü bulmaya çalışmak için bu teknikleri çok kullanıyorum, ancak özellikle ilgili tür parametreleri olduğunda her zaman işe yaramaz. Örneğin, derleyici ImageBuffer<_, Vec<_>>, bu şeylerden birini parametre olarak alan bir işlev yazmaya çalıştığımda bana çok yardımcı olmayan bir şey beklediğini söyleyecektir . Ve bu ekleyene kadar derleyen kodda olur :(). Daha iyi bir yol yok mu?
Christopher Armstrong

2
Bu biraz kıvrık ve sezgisel görünmüyor. Kod düzenleyici için, örneğin Emacs, diğer birçok dilde olduğu gibi, imleç değişkene dayandığında türü sağlamak çok zor olur mu? Derleyici hata üzerine türü söyleyebilirse, kesinlikle herhangi bir hata olmadığında da türü bilmelidir?
xji

1
@JIXiang: Rust Language Server tamamen bu bilgileri bir IDE'ye sunmakla ilgili, ancak henüz olgun değil - ilk alfa sürümü sadece birkaç gün önceydi. Evet, bu bir eldritch yaklaşımı; evet, hedefe ulaşmak için daha az ezoterik yollar sürekli geliyor.
Chris Morgan

1
bu kulağa bir hack gibi geliyor. bu aslında bir değişkenin türünü kontrol etmenin deyimsel yolu mudur?
confused00

109

Her std::intrinsics::type_namegece Rust yapısını kullanmak zorunda kalmanıza rağmen, bir türün adını alabileceğiniz kararsız bir işlev vardır (bu, istikrarlı Rust'da çalışmak için pek olası değildir). İşte bir örnek:

#![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: stabilize olana kadar değil. Böyle bir şeyin, eğer bir süre için, oldukça uzun bir süre stabilize olması pek olası değildir ve asla stabilize edilmezse beni şaşırtmaz; bu gerçekten yapmanız gereken bir şey değil.
Chris Morgan

2
Rust-nightly'de (1.3) sadece bu ilk satırı değiştirirken işe yaradı#![feature(core_intrinsics)]
AT

1
@DmitriNesteruk: print_type_ofreferansları (alıyor &T) değil, değerleri ( Tsen geçmelidir böylece) &&stryerine &str; yani, print_type_of(&"foo")yerine print_type_of("foo").
Chris Morgan

Haklıydın, 3 yıl geçti ve hala stabil değil.
Anton Kochkov

5
std::any::type_namepas 1.38'den beri kararlı: stackoverflow.com/a/58119924
Tim Robinson

66

std::any::type_nameİşlevi kullanabilirsiniz . Bunun gece derleyicisine veya harici bir kasaya ihtiyacı yoktur ve sonuçlar oldukça doğrudur:

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

Uyarı: belgelerde belirtildiği gibi, bu bilgiler sadece hata ayıklama amacıyla kullanılmalıdır:

Bu, teşhis amaçlı kullanıma yöneliktir. Dizenin tam içeriği ve biçimi, türün en iyi çaba açıklaması olmak dışında belirtilmez.

Tür temsilcinizin derleyici sürümleri arasında aynı kalmasını istiyorsanız, phicr yanıtında olduğu gibi bir özellik kullanmalısınız .


1
çoğu geliştirici bunu yazdırma ayrıştırma hataları gibi hata ayıklama amacıyla kullanmak istediği için benim için en iyi yanıt
kaiser

Tam olarak neye ihtiyacım vardı, neden bu işaretli cevap değil bilmiyorum!
James Poulose

1
@JamesPoulose Çünkü bu işlev yakın zamanda yanıtım daha yeni.
Boiethios

53

Önceden tüm türleri biliyorsanız, type_ofyöntem eklemek için özellikleri kullanabilirsiniz :

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

//...

İntrisik veya hiçbir şey yok, bu yüzden daha sınırlı olmasına rağmen, bu size bir ip ve istikrarlı olan tek çözümdür. (bkz. Fransız Boiethios'un cevabı ) Ancak, çok zahmetli ve tip parametrelerini hesaba katmıyor , bu yüzden ...

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

Kullanalım:

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

çıktı:

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

Rust Bahçesi


Bu cevap, ikisinin karışmasını önlemek için iki ayrı cevaba bölünebilir.
Prajwal Dhatwalia

2
@PrajwalDhatwalia Ne dediğini düşünüyordum ve sürümlerin birbirini nasıl tamamladığından memnun olduğumu hissediyorum. Özellik sürümü, makro sürümünün kaputun altında ne yaptığını basitleştirerek, hedeflerini daha net hale getirir. Makro versiyonu ise, özellik versiyonunun nasıl daha genel olarak kullanılabilir hale getirileceğini gösterir; bunu yapmanın tek yolu değildir, ancak bunun mümkün olduğunu göstermek bile avantajlıdır. Özetle, bu iki cevap olabilir, ama bütünün parçalarının toplamından daha büyük olduğunu hissediyorum.
phicr

19

UPD Aşağıdakiler artık çalışmıyor. Düzeltme için Shubham'ın cevabını kontrol edin .

Kontrol edin std::intrinsics::get_tydesc<T>(). Şu anda "deneysel" durumda, ama sadece tip sistemi kesmek harikaydı.

Aşağıdaki örneği inceleyin:

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

Ünlü biçimlendiriciyi uygulamak için dahili olarak kullanılan budur {:?}.


15

** GÜNCELLEME ** Bu son zamanlarda çalıştığı doğrulanmadı.

Vbo'nun cevabına dayanarak bunu yapmak için küçük bir sandık koydum. Size türü döndürmek veya yazdırmak için bir makro verir.

Bunu Cargo.toml dosyanıza koyun:

[dependencies]
t_bang = "0.1.2"

Sonra şöyle kullanabilirsiniz:

#[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, çözümünün artık çalışmadığını söylüyor. Sizinki çalışıyor mu?
Antony Hatchkins

çalışmıyor `hata [E0554]: #![feature]kararlı yayın kanalında kullanılamaz`
Muhammed Moussa

7

İçindeki değişkeni kullanmanın basit yaklaşımını da kullanabilirsiniz println!("{:?}", var). Eğer Debugtürü için uygulanmadı, sen derleyici'nın hata iletisinde türünü görebilirsiniz:

mod some {
    pub struct SomeType;
}

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

(çocuk parkı )

Kirli ama işe yarıyor.


8
Eğer Debuguygulanmadı - bu olsa oldukça olası bir durumdur. Herhangi bir yapı için yapmanız gereken ilk şeylerden biri eklemektir #[derive(Debug)]. Bence istemediğin Debugzamanlar çok küçük.
Shepmaster

1
neler olduğunu açıklayabilir misin println!("{:?}", unknown_var);?? Bu bir dize enterpolasyonu ama neden :?kıvrımlı parantezin içinde? @DenisKolodin
Julio Marins

Hatayı kışkırtırım. Derleyicinin hata ile tür bilgisi sağlamasına izin verme fikri. Kullanıldığım Debugiçin kullandım , ama siz de kullanabilirsiniz {}.
DenisKolodin

4

Bir @ChrisMorgan var cevabı istikrarlı pas içinde yaklaşık türünü almak için ( "şamandıra") ve bir @ShubhamJain var cevabı gece pas kararsız işlevi aracılığıyla kesin tipini ( "F64") alır.

Şimdi, kararlı pasta hassas tip (yani f32 ve f64 arasında karar verebilirsiniz)

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

sonuç

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)

Güncelleme

Turbofish varyasyonu

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

biraz daha kısa ama biraz daha az okunabilir.


Eğer varsa zaten biliyorum öyle floatarasındaki söylüyorum f32ve f64ile gerçekleştirilebilirstd::mem::size_of_val(&a)
Antony Hatchkins

1

Diğer bazı cevaplar işe yaramaz, ancak typename sandığının işe yaradığını görüyorum .

  1. Yeni bir proje oluşturun:

    cargo new test_typename
  2. Cargo.toml dosyasını değiştirin

    [dependencies]
    typename = "0.1.1"
  3. Kaynak kodunuzu değiştirin

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

Çıktı:

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

Açıkladığınız adımları izledim. Bugün itibariyle typename, beyanda açık tip olmayan değişkenlerle çalışmaz. Soruyla birlikte çalıştırıldığında my_number aşağıdaki hata " type_name_ofbelirsiz sayısal tipte yöntem {float}f32
çağıramıyor

Ben testi 0.65ve iyi çalışıyor: type of c 0.65 0.65 is f64. İşte benim sürüm:rustc 1.38.0-nightly (69656fa4c 2019-07-13)
Flyq

1

Etkileşimli geliştirme sırasında değişkeninizin türünü bilmek istiyorsanız , editörünüzün veya ide'nizin içinde rls (pas dil sunucusu) kullanmanızı şiddetle tavsiye ederim . Ardından, fareyle üzerine gelme yeteneğini kalıcı olarak etkinleştirebilir veya değiştirebilirsiniz ve imlecinizi değişkenin üzerine getirebilirsiniz. Küçük bir diyalog türü dahil değişken hakkında bilgi gelmelidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.