自己紹介
出田 守と申します。
しがない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メソッドでは括弧なしとありで結果が違います。これは マイナス(-)よりもメソッドが先に評価される ためです。なので、括弧なしの方ではメソッドが先に評価され、その後にマイナスをつけるのでこのような結果になっています。
この記事の最後にこれまでの整数型について調べる際に使用したソースコードを載せておきます。
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
を使用してみました!ちゃんと型名が返ってきています。
今回はここまで。