まえがき
この記事はRUSTチュートリアルをZerobillbank社内メンバー(有志)で勉強がてら、可能な限りわかりやすく内容を書き出す企画の一部です。
なお、初学者のため(略)という免責を打ちつつ間違いなどあればコメントで教えていただけますと一同嬉しく思います!
概要
一般的なコレクション
ベクタ型
特徴
- メモリ上に値を隣り合わせに並べる単独のデータ構造に2つ以上の値を保持させる
- 同じ型の値しか保持できない
- ベクタは、 ジェネリクスを使用して実装されている
- 標準ライブラリにより提供されている
Vec<T>型
は、どんな型でも保持できる
ベクタの生成
let v: Vec<i32> = Vec::new();
ベクタの更新
let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);
v.push(8);
- 値を変化させるためmutが必要
- 上記の場合、値が全てi32のため型注釈はなくても良い
ベクタのドロップ
{
let v = vec![1, 2, 3, 4];
// vで作業をする
} // <- vはここでスコープを抜け、解放される
ベクタの要素の参照
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2]; // 参照を得る
let third: Option<&i32> = v.get(2); // Optionで得る
let v = vec![1, 2, 3, 4, 5];
let does_not_exist = &v[100]; // パニックを起こす
let does_not_exist = v.get(100); // Noneが返る
ベクタの値を走査する
let v = vec![100, 32, 57];
for i in &v {
println!("{}", i);
}
変更する場合
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
Enumを使って複数の型を保持する
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
文字列型
文字列型とString型
文字列型
- Rustには、言語の核として1種類しか文字列型が存在しない
- 文字列スライスのstrで、通常借用された形態&str
String型
- 言語の核として組み込まれるのではなく、Rustの標準ライブラリで提供される
- 伸長可能、 可変、所有権のあるUTF-8エンコードされた文字列型
新規文字列の生成
let mut s = String::new();
初期値を指定して生成
どちらでも可
// to_stringを使う場合
let data = "initial contents";
let s = data.to_string();
let s = "initial contents".to_string();
// String::fromを使う場合
let s = String::from("initial contents");
文字列の更新
-
Stringはサイズの伸ばすことができる
-
Vec<T>
の中身のように追加のデータをプッシュすれば、中身も変化する -
String値を連結する
+
演算子や、format!
マクロを便利に使用することができる
push_strとpushで文字列を追加する
push_strメソッドで文字列スライスを追記することで、Stringを伸ばすことができる。
let mut s = String::from("foo");
s.push_str("bar");
pushメソッドは1文字を引数として取り、Stringに追加する
let mut s = String::from("lo");
s.push('l');
+演算子で連結
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // s1はムーブされ、もう使用できないことに注意
※+演算子はaddメソッドを使用しており、addメソッドは以下のようなシグネチャになっている。
fn add(self, s: &str) -> String {
-
selfは借用ではなく、所有権をそのままとっているのでs1がムーブされて使用できなくなっている
-
第二引数には&がついてるので、s2は参照で指定している
-
第二引数が&strなのにStringであるs2を渡すことができるのはコンパイラが「参照外し型強制」ということをしているためとのこと
- 「参照外し型強制」についてはのちの章で説明
format!マクロで連結
+演算子だと複数の文字列連結はわかりづらい。
複雑な文字列の連結には、format!
マクロを使用するとよい。
// +演算子を使った場合
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = s1 + "-" + &s2 + "-" + &s3;
// format!マクロを使った場合
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
ハッシュマップ
-
RUSTにおけるハッシュマップとは、 K型のKeyと、V型の Valueを対応関係で保持するデータ型のこと
- 型は Hashmap
-
ベクタ型のように番号によるデータの索引ではなく、Keyを使って索引する
ハッシュマップの生成
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
- 標準ライブラリのコレクションから HashMap を使用する宣言が必要
- 格納されるKey・Valueの型はそれぞれ均一である必要がある
ベクタ型を用いたハッシュマップ
use std::collections::HashMap;
// Keyになる情報をVectorに格納
let teams = vec![String::from("Blue"), String::from("Yellow")];
// valueをVectorに格納
let initial_scores = vec![10, 50];
let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
HashMap<_, _>
の部分は、「ベクタで使用されている型を使用する」という意味
ハッシュマップと所有権
値はハッシュマップに格納されると所有権を失う
fn main() {
use std::collections::HashMap;
let field_name = String::from("Favorite color");
let field_value = String::from("Blue");
let mut map = HashMap::new();
map.insert(field_name, field_value);
println!("{}", field_name);
}
-------------------------------------
Compiling playground v0.0.1 (/playground)
error[E0382]: borrow of moved value: `field_name`
--> src/main.rs:9:16
ハッシュマップの値にアクセスする
参照する際は以下のように束縛した変数でgetメソッドを使用することで取り出しが可能
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("Blue");
let score = scores.get(&team_name);
forを用いて全てのリソースにアクセスすることも可能
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
for (key, value) in &scores {
println!("{}: {}", key, value);
}
ハッシュマップの値を更新する
値の上書き
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Blue"), 25);
println!("{:?}", scores);
そもそも初期化時にmut
をつけているので、同じKeyに対して操作を行えば上書きが可能
キーに値がなかった場合のみ値を挿入する
あるキーに対して値が存在しているかチェックしたい場合、
rustのhashmapには entry
メソッドというものが実装されている
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(50);
println!("{:?}", scores);
-
Entry
はソース上列挙型で定義されていて、キーに対応する値を探した結果キーと値の組があれば(Occupied
)、なければないことを表わす値(Vacant
)をとる -
or_insert
は値がないことを示す返り値があった場合に、データを挿入するというメソッド
ではまた🙋♂️
さいごに
ZEROBILLBANKでは一緒に働く仲間を募集中です。
なんとかjsとか、ブロックチェーンとか、kubernetesとかでいろんなAPIを作るお仕事。
今のところエンジニアは5人くらい。スタートアップだけど、結構ホワイトで働きやすいです。