web-dev-qa-db-ja.com

数値型を安全かつ慣用的に変換するにはどうすればよいですか?

編集者注:この質問は、Rust 1.0より前のバージョンからのものであり、Rust 1.0。 。

(たとえば)usizeからu32に変換する慣用的な方法は何ですか?

たとえば、4294967295us as u32を使用したキャストは機能し、 Rust 0.12型キャストに関するリファレンスドキュメントは と言います

数値は任意の数値型にキャストできます。生のポインタ値は、整数型または生のポインタ型との間でキャストできます。その他のキャストはサポートされていないため、コンパイルに失敗します。

しかし4294967296us as u32は静かにオーバーフローし、結果0を返します。

私は、 ToPrimitiveFromPrimitive を見つけました。これらは、to_u32() -> Option<u32>などの素敵な関数を提供します。しかし、それらは不安定とマークされています:

#[unstable(feature = "core", reason = "trait is likely to be removed")]

数値(およびポインター)型間で変換する慣用的な(そして安全な)方法は何ですか?

プラットフォームに依存するisize/usizeのサイズは、この質問をする理由の1つです。元のシナリオは、ツリーを表現できるようにu32からusizeに変換したかったからです。 Vec<u32>(たとえばlet t = Vec![0u32, 0u32, 1u32]の場合、ノード2の祖父母を取得するにはt[t[2us] as usize]になります)で、usizeが32ビット未満の場合、どのように失敗するのでしょうか。

35
Caspar

ToPrimitive/FromPrimitive

RFC 369、Num Reform、states

理想的には[...] ToPrimitive [...]はすべて削除され、Cに似た列挙型を扱うより原則的な方法が採用されます。

それまでの間、これらの特性は num crate で生き続けます。

特性なしで対処する

別のものに完全に適合するタイプから

ここに問題はありません。 Fromを使用して、損失が発生していないことを明示します。

fn example(v: i8) -> i32 {
    i32::from(v) // or v.into()
}

asを使用することもできますが、不要な場合は使用しないことをお勧めします(以下を参照)。

fn example(v: i8) -> i32 {
    v as i32
}

別のものに完全には収まらないタイプから

一般的に意味のある単一の方法はありません-1つのスペースに2つのものを収める方法を尋ねています。最初の良い試みの1つは、値が適合する場合はOptionSomeを使用し、そうでない場合はNoneを使用することです。その後、必要に応じて、プログラムを失敗させるか、デフォルト値に置き換えることができます。

Rust 1.34なので、 TryFrom を使用できます。

use std::convert::TryFrom;

fn example(v: i32) -> Option<i8> {
    i8::try_from(v).ok()
}

その前に、同様のコードを自分で記述する必要があります。

fn example(v: i32) -> Option<i8> {
    if v > std::i8::MAX as i32 {
        None
    } else {
        Some(v as i8)
    }
}

asが行うこと

しかし、4294967296us as u32は静かにオーバーフローし、結果0を返します

小さい型に変換する場合、asは、符号を含む上位ビットを無視して、数値の下位ビットのみを取得します。

fn main() {
    let a: u16 = 0x1234;
    let b: u8 = a as u8;
    println!("0x{:04x}, 0x{:02x}", a, b); // 0x1234, 0x34

    let a: i16 = -257;
    let b: u8 = a as u8;
    println!("0x{:02x}, 0x{:02x}", a, b); // 0xfeff, 0xff
}
22
Shepmaster