若干理解が足りない部分もあるが,個人的に面白かった項目を書いておく.
参照周り
Rustは値ごとに所有権を有しており,その扱い方がいろいろある.
移動
所有権を移動した値を再度使おうとするとエラーが出る.
メモリ上の動きとしては,sのヘッダ情報には {ヒープ領域へのアドレス,capacity, length}
が入っており,このヘッダ情報(3ワードのみ)をtに移動する.
下は移動された後の変数を使おうとするとエラーを吐く例.
fn main() {
let s = vec![1, 2, 3];
let t = s;
println!("{:?}", t);
println!("{:?}", s);
}
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);
}
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);
}
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);
}
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)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
return Ok(contents);
}
fn main() {
let contents = read_file("foo.txt".to_string());
match contents {
Ok(v) => println!("{}", v),
Err(e) => panic!(e),
}
}
1
2
3
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);
match val {
Some(v) => println!("{}", v),
None => println!("None value"),
}
let none_val = div(10, 0);
match none_val {
Some(v) => println!("{}", v),
None => println!("None value"),
}
}
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 {
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);
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);
}
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));
}
2
4
mapにクロージャを食わせる
fn main() {
let v = vec![1, 2, 3, 4, 5];
v.iter().map(|x| x + 1).for_each(|x| println!("{}", x));
}
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);
}
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);
}
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は,今までのプログラミング言語には若干ない概念があったりして,少しとっかかりづらい面もある感じですが,やってみると意外と面白かったりするので是非.
お堅い言語かと思いきや意外と柔軟だったり.
本を読んだ後に参考にしたドキュメントです.ドキュメントが本当にしっかりしているのに感動したりして最高.
Foreword - The Rust Programming Language