- コアライブラリ内で定義されている
- 標準ライブラリ内で定義されている
- アルファベット 1 文字とビット数で表す
- アルファベット
- 符号あり整数型: i
- 符号なし整数型: u
- 浮動小数点数: f
- ビット数
- 8 ビット
- 16 ビット
- 32 ビット
- 64 ビット
- 128 ビット
- isize,usize
- アーキテクチャのメモリ空間に依存して変化
- 32 ビット環境なら 32 ビット、64 ビット環境なら 64 ビット
- str 型と String 型
- str 型
- 文字列スライス
- メモリ上に存在する文字列データの始点と長さを表している
- 固定長のため長さを変更することはできない
&str
- String 型
- 標準ライブラリで定義されている
- データの変更、長さの変更が可能
- 文字列操作に向いている
- str,String とも UTF-8 を用いている
- 互いに型変換可能
- String->&str
- String 型の文字列はすでにメモリ上にある
- そのメモリ位置と長さをコピーするだけであるからメモリを圧迫しない
- &str->String
fn main() {
let str1: String = String::from("Hello,world"); // String型の文字列を定義
let str2: &str = &str1; // String型から&str型への変換
let str3: String = str2.to_string(); // &str型からString型への変換
}
- 異なる型のデータを一緒に収めることができる
- 関数の返り値として使われることがある
let mut tpl = (2021, "2021");
t.0 = 2022; // .0のような添字で要素を指定する
t.1 = "2020";
- 特定の型の値を連続的に収めた集合
- 配列のサイズは固定
- 個別の要素には
[]
を用いてアクセスする
- 配列参照時には自動的にスライスとして扱われる
let mut a: [i32; 3] = [0, 1, 2];
let b: [i32; 3] = [0; 3]; // [0, 0, 0]
a[1] = b[1];
a[2] = b[2];
println!("{:?}", &a[1..3]);
struct Animal {
name: String,
age: u32
}
let p = Animal {
name: String::from("ポチ"),
age: 10
}
enum Event {
Quit,
KeyDown(u9),
MouseDown {
x: i32,
y: i32
}
}
let e1 = Event::Quit;
let w2 = Event::MouseDown {
x: 10,
y: 10
}
- 列挙型
- データが存在する場合と存在しない場合を表現できる
- None: データが存在しない
- Some(T): 型 T のデータが存在する
pub enum Option<T> {
None,
Some(T),
}
- 列挙型
- 処理の結果が成功する場合とエラーになる場合を表現できる
- 例:
Result<i32, String>
- 成功時は整数(i32)を返す
- 失敗時は文字列(String)のエラーメッセージを返す
- 処理の結果として Result 型を返す関数は多い
- 結果を Result として受け取った場合、match や if let を用いて処理する
pub enum Result(<T, E>) {
Ok(T),
Err(E),
}
fn hoge(num: i32) -> Result<i32, String> {
if num > 10 {
Ok(num)
} else {
Err("error".to_string())
}
}
fn main() {
let result = hoge(10);
match result {
Ok(number) => println!("{}", number),
Err(error) => println!("{}", error),
}
}
unwrap_or()
を用いることもできる
- Ok()だった場合はそのまま展開
- Err()だった場合は引数に与えた値を返す
let mut result: Result<i32, String> = Ok(200);
result.unwrap_or(-1) // 200
result = Err("error occurs".to_string());
result.unwrap_or(-1) // -1
?
演算子
- Ok だった場合は値を展開し、
- Err だった場合はその Err()をそのまま return する
- ベクタ型
- 配列と違う点
vec![]
マクロで初期化できる
push()
pop()
- 要素数以上の範囲にアクセスしようとすると panic が起きて強制終了
get()
を用いると panic は起きない
- None を返す
let mut vec1 = vec![1,2,3,4,5]; // [1,2,3,4,5]
let vec2 = vec![0; 5]; // [0,0,0,0,0]
vec1.push(6); // [1,2,3,4,5,6]
- スタック領域ではなくヒープ領域にデータを保管する
- コンパイル時にデータのサイズが確定していなくても大丈夫
- 確保したいタイミングで必要な分を確保
- 不要になったタイミングでメモリを開放
- Box 型を用いると、ヒープ領域にデータを保存し、スタック領域にヒープ領域へのポインタを置く
- 使用例
- コンパイル時にサイズがわからない型を格納するとき
- 大きなサイズの型の値を渡すが、データの中身をコピーせず、ポインタで渡す時
- 共通のトレイトを実装した様々な型を画一的にポインタで扱う時
- 変数束縛のキーワード
- 変数は不変
- 可変変数にしたい場合は mut キーワードを用いる
- 条件式
- 評価した値を変数に束縛したり関数の引数にすることができる
fn main() {
let num = -10;
let result = if num > 0 {
num
} else {
num * (-1)
};
println!("{:?}", result);
}
- コードを何度も繰り返し実行
- break を使ってループから抜け出す
- loop は式
fn main() {
let mut count = 0;
let result = loop {
println!("{}", count);
count = count + 1;
if count == 100 {
break count;
}
};
println!("{:?}", result);
}
fn main() {
let mut count = 0;
while count < 10 {
println!("{}", count);
count = count + 1;
}
}
fn main() {
let array = [0,1,2,3,4,5,6,7,8,9];
for elem in &array {
println!("{}", elem);
}
}
- switch 文のように変数の値によって処理を分岐させられる
- match は式である
fn main() {
let num: i32 = 10;
match num {
0 => println!("zero"),
10 => println!("ten"),
_ => println!("neither zero nor ten") // アンダースコアはそれ以外
}
}
for num in 1..5 {
println!(num);
}
struct Animal {
name: String,
nation: String,
}
impl Animal {
fn say_name(&self) {
println!("I am {}.", self.name);
}
fn from_where(&self) {
println!("I am from {}.", self.nation);
}
}
fn main() {
let animal = Animal{
name: String::from("Panda"),
nation: String::from("China"),
};
animal.say_name();
animal.from_where();
}
- プログラムを安全で高速に処理するための機能
- カプセル化
- 密接な関係にあるデータと処理をまとめ、あえてオブジェクトの外から見えなくすること
- ポリモーフィズム
- 使われ方が同じオブジェクトが複数あったばあい、外から見たインターフェースを決め使われ方を共通化する
- 抽象化にかかるコスト
- カプセル化、ポリモーフィズムを使うと抽象化ができる
- しかし抽象的なオブジェクトから具体的な処理を導く際にメモリ消費などのコストがかかる
- ゼロコスト抽象化とは
- 抽象化した処理を実行するために必要な負荷を可能な限り小さくすること
trait Tweet {
fn tweet(&self);
fn tweet_twice(&self) {
self.tweet();
self.tweet();
}
fn shout(&self) {
println!("piyopiyo");
}
}
struct Duck;
impl Tweet for Duck {
// tweet()を定義するだけでtweet_twice(), shout()が付いてくる
fn tweet(&self) {
println!("Quack!");
}
}
fn main() {
let duck = Duck {};
duck.tweet();
duck.tweet_twice();
duck.shout();
}
- メソッドは持っていない
- 意味や役割を与えるためのトレイト
- 例
- ある特定の型に対して定義した処理を別の型にも用意することが出来る機能
- 任意の型 T,S を引数に取りタプルにまとめて返す
fn to_tuple<T,S>(t:T, s:S) -> (T,S) {
(t,s)
}
fn main() {
let t1 = to_tuple(10, 20); // (i32,i32)
let t2 = to_tuple("Hello", "world"); // (String, String)
let t3 = to_tuple(3, "years old"); // (i32, String)
}
- 所有権、借用、ライフタイム
- 所有権
- それぞれの値には所有権がある
- 所有権を持っているのはただ一つの変数
- その変数がスコープから外れたら紐付けられたその値は破棄される
- 所有者が一人であるからメモリの二重解放問題が起きない
- 借用
- 値の所有権が一人に限られると困ることもある
- 値自身ではなく値の参照を貸し出す機能
- 所有権はもとの所有者がもったままでその値を参照する権利だけをもらう
- ライフタイム
- 値の参照を借用している間に値が破棄されてしまっては困る
- 参照は所有者より長生きではいけない
- 参照には安全に利用できる期間を明確にする必要がある
=
で値の所有権が移る
- C++の場合
- Rust では
- 右の変数が所有していた値の所有権が左の変数に渡る
- Copy トレイトを持つ型はコピーセマンティクスになり
=
で値がコピーされる
- ほとんどの型ではムーブセマンティクスがデフォルト
- ムーブセマンティクスによって所有権システムが機能する
struct Color {
r: i32,
g: i32,
b: i32,
}
fn main() {
let a = Color{
r: 255,
g: 255,
b: 255
};
let b = a; // 所有権が譲渡される
println!("{}, {}, {}", b.r, b.g, b.b);
}
- 値の参照を渡す方法を借用という
- 不変な参照を渡す場合と可変な参照を渡す場合でルールが異なる
- 不変な参照を渡す場合
- いくつでも参照を渡すことが出来る
let x = 5; let y = &x; let z = &x;
- 可変な参照を渡す場合
- 一度に渡せる参照はひとつだけ
- 値の更新が発生した場合に値が壊れる可能性があるため
let mut = 5; let y = &mut x; let z = &mut x; //🙅
- 不変な参照渡し
- 可変な参照渡し
fn print_data(data: &String) {
println!("{}", data);
}
fn main() {
let data = "Hello, world".to_string();
print_data(&data); // 関数print_data()にはdataの参照しか渡していないから所有権は変わらない
println!("{}", data);
}
- Resource Acquisition Is Initialization
- リソースの取得は初期化である
- 変数の初期化時にリソースの確保を行ない、
- 変数を破棄する時にリソースの解放を行なう
- メモリに限らず、開いているファイル、データベース接続なども変数破棄時に開放される
- Dropトレイト
- デストラクタ
- 何らかの解放処理が必要な場合に便利
- 保有しているリソースを開放してくれる
struct Droppable;
impl Drop for Droppable {
fn drop(&mut self) {
println!("リソースを開放します");
}
}
fn main() {
{
let d = Droppable;
} // 変数dのライフタイムが終わる
}
- 共有メモリ
- 複数のスレッドで同じメモリ領域を共有する
std::sync::{Arc, Mutex}
を用いる
std::sync::Arc
- Atomatically Reference Counted
- 所有権を共有するため所有権を所有している所有者の数をカウントするユーティリティ
- 所有者が0になったところでメモリを解放
std::sync::Mutex
- lockメソッドにより排他制御を行なうためのユーティリティ
- あるスレッドがlockを使用するとそれ以外のスレッドのlockは完了しない(ストップする)
- そのスレッドがアクセスを完了し参照を破棄→他のスレッドに出番が回る
- メッセージパッシング
- 各スレッドがお互いにメッセージをやり取りしながら動作するしくみ
- ソースコードとテストコードを同じファイルの中に記述できる
- テスト関数にはアトリビュート
#[test]
を用いる
cargo test
コマンドでテスト実行
assert!()
assert_eq!()
assert_ne!()