LoginSignup
5
1

More than 3 years have passed since last update.

Rust勉強中 - その5 -> 整数型

Last updated at Posted at 2019-09-27

自己紹介

出田 守と申します。
しがないPythonプログラマです。
情報セキュリティに興味があり現在勉強中です。CTFやバグバウンティなどで腕を磨いています。主に低レイヤの技術が好きで、そっちばかり目が行きがちです。

Rustを勉強していくうえで、読んで学び、手を動かし、記録し、楽しく学んでいけたらと思います。

環境

新しい言語を学ぶということで、普段使わないWindowsとVimという新しい開発環境で行っています。
OS: Windows10 Home 64bit 1903
CPU: Intel Core i5-3470 @ 3.20GHz
Rust: 1.38.0
RAM: 8.00GB
Editor: Vim 8.1.1
Terminal: PowerShell

前回

前回は私ではない何者かがどうやら暴走していたようです。記憶にございません。ただ、新しく学んだloopやクロージャなどは都合よく憶えています。
Rust勉強中 - 暴走1

Rustアップデート

Rust-jpのSlackでもアナウンスされてますが、Rustの1.38がリリースされたようです。
https://blog.rust-lang.org/2019/09/26/Rust-1.38.0.html

なので、私のRustも1.38にアップデートしてみようと思います。
アップデートは最初のインストール時にも使用したrustupコマンドを用いるようです。

$ rustup update stable

これだけ!非常に簡単!

$ cargo --version
cargo 1.38.0 (23ef9a4ef 2019-08-20)
$ rustc --version
rustc 1.38.0 (625451e37 2019-09-23)
$ rustdoc --version
rustdoc 1.38.0 (625451e37 2019-09-23)

1.38では以下のような感じで、型名が文字列として取得できるようになったみたいです!(上記URLより引用)

fn gen_value<T: Default>() -> T {
    println!("Initializing an instance of {}", std::any::type_name::<T>());
    Default::default()
}

fn main() {
    let _: i32 = gen_value();
    let _: String = gen_value();
}

doc - std::any::type_name
これからしばらく型について学習していくのでちょうど良いタイミングですので私も活用していきましょう!

以降しばらくはRustの型について学んでいきます。

まずは学習用のプロジェクトを念のために作っておきます。

$ cargo new --bin types
     Created binary (application) `types` package

型推論

Rustは静的型付け言語です。プログラマがソース上で型を明示的に宣言し、事前にコンパイラによって細かくチェックしてくれます。
PythonやJavascriptではインタプリタが動的に型付けをしてくれます。これらは動的型付け言語です。しかし、よく言われるように(Javascriptは分かりませんが・・・)Pythonは遅いと言われます。その原因の一つがこの動的型付けによる遅延と言われます。一方、Rustのような静的型付け言語は事前に型をチェックし、機械語に変換されるため高速です。

静的型付け言語は関数の戻り値や仮引数、変数などそれぞれの型が適切になるように考えて書く必要があります。Rustでは、すばらしい型推論が搭載されており、一部変数や式の型は宣言しなくても推論してくれます。

型を全て宣言
fn plus1(a: i32) ->  i32 {
    let b: i32 = 1i32;
    a+b
}

fn main() {
    let a: i32 = 1i32;
    println!("{}+1 = {}", a, plus1(a));
}
一部型を省略
fn plus1(a: i32) ->  i32 {
    let b = 1;
    a+b
}

fn main() {
    let a = 1;
    println!("{}+1 = {}", a, plus1(a));
}

整数型

整数型では符号なし整数型と符号あり整数型があります。

符号なし整数型

範囲
u8 0 ~ $2^8-1$ (0 ~ 255)
u16 0 ~ $2^{16}-1$ (0 ~ 65,535)
u32 0 ~ $2^{32}-1$ (0 ~ 4,294,967,295)
u64 0 ~ $2^{64}-1$ (0 ~ 18,446,744,073,709,551,615)
usize 32bitシステム: 0 ~ $2^{32}-1$
64bitシステム: 0 ~ $2^{64}-1$

符号あり整数型

範囲
i8 $-2^7$ ~ $2^7-1$ (-128 ~ 127)
i16 $-2^{15}$ ~ $2^{15}-1$ (-32,768 ~ 32,767)
i32 $-2^{31}$ ~ $2^{31}-1$ (-2,147,483,648 ~ 2,147,483,647)
i64 $-2^{63}$ ~ $2^{63}-1$ (-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807)
isize 32bitシステム: $-2^{31}$ ~ $2^{31}-1$
64bitシステム: $-2^{63}$ ~ $2^{63}-1$

通常はi32が高速になるので、どの整数型を宣言しようか迷ったときはi32にすると良いらしいです。
https://doc.rust-jp.rs/book/second-edition/ch03-02-data-types.html から引用

では、どの整数型を使うべきかはどう把握すればいいのでしょうか?もし確信が持てないのならば、 Rustの基準型は一般的にいい選択肢になります。整数型の基準はi32型です: 64ビットシステム上でも、 この型が普通最速になります。

また、Rustでは数値型とchar型は区別されています。

整数リテラル

整数リテラルは型を後ろに付けます。アンダーバー(_)を入れることで見た目を分かりやすくすることもできます。

整数リテラル 補足
15i8 i8型の10進数
0o17u16 u16型の8進数
0xfi32 i32型の16進数
0b1111u64 u64型の2進数
123_456 数値の間にアンダーバーを入れられる
0o17_i32 型の間にアンダーバーを入れられる
0x_f プレフィックスの間にアンダーバーを入れられる

バイトリテラル

u8の値を表す文字のようなリテラルをバイトリテラルがあります。
バイトリテラルはb'文字'またはb'\xhh'で書きます。(hhは16進数)

バイトリテラル 整数リテラル
b'A'(b'\x41') 65u8
b'\''(b'\x27') 39u8
b'\'(b'\x5c') 92u8
b'\n'(b'\xa') 10u8
b'\r'(b'\xd') 13u8
b'\t'(b'\x9') 9u8

整数のキャスト

整数型から別の整数型へ変換する場合はas演算子を使用します。

変換 結果 備考
0i8 as u16 0u16 i8型からu16型への変換
-1i16 as i32 -1i32 符号拡張
65535u16 as i32 65535i32 ゼロ拡張
256u16 as u8 0u8 切り捨て1
257u16 as u8 1u8 切り捨て2
65535u32 as i16 -1i16 切り捨て3
-1i8 as u8 255u8 切り捨て4
255u8 as i8 -1i8 切り捨て5

切り捨て(truncation)はサイズが大きい型から小さい方へ変換される際に発生します。変換先のbitから上位のbitは切り捨てられます。

整数型のメソッド

整数型にはメソッドがあります。
その一部を紹介します。それ以外はドキュメントを検索します。

結果
2u16.pow(4) 16
(-1i32).abs() 1
-1i32.abs() -1
15_u8(0b1111).reverse_bits() 240(0b11110000)

absメソッドでは括弧なしとありで結果が違います。これは マイナス(-)よりもメソッドが先に評価される ためです。なので、括弧なしの方ではメソッドが先に評価され、その後にマイナスをつけるのでこのような結果になっています。

この記事の最後にこれまでの整数型について調べる際に使用したソースコードを載せておきます。

main.rs
fn print_int_range() {
    println!("\n[print integer range]");
    println!("u8:    {} ~ {}", std::u8::MIN, std::u8::MAX);
    println!("u16:   {} ~ {}", std::u16::MIN, std::u16::MAX);
    println!("u32:   {} ~ {}", std::u32::MIN, std::u32::MAX);
    println!("u64:   {} ~ {}", std::u64::MIN, std::u64::MAX);
    println!("usize: {} ~ {}", std::usize::MIN, std::usize::MAX);
    println!("i8:    {} ~ {}", std::i8::MIN, std::i8::MAX);
    println!("i16:   {} ~ {}", std::i16::MIN, std::i16::MAX);
    println!("i32:   {} ~ {}", std::i32::MIN, std::i32::MAX);
    println!("i64:   {} ~ {}", std::i64::MIN, std::i64::MAX);
    println!("isize: {} ~ {}", std::isize::MIN, std::isize::MAX);
}

fn get_type<T>(_: T) -> &'static str {
    std::any::type_name::<T>()
}

fn print_int_literals() {
    println!("\n[print integer literals]");
    println!("15i8   :   \ttype={},\t decimal={}", get_type(15i8),      15i8);
    println!("0o17u16:   \ttype={},\t decimal={}", get_type(0o17u16),   0o17u16);
    println!("0xfi32 :   \ttype={},\t decimal={}", get_type(0xfi32),    0xfi32);
    println!("0b1111u64 :\ttype={},\t decimal={}", get_type(0b1111u64), 0b1111u64);
    println!("123_456 :  \ttype={},\t decimal={}", get_type(123_456),   123_456);
    println!("0o17_i32 : \ttype={},\t decimal={}", get_type(0o17_i32),  0o17_i32);
    println!("0x_f:      \ttype={},\t decimal={}", get_type(0x_f),      0x_f);
}

fn print_byte_literals() {
    println!("\n[print byte literals]");
    println!("b'A' (b'\\x{l:x}'):{l}{}", get_type(b'A'), l=b'A');
    println!("b'\\''(b'\\x{l:x}'):{l}{}", get_type(b'\''), l=b'\'');
    println!("b'\\\\'(b'\\x{l:x}'):{l}{}", get_type(b'\\'), l=b'\\');
    println!("b'\\n'(b'\\x{l:x}'): {l}{}", get_type(b'\n'), l=b'\n');
    println!("b'\\r'(b'\\x{l:x}'): {l}{}", get_type(b'\r'), l=b'\r');
    println!("b'\\t'(b'\\x{l:x}'): {l}{}", get_type(b'\t'), l=b'\t');
}

fn print_int_cast() {
    println!("\n[print integer cast]");
    println!("<normal>");
    println!("    0i8      as u16 => {}{}", 0i8 as u16, get_type(0i8 as u16));
    println!("<sign extended>");
    println!("   -1i16     as i32 => {}{}", -1i16 as i32, get_type(-1i16 as i32));
    println!("<zero extended>");
    println!("    65535u16 as i32 => {}{}", 65535u16 as i32, get_type(65535u16 as i32));
    println!("<truncation>");
    println!("    256u16   as u8  => {}{}", 256u16 as u8, get_type(256u16 as u8));
    println!("    257u16   as u8  => {}{}", 257u16 as u8, get_type(257u16 as u8));
    println!("    65535u32 as i16 => {}{}", 65535u32 as i16, get_type(65535u32 as i16));
    println!("   -1i8      as u8  => {}{}", -1i8 as u8, get_type(-1i8 as u8));
    println!("    255u8    as i8  => {}{}", 255u8 as i8, get_type(255u8 as i8));
}

fn print_int_methods() {
    println!("\n[print interger methods]");
    println!("2u16.pow(4)                      = {}", 2u16.pow(4));
    println!("(-1i32).abs()                    = {}", (-1i32).abs());
    println!("-1i32.abs()                      = {}", -1i32.abs());
    println!("15_u8(0b{0:08b}).reverse_bits() = {1}(0b{1:08b})", 15_u8, 15_u8.reverse_bits());
}

fn main() {
    print_int_range();
    print_int_literals();
    print_byte_literals();
    print_int_cast();
    print_int_methods();
}
$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target\debug\types.exe`

[print integer range]
u8:    0 ~ 255
u16:   0 ~ 65535
u32:   0 ~ 4294967295
u64:   0 ~ 18446744073709551615
usize: 0 ~ 18446744073709551615
i8:    -128 ~ 127
i16:   -32768 ~ 32767
i32:   -2147483648 ~ 2147483647
i64:   -9223372036854775808 ~ 9223372036854775807
isize: -9223372036854775808 ~ 9223372036854775807

[print integer literals]
15i8   :        type=i8,         decimal=15
0o17u16:        type=u16,        decimal=15
0xfi32 :        type=i32,        decimal=15
0b1111u64 :     type=u64,        decimal=15
123_456 :       type=i32,        decimal=123456
0o17_i32 :      type=i32,        decimal=15
0x_f:           type=i32,        decimal=15

[print byte literals]
b'A' (b'\x41'):65u8
b'\''(b'\x27'):39u8
b'\\'(b'\x5c'):92u8
b'\n'(b'\xa'): 10u8
b'\r'(b'\xd'): 13u8
b'\t'(b'\x9'): 9u8

[print integer cast]
<normal>
    0i8      as u16 => 0u16
<sign extended>
   -1i16     as i32 => -1i32
<zero extended>
    65535u16 as i32 => 65535i32
<truncation>
    256u16   as u8  => 0u8
    257u16   as u8  => 1u8
    65535u32 as i16 => -1i16
   -1i8      as u8  => 255u8
    255u8    as i8  => -1i8

[print interger methods]
2u16.pow(4)                      = 16
(-1i32).abs()                    = 1
-1i32.abs()                      = -1
15_u8(0b00001111).reverse_bits() = 240(0b11110000)

さっそくstd::any::type_nameを使用してみました!ちゃんと型名が返ってきています。

今回はここまで。

5
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1