若干理解が足りない部分もあるが,個人的に面白かった項目を書いておく.
参照周り
Rustは値ごとに所有権を有しており,その扱い方がいろいろある.
移動
所有権を移動した値を再度使おうとするとエラーが出る.
メモリ上の動きとしては,sのヘッダ情報には {ヒープ領域へのアドレス,capacity, length}
が入っており,このヘッダ情報(3ワードのみ)をtに移動する.
下は移動された後の変数を使おうとするとエラーを吐く例.
fn main() { let s = vec![1, 2, 3]; let t = s; println!("{:?}", t); println!("{:?}", s); } // error 3 | let t = s; | - value moved here 4 | println!("{:?}", t); 5 | println!("{:?}", s); | ^ value used here after move
関数の引数として移動する場合もエラーを吐く.
fn print_vec(v: Vec<i64>) { for val in v { println!("{}", val); } } fn main() { let v = vec![1, 2, 3]; print_vec(v); println!("{:?}", v); } // error 9 | print_vec(v); | - value moved here 10 | println!("{:?}", v); | ^ value used here after move
借用
参照を渡すことで,所有権を借用できる. 所有権は一時的に貸し出され,その後帰ってくる. なので以下のコードはエラーを吐かない.
fn print_vec(v: &Vec<i64>) { for val in v { println!("{}", val); } } fn main() { let v = vec![1, 2, 3]; print_vec(&v); println!("{:?}", v); } // output 1 2 3 [1, 2, 3]
Copy型
Copy型は移動した場合,Stack領域に値のコピーが作成される.
下のコードは,Copy型であるi32を移動させた時の例. 基本的にヒープ領域にある値を参照している型は,Copy型ではないので,移動した時にコピーは作成されない.
fn main() { let s: i32 = 1; let t: i32 = s; println!("t: {}", t); println!("s: {}", s); } // output t: 1 s: 1
Box
T型の変数を受け取りそれをヒープ領域に写してその参照を得る.
Box
以下のexampleがわかりやすい.
Box, stack and heap - Rust By Example - Rust Documentation
エラー周り
Result型
Eitherみたいなアレみたいな感じ. 値を取り出すときはパターンマッチで,OkかErrを判定して値を取り出す. ?演算子は,その関数でエラーが起こったらErrを返し,正常に関数が実行できたら次の行へと処理を移せる最高のやつ. 難しいことを考えず,手続き型のように書けるのがいいですね.
use std::io::prelude::*; use std::fs::File; use std::io::Result; fn read_file(filename: String) -> Result<String> { let mut file = File::open(filename)?; // => Err? let mut contents = String::new(); file.read_to_string(&mut contents)?; // => Err? return Ok(contents); } fn main() { let contents = read_file("foo.txt".to_string()); match contents { Ok(v) => println!("{}", v), Err(e) => panic!(e), } } // foo.txt 1 2 3 // output 1 2 3
Option
SomeとNoneでパターンマッチして,分岐したりできるいろんな言語でよくあるやつ.
fn div(a: i64, b: i64) -> Option<i64> { if b == 0 { return None; } return Some(a / b); } fn main() { let val = div(10, 5); // => 10 / 5 match val { Some(v) => println!("{}", v), None => println!("None value"), } let none_val = div(10, 0); // => 10 / 0 match none_val { Some(v) => println!("{}", v), None => println!("None value"), } } // output 2 None value
パターンマッチ
Rustには,パターンマッチがあるのでいろいろ分岐が可能. listパターンみたいなものはないっぽい?
下のexampleがわかりやすくて良いです.
All the Pattern Syntax - The Rust Programming Language
トレイト
みんな大好き型クラス的なアレみたいな感じ.
impl <trait> for <struct name>
とかで実装する.
use std::ops::Add; struct User { name: String, } impl Add for User { // impl Add trait type Output = Self; fn add(self, other: Self) -> Self { return User{name: self.name + "_" + &other.name}; } } fn main() { let mituba = User{name: "mituba".to_string()}; let mother = User{name: "mother".to_string()}; let mituba_mother = mituba.add(mother); // mituba + _ + mother println!("{}", mituba_mother.name); let mituba = User{name: "mituba".to_string()}; let father = User{name: "father".to_string()}; let mituba_father = mituba + father; // こうも書ける println!("{}", mituba_father.name); } // output mituba_mother mituba_father
PartialEqを実装して,assert_eqとかできるようにする.
use std::cmp::PartialEq; #[derive(Debug)] struct User { name: String, } impl PartialEq for User { fn eq(&self, other: &User) -> bool { self.name == other.name } } fn main() { let mituba = User{name: "mituba".to_string()}; let mother = User{name: "mother".to_string()}; assert_ne!(mituba, mother); let mituba = User{name: "mituba".to_string()}; let mituba2 = User{name: "mituba".to_string()}; assert_eq!(mituba, mituba2); }
下のドキュメントにポリモーフィズムぽいやつも記載されている.
Advanced Traits - The Rust Programming Language
クロージャ
関数っぽく書けたり,map関数などに渡せる高階関数的なアレな感じ.
fn main() { let cl = |x: i64| -> i64 x + 1; println!("{}", cl(1)); let cl2 = |x: i64| -> i64 { let x1 = x + 1; return x1 + 2; }; println!("{}", cl2(1)); } // output 2 4
mapにクロージャを食わせる
fn main() { let v = vec![1, 2, 3, 4, 5]; v.iter().map(|x| x + 1).for_each(|x| println!("{}", x)); } // output 2 3 4 5 6
移動(クロージャ)
fn main() { let v = vec![1, 2, 3, 4, 5]; let cl = |vc: &Vec<i64>| vc.iter().for_each(|x| println!("{}", x)); cl(&v); println!("{:?}", v); } // output 1 2 3 4 5 [1, 2, 3, 4, 5] fn main() { let v = vec![1, 2, 3, 4, 5]; let cl = |vc: Vec<i64>| vc.iter().for_each(|x| println!("{}", x)); cl(v); println!("{:?}", v); } // output 9 | cl(v); | - value moved here 10 | println!("{:?}", v); | ^ value used here after move
下のドキュメントに他の事柄はいろいろ載ってます.
Closures: Anonymous Functions that Can Capture Their Environment - The Rust Programming Language
まとめ
Rustは,今までのプログラミング言語には若干ない概念があったりして,少しとっかかりづらい面もある感じですが,やってみると意外と面白かったりするので是非. お堅い言語かと思いきや意外と柔軟だったり.
本を読んだ後に参考にしたドキュメントです.ドキュメントが本当にしっかりしているのに感動したりして最高.