Rustの基礎 - 組み込み関数
概要
Rustは、標準ライブラリ (std) として多くの便利な機能を提供している。
Rustには、以下に示すような特徴がある。
- マクロシステム
println!、vec!等、マクロ (末尾に!が付く) により柔軟な機能を提供
- メソッド構文
.len()、.is_empty()等、値に対してメソッドとして呼び出す形式
- 型推論
- 多くの場合、コンパイラが自動的に型を推論するが、明示的な型指定が必要な場合もある。
- 所有権システム
- Rustの特徴的な機能であり、メモリ安全性を保証する。
- Result型とOption型
- エラー処理や値の有無を型安全に扱う仕組み
- 入出力関連
println!、print!、writeln!等
- 型変換関連
to_string、parse、from等
- コレクション操作
len、is_empty、collect等
- 範囲表現
Range型、step_by、rev等
- 型情報取得
std::any::type_name、std::mem::size_of等
- 判定メソッド
is_some、is_ok、is_nan等
println!マクロ (文字列を出力)
基本と書式
println!マクロの書式を以下に示す。
println!("フォーマット文字列", 引数...);
最も基本的な使用方法は、引数に文字列を指定して実行することにより、標準出力に対して引数に指定した文字列を出力する。
この時、自動的に最後に改行が出力される。
引数には、文字列リテラルの他、数値等の様々な型を指定することができる。
フォーマット指定子 {} を使用して、値を埋め込むことができる。
println!("Hello");
println!("{}", 100);
println!("{} {} {}", "Blue", "Red", "Green");
// 出力
Hello
100
Blue Red Green
フォーマット指定
複数の値を出力する場合、フォーマット指定子 {} を使用する。
位置指定やパラメータ名を使用することもできる。
// 順番に値を埋め込む
println!("{} {} {}", "Blue", "Red", "Green");
// 位置を指定して埋め込む
println!("{0} {2} {1}", "Blue", "Red", "Green");
// パラメータ名を使用
println!("{color1} {color2} {color3}", color1="Blue", color2="Red", color3="Green");
// 出力
Blue Red Green
Blue Green Red
Blue Red Green
デバッグ出力
{:?} を使用することで、デバッグ形式で値を出力できる。
構造体やベクター等の複雑なデータ型を出力する場合に便利である。
let vec = vec![10, 20, 30];
println!("{:?}", vec);
// より見やすい形式で出力
println!("{:#?}", vec);
// 出力
[10, 20, 30]
[
10,
20,
30,
]
改行無し
改行無しで出力する場合は、print! マクロを使用する。
print!("Hello");
print!("World");
// 出力
HelloWorld
ファイルへ出力する
ファイルに対して出力する場合は、write! マクロ または writeln! マクロを使用する。
use std::fs::File;
use std::io::Write;
fn main() -> std::io::Result<()> {
let mut file = File::create("output.txt")?;
writeln!(file, "Hello")?;
writeln!(file, "Bye")?;
Ok(())
}
to_stringメソッド (文字列に変換)
to_string メソッドは、値を文字列に変換して取得する。
Display トレイトを実装している型であれば、to_string メソッドを使用できる。
value.to_string()
let num_str = 100.to_string();
let vec_str = format!("{:?}", vec![10, 20, 30]);
let bool_str = true.to_string();
println!("{}", num_str);
println!("{}", vec_str);
println!("{}", bool_str);
// 出力
100
[10, 20, 30]
true
数値を文字列と連結する場合や数値から文字列を取得する場合等に使用する。
let message = "My age is ".to_string() + &100.to_string();
let value = "Value = ".to_string() + &format!("{:?}", vec![10, 20, 30]);
let result = "Result is ".to_string() + &true.to_string();
println!("{}", message);
println!("{}", value);
println!("{}", result);
// 出力
My age is 100
Value = [10, 20, 30]
Result is true
parseメソッド (文字列を数値に変換する)
整数への変換
parse メソッドは、文字列を指定した型に変換する。
let result: 型 = 文字列.parse().unwrap();
整数型への変換を行う場合、型を明示的に指定する必要がある。
変換に失敗する可能性があるため、Result型が返される。
let a: i32 = "15".parse().unwrap();
let b: i32 = "28".parse().unwrap();
let c: i32 = "1011".parse().unwrap();
println!("{}", a);
println!("{}", b);
println!("{}", c);
// 出力
15
28
1011
基数を指定した変換を行う場合は、i32::from_str_radix メソッドを使用する。
let a = i32::from_str_radix("1011", 2).unwrap();
let b = i32::from_str_radix("3F", 16).unwrap();
let c = i32::from_str_radix("24", 8).unwrap();
println!("{}", a);
println!("{}", b);
println!("{}", c);
// 出力
11
63
20
浮動小数点数への変換
浮動小数点数への変換も同様に parse メソッドを使用する。
let a: f64 = "15".parse().unwrap();
let b: f64 = "3.14".parse().unwrap();
let c: f64 = "5.755".parse().unwrap();
let d: f64 = "8.5e+5".parse().unwrap();
let e: f64 = "2.34e-4".parse().unwrap();
println!("{}", a);
println!("{}", b);
println!("{}", c);
println!("{}", d);
println!("{}", e);
// 出力
15
3.14
5.755
850000
0.000234
lenメソッド
len メソッドは、コレクションの要素数や文字列の長さを取得する。
collection.len()
引数には、文字列、ベクター、配列、ハッシュマップ等のコレクション型が指定できる。
let a = "Hello".len();
let b = vec!["red", "blue", "green"].len();
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert("s", 150);
map.insert("m", 160);
map.insert("l", 170);
map.insert("xl", 180);
let c = map.len();
println!("{}", a);
println!("{}", b);
println!("{}", c);
println!("{}", "Next week is a trip".len());
println!("{}", vec!["Yes", "No"].len());
// 出力
5
3
4
19
2
※注意
lenメソッドは、バイト数を返す。
文字数を取得する場合は chars().count() メソッドを使用する。
let s = "こんにちは";
println!("バイト数: {}", s.len());
println!("文字数: {}", s.chars().count());
// 出力
バイト数: 15
文字数: 5
is_emptyメソッド (空かどうかの判定)
is_empty メソッドは、コレクションが空かどうかを判定する。
戻り値として、ブール値の true または false を返す。
collection.is_empty()
let a = "Hello".is_empty();
let b = "".is_empty();
let c = vec![1, 2, 3].is_empty();
let d = Vec::<i32>::new().is_empty();
println!("{}", a);
println!("{}", b);
println!("{}", c);
println!("{}", d);
// 出力
false
true
false
true
Range型
Range 型は、連続した値の範囲を表現する。
start..end の形式で、start 以上 end 未満の範囲を表す。
start..=end の形式を使用すると、end を含む範囲となる。
通常のRange (..) は、終了値を含まないことに注意すること。
start..end // endを含まない
start..=end // endを含む
let r1 = 0..5;
let r2 = 4..7;
let r3 = 0..=5;
for i in r1 {
print!("{} ", i);
}
println!();
for i in r2 {
print!("{} ", i);
}
println!();
for i in r3 {
print!("{} ", i);
}
println!();
// 出力
0 1 2 3 4
4 5 6
0 1 2 3 4 5
step_by メソッドを使用することにより、ステップ数を指定できる。
for i in (0..10).step_by(2) {
print!("{} ", i);
}
println!();
// 出力
0 2 4 6 8
逆順の範囲を生成する場合は、rev メソッドを使用する。
for i in (1..=10).rev() {
print!("{} ", i);
}
println!();
for i in (0..8).rev().step_by(2) {
print!("{} ", i);
}
println!();
// 出力
10 9 8 7 6 5 4 3 2 1
7 5 3 1
Rangeは、for 文で繰り返し処理を実行する時に繰り返し回数を指定するため等に使用されることが多い。
for i in 0..10 {
println!("num : {}", i);
}
// 出力
num : 0
num : 1
num : 2
num : 3
num : 4
num : 5
num : 6
num : 7
num : 8
num : 9
また、配列のスライスを取得する場合にも使用される。
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let slice = &arr[2..5];
println!("{:?}", slice);
// 出力
[2, 3, 4]
std::mem::size_ofメソッド (型のサイズ取得)
std::mem::size_of メソッドは、指定した型のサイズをバイト単位で取得する。
std::mem::size_of::<T>()
以下の例では、Rustで使用される主なデータ型のサイズを確認している。
use std::mem::size_of;
println!("i32: {} bytes", size_of::<i32>());
println!("i64: {} bytes", size_of::<i64>());
println!("f64: {} bytes", size_of::<f64>());
println!("bool: {} bytes", size_of::<bool>());
println!("char: {} bytes", size_of::<char>());
println!("&str: {} bytes", size_of::<&str>());
println!("String: {} bytes", size_of::<String>());
println!("Vec<i32>: {} bytes", size_of::<Vec<i32>>());
println!("Option<i32>: {} bytes", size_of::<Option<i32>>());
// 出力(環境によって異なる場合がある)
i32: 4 bytes
i64: 8 bytes
f64: 8 bytes
bool: 1 bytes
char: 4 bytes
&str: 16 bytes
String: 24 bytes
Vec<i32>: 24 bytes
Option<i32>: 8 bytes
std::any::type_nameメソッド (型名の取得)
std::any::type_name メソッドは、指定した型の名前を文字列として取得する。
主にデバッグ用途で使用される。
std::any::type_name::<T>()
以下の例では、様々な型の名前を取得している。
use std::any::type_name;
fn print_type<T>(_: &T) {
println!("{}", type_name::<T>());
}
let s = "Hello";
let i = 123;
let f = 14.5;
let v = vec![1, 2, 3];
let b = true;
print_type(&s);
print_type(&i);
print_type(&f);
print_type(&v);
print_type(&b);
// 出力
&str
i32
f64
alloc::vec::Vec<i32>
bool
is_xxxメソッド群 (型判定メソッド)
Rustでは、様々な型に対して、特定の状態かどうかを判定するメソッドが用意されている。
Option型の判定
Option 型に対して、値が存在するかどうかを判定できる。
let some_value: Option<i32> = Some(5);
let none_value: Option<i32> = None;
println!("{}", some_value.is_some());
println!("{}", some_value.is_none());
println!("{}", none_value.is_some());
println!("{}", none_value.is_none());
// 出力
true
false
false
true
Result型の判定
Result 型に対して、成功か失敗かを判定できる。
let ok_result: Result<i32, &str> = Ok(5);
let err_result: Result<i32, &str> = Err("error");
println!("{}", ok_result.is_ok());
println!("{}", ok_result.is_err());
println!("{}", err_result.is_ok());
println!("{}", err_result.is_err());
// 出力
true
false
false
true
数値の判定
浮動小数点数に対して、特殊な値かどうかを判定できる。
let normal = 3.14;
let infinity = f64::INFINITY;
let nan = f64::NAN;
println!("normal - finite: {}, infinite: {}, nan: {}", normal.is_finite(), normal.is_infinite(), normal.is_nan());
println!("infinity - finite: {}, infinite: {}, nan: {}", infinity.is_finite(), infinity.is_infinite(), infinity.is_nan());
println!("nan - finite: {}, infinite: {}, nan: {}", nan.is_finite(), nan.is_infinite(), nan.is_nan());
// 出力
normal - finite: true, infinite: false, nan: false
infinity - finite: false, infinite: true, nan: false
nan - finite: false, infinite: false, nan: true
collectメソッド (イテレータをコレクションに変換)
collect メソッドは、イテレータを任意のコレクション型に変換する。
iterator.collect::<コレクション型>()
範囲やイテレータをベクター、ハッシュマップ等に変換する場合に使用される。
// 範囲をベクタに変換
let vec: Vec<i32> = (0..5).collect();
println!("{:?}", vec);
// フィルタした結果をベクタに変換
let even: Vec<i32> = (0..10).filter(|x| x % 2 == 0).collect();
println!("{:?}", even);
// 文字列を文字のベクタに変換
let chars: Vec<char> = "Hello".chars().collect();
println!("{:?}", chars);
// 出力
[0, 1, 2, 3, 4]
[0, 2, 4, 6, 8]
['H', 'e', 'l', 'l', 'o']
型推論により、戻り値の型を自動的に決定することもできる。
let vec = (0..5).collect::<Vec<i32>>();
println!("{:?}", vec);
// 出力
[0, 1, 2, 3, 4]