Rust メモ
勉強した成果を随時記述していく
基本
型
符号付き整数: i8, i16, i32, i64, i128, isize
通常、整数にはi32を使う
符号無し整数: u8, u16, u32, u64, u128, usize
浮動小数点数: f32, f64
char: 'a', 'α', '∞'など(Unicode単位)
bool: trueまたはfalse
ユニット型: ()が唯一の値
配列: [1, 2, 3]など
タプル: (1, true)
code:rust
let a : i32 = 1;
let b = 2i8; //サフィックスで型指定できる
変数宣言
変数はデフォルトでimmutable(不変)。再代入しようとすると怒られる
code:rust
let a : i32 = 0;
a += 1; //怒られる
余談:まあ確かにJSとか書いててもletよりもconstのほうが圧倒的に使う回数多いなあとは思っていた
mutable(可変)で宣言したいときはmutを使う
code:rust
let mut a : i32 = 0;
a += 1; //OK
また同じ変数名を何度でも再定義できる
code:rust
let a : i32 = 0;
let a : i32 = a + 1; //OK
文と式
Rustは文(statement)と式(expression)の区別が重要である
println!("hello") //文
0 //式
関数
関数の戻り値は「最後に扱った式の結果」
最後のreturnは省略するのが慣習らしい(途中で戻す場合はreturnは普通に使って良い)
code:rust
//必ず整数値0を返す関数
fn zero() -> i32 {
0
}
セミコロン
Rustではセミコロンは文と式の区切りに使う記号であり、文の「終わり」に使うものではない、という考え方。特に「最後に扱った式」が関数において重要になるので、セミコロンの置き方はCやJSと違い少しシビアな感覚。
イメージ的には<文>;<文>;<式> //<-最後に扱った式という感じ。
CやJSだと<文>;<文>;<文>;のようにすべてにセミコロンを置くのが慣例なのでちょっと感覚が違う。
余談:まあ最近はJSでもセミコロン書かずに改行だけで済ますのが流行りだけどね
関数の設計と「?」について
基本的にRustにおいて新規に関数を設計するときは、
メソッド的な関数(「ファイルに書き込む」など、なにかを実現する目的の関数)は
戻り型をResult(<データ型>)として
成功すればOKを返し、失敗すればErrを返すようにする
関数的な関数(「ファイルの内容を読む」など、なにかを取得や計算をする目的の関数)は
戻り型をOption(<データ型>)として
成功すれば必要なデータを、失敗すればNoneを返すようにする
という形に統一することで、圧倒的にその後のプログラムが楽になる。
なぜならそれらを1文字で手助けする「?」演算子が用意されているからである。
Rustでのエラー処理いろいろ(Option編)
Option<型>は、Noneを含むかもしれない<型>
例えばOption<String>は、typescriptで言うならstring | nullって感じ
代入(let)時になんとかするパターン。一番わかり易い。
code:rust
let x: Option<i32> = None;
let y: i32 = match x {
None => 0,
Some(v) => v
};
println!("{}",y); //-> 0
Noneの代わりに値を入れるパターン
code:rust
//unwrap_orはNoneが来ると代わりに指定値を入れる
let x: Option<i32> = None;
let y: i32 = x.unwrap_or(0);
println!("{}",y); //-> 0
エラー発生即関数脱出のパターン
?演算子を使う。関数が入れ子に呼び出されているケースのみ有効。None発生時に処理を即中断して親に伝える
code:rust
fn fnc() -> Option<i32> {
let x: Option<i32> = None;
//?を使うとNoneが入っていた場合その場でNoneを返して関数を抜ける(return None)
let y: i32 = x?;
println!("以下は実行されない");
Some(y)
}
fn main() {
match fnc() {
None => println!("None"),
Some(_) => println!("Some"),
};
}
Rustでのエラー処理:Result編
Resultはエラー状態を含む型
普通にmatchで分岐するパターン
code:rust
fn main() {
let s = "x";
//str.parse::<T>()はstrをT型に変換しようとする
match s.parse::<i32>() {
Ok(n) => println!("{}", n),
Err(e) => println!("Error Happened"),
};
}
エラー発生即関数脱出のパターン
?演算子を使う。関数が入れ子に呼び出されているケースのみ有効。Err発生時に処理を即中断して親に伝える
code:rust
use std::num::ParseIntError;
fn func() -> Result<i32, ParseIntError> {
let s = "x";
//str.parse::<T>()はstrをT型に変換しようとする
let n = s.parse::<i32>()?;
println!("コレ以降は実行されない");
Ok(n)
}
fn main() {
match func() {
Ok(n) => println!("{}", n),
Err(e) => println!("Error Happened"),
};
}
文字列リテラル(例:"Hello")は文字型(String)ではない
同様には扱えず、変換が必要な場合が多々ある
code:rust
//リテラルを直接使う
println!("Hello World"); //OK
//リテラルを変数に入れて直接使う
let str = "Hello World";
//println!(str) //エラー
println!("{}", str); //OK
println!("{}", &str); //OK
//&strはstrへの参照
//リテラルを文字列型に変換する
//let str: String = "Hello World";//エラー
let str: String = "Hello World".to_string(); //OK
println!("{}", &str); //OK
//文字列型を宣言する
let str: String = String::from("Hello World");
println!("{}", &str); //OK
文字列とバイト列の変換
File::writeとかでバイト列を要求されるが、文字列Stringをバイト列[u8]に変換するにはas_bytes()がある
code:rust
// pub fn write_all(&mut self, buf: &u8) -> Result<()> f.write_all(&(content_string.as_bytes()))?;
その逆(バイト列を文字列に変換)にはfrom_utf8() 関数がある
まだ使ったことはない
ファイル存在確認
code:rust
use std::path::Path;
fn is_file_exists(file_path_string: &String) -> bool {
let file_path = Path::new(file_path_string);
if file_path.exists() {
return true;
}
false
}
配列とスライス
配列
固定長。追加も削除もできない。
T[<型>;<大きさ>] (型は代入時に自明なら省略できる)
code:rust
スライス
配列の一部を抜き出したもの。ある意味可変長といえるが元となる配列の大きさを超えられない。
&T[<開始添字>..<終了添字]>
code:rust
ベクタ型
可変長
Vec<型>
最後尾への要素の追加はpush(<新しい要素>)
最後尾の要素の削除はpop()。戻り値はOption<T>なので注意(範囲外だった場合Noneが返る)
code:rust
//空のベクタの宣言
let mut vc: Vec<i32> = Vec::new();
// ベクタの初期化にはvec!マクロが使用できる。
//一つ追加する
vc.push(6);
//一つ取り出す(範囲外だった場合-1を返してみる)
let v = vc.pop().unwrap_or(-1);
println!("{}",v); // -> 6
配列・スライス・ベクタ型共通の扱い方
出力printlin!する→"{:?}
code:rust
println!("{:?}",xs);
長さ(要素数)→ len()
code:rust
println!("{}",vc.len()); //-> 5
ありがちエラー
warning: unused variable: ... help: if this is intentional, prefix it with an underscore: ...
宣言した変数が使われていない。デバッグ中にあれこれ試している途中によく出る。アンダースコアを先頭につけると警告をスキップするようだ
想定外に出ているなら直す必要がある、直すべき箇所は宣言箇所(アンダースコアをつけること)ではなく、この変数が使われている「つもりの」ほかの場所である
逆に、使わないと分かっている変数を受け取らなければならない状況(match内でよくある)ではアンダースコアのみ_を置いたりできる。Err(_)など。