プログラミング言語Rust 「4.1. 変数束縛」写経時のつぶやき
2018/12/10
■ 変数束縛
— ちゃまぐ (@tyamaguc07) 2018年12月10日
変数束縛は何らかの値を名前へと束縛するため、後でその値を使える
Rustでは他の言語と違って1つの値に対して1つの名前が対応する
多くの言語では変数束縛は変数と呼ばれる
■ パターン
Rustの変数束縛は多少皮を被せてある
let の左側の式は「パターン」であって、ただの変数名ではない
■ 型アノテーション
— ちゃまぐ (@tyamaguc07) 2018年12月10日
Rustは静的な型付言語であり、前もって型を与えておき、コンパイル時に検査される
Rustには「型推論」と呼ばれるものがある
型推論が型が何であるか判断出来るなら、型を書く必要はない
書きたいなら型を書くことも出来る
型はコロン(:)のあとに書く
let x: i32 = 5;
Rustには多くのプリミティブな整数型がある
— ちゃまぐ (@tyamaguc07) 2018年12月10日
プリミティブな整数型は符号付き型は i 、符号無し型は u から始まる
整数型として可能なサイズは8、16、32、64ビット
■ 可変性
— ちゃまぐ (@tyamaguc07) 2018年12月10日
デフォルトで、 束縛はイミュータブル
mutを使って束縛をミュータブルに出来る(let mut x = 5;)
■ 束縛を初期化する
束縛(変数)を使う前に初期化されている必要がある
Rustは一度も使われていない変数について警告を出す
未初期化の値を使うことは許されていない(未初期化の値?)
文字列インターポーレーション (String interpolation)はコンピュータサイエンスの用語
— ちゃまぐ (@tyamaguc07) 2018年12月10日
単に波括弧だけを使った時は、Rustはインターポーレートされる値の型を調べて意味のある方法で表示しようとする
フォーマットを更に詳しく指定したいなら数多くのオプションを利用できる
■ スコープとシャドーイング
— ちゃまぐ (@tyamaguc07) 2018年12月10日
変数束縛にはスコープがあり、変数束縛は定義されたブロック内でのみ有効
ブロックとは { と } で囲まれた文の集まり
変数束縛は覆い隠すこともできる(このことをシャドーイングという)
あとに出てくる同じ名前の変数束縛があると、以前の束縛を上書きする
シャドーイングは同じ名前に違う型の値を再束縛することが出来る
— ちゃまぐ (@tyamaguc07) 2018年12月10日
プログラミング言語Rust 「3.2 食事する哲学者」写経時のつぶやき
2018/12/07
str型とString型が違うって言われて混乱。調べる
— ちゃまぐ (@tyamaguc07) 2018年12月7日
Stringの方を文字列、strの方を文字列スライスと読んだりする
— ちゃまぐ (@tyamaguc07) 2018年12月7日
String型はu8のベクタ、str型はu8のスライス
ベクタは、他のプログラミング言語でおなじみのヒープに格納される可変長配列
スライスは、実行時に長さの決定する固定長配列https://t.co/njijMTdI4k
■ 関連関数(associated function)
— ちゃまぐ (@tyamaguc07) 2018年12月7日
self を引数に取らない関数
幾つかの言語では、関連関数を「静的メソッド」(static methods)と呼んでいます。
impl ブロックは 構造体に関する定義を与える
— ちゃまぐ (@tyamaguc07) 2018年12月7日
&https://t.co/3Ucpvf6C24_string() は &strの指す文字列のコピーをString型で取得する
Rust言語は「式ベース(expression based)」なので、 Rustではほとんどが値を返す式となる
new()という名前はRustにとって特別な意味を持たないが構造体の新しいインスタンスを生成する関数としてよく用いられる
— ちゃまぐ (@tyamaguc07) 2018年12月7日
Rustでは、メソッドは明示的なselfパラメータを取る
Vec<T> は「ベクタ(vector)」とも呼ばれる、可変長の配列型
ベクタの走査にはforループを使え、それぞれの要素への参照を得れる
millisecondをmillsと省略するのか。
— ちゃまぐ (@tyamaguc07) 2018年12月7日
use は名前をスコープに持ち込む
2018/12/08
Vec<_> の _ は型プレースホルダ。「何らかの型のベクトルとするが、その型が何であるかはRustが解決せよ」という表現
— ちゃまぐ (@tyamaguc07) 2018年12月8日
into_iter()は所有権を持つイテレータを生成する
thread::spawn 関数はクロージャを1つ引数にとり、新しいスレッド上でそのクロージャを実行する
moveアノテーションはキャプチャする値の所有権がクロージャ内へと移動する
— ちゃまぐ (@tyamaguc07) 2018年12月8日
(このmoveはmoveクロージャと言われるものらしい)
thread::spawn 呼び出しの末尾にセミコロンを置かないことで、式としている
式は値を返すが、文は返さない
Rustには2種類の文がある。「宣言文」と「式文」
既に束縛されている変数(例えば、y = 5)への割当ては式
— ちゃまぐ (@tyamaguc07) 2018年12月8日
割当てが割り当てられる値を評価する他の言語とは異なり、Rustでは割当ての値は空のタプル
y=5の場合、割り当てられる値は5
タプルは異なる型の値の集合
式文は式を文に変換する
collect() は何らかのコレクション型を生成する
thread::spawnはスレッドへのハンドルを返す
— ちゃまぐ (@tyamaguc07) 2018年12月8日
joinHandle.join()はスレッドの終了まで待つ
Rustでなにかをunwrap()するとき、こう言っているのと同じです。「計算結果を取り出しなさい。もしエラーになっていたのなら、パニックを起こしてプログラムを終了させなさい。」
2018/12/09
usize => The pointer-sized unsigned integer type.
— ちゃまぐ (@tyamaguc07) 2018年12月9日
lock() 呼び出しは失敗する可能性があり、その場合はプログラムをクラッシュさせる
lock()が失敗するケースはロックを保持しているスレッドがパニックした場合のみ(この状態を「poisoned」 状態と表現する)
Rustは未使用の変数名があると警告を出す
— ちゃまぐ (@tyamaguc07) 2018年12月9日
変数名のプレフィックスにアンダースコア(例えば、_hoge)を使うことでRustは未使用の警告を出さない
「arc」は「アトミック参照カウント(atomic reference count)」を意味する
Arc<T>はRustの標準アトミック参照カウント型
— ちゃまぐ (@tyamaguc07) 2018年12月9日
複数の参照間で値の所有権を同時に共有できるように、値を特別な実行時の管理用データでくるむもの
管理用データは、値への参照がいくつ存在しているかというカウントが記録する=「参照カウント」
複数スレッドから安全にアクセスできる=「アトミック」
ムッシュ・フーコー(Foucault)は 4, 0 を引数にとるべきですが、 代わりに、 0, 4 としています。これはデッドロックを防ぐためのもの
— ちゃまぐ (@tyamaguc07) 2018年12月9日
→ 一人目が2つ目の🍴を取るときには二人目が2つ目の🍴を取る処理になっているため単純にやるとデッドロックする模様
Arc<T> の clone() メソッドにより参照カウントが増加し、 スコープ外に出たときは、参照カウントが減算される
— ちゃまぐ (@tyamaguc07) 2018年12月9日
参照カウントを行わないと、いつ解放すればよいかが分からなくなる
実行結果がサンプルと異なってあれってなった。
— ちゃまぐ (@tyamaguc07) 2018年12月9日
環境によって実行速度が変わるにで、あたり前ではある。
自分の場合は、🍴を取るのにかかる時間(150ms)を変更(15ms)に変更することで、二人同時に食べている状態を作れた。
プログラミング言語Rust 3.1. 数当てゲーム つぶやきまとめ
数当てゲーム 写経時のつぶやき
!が付くとマクロ
— ちゃまぐ (@tyamaguc07) 2018年12月3日
変数束縛
関連関数 → スタティックメソッドと呼ぶ言語もある
io::Resultを使っていないと警告を出す
標準ライブラリには乱数の機能がまだない
「クレート」はRustのコードのパッケージ
--binで出来るのはバイナリクレート
Cargoはバージョン記述の標準、セマンティックバージョニングを理解する
— ちゃまぐ (@tyamaguc07) 2018年12月3日
rand="0.3.0"はrand="^0.3.0" の略記で、「0.3.0と互換性のあるもの」という意味
正確に 0.3.0 だけを使いたいなら rand="=0.3.0"と記す
Cargoはそれぞれの最新版をレジストリ—https://t.co/5P9nqOVeTUのコピー—から取得
https://t.co/5P9nqOVeTUはオープンソースのRustプロジェクトを投稿する場所
— ちゃまぐ (@tyamaguc07) 2018年12月3日
Cargoは基準を満たす全てのバージョンを探索し、 Cargo.lock ファイルに書き出す
その後のビルドではCargoはまず Cargo.lock ファイルがあるか確認し、再度バージョンを探索することなく、そこで指定されたバージョンを使う
cargo updateはロックを無視して、指定したバージョンを満たす全ての最新版を探し、出来たらそれをロックファイルに書き出す
— ちゃまぐ (@tyamaguc07) 2018年12月3日
0.3.0が指定されている場合、デフォルトでは0.3.0より大きく、0.4.0より小さいバージョンを探す
0.4.xより大きなバージョンを使いたいなら直接Cargo.tomlを更新する必要がある
extern crateでdependenciesで指定したクレートを使うことを宣言
— ちゃまぐ (@tyamaguc07) 2018年12月5日
extern crate rand; で内部的にはuser rand; まで行われる
メソッドは「トレイト」に定義される
メソッドが動作するには、対象となるトレイトがスコープに入っている必要がある
use rand::Rng; はRngトレイトをスコープに入れる宣言
thread_rng()はrandに定義されている関数
— ちゃまぐ (@tyamaguc07) 2018年12月5日
gen_range() は Rngトレイトに定義されているメソッド
cmp() は比較可能なものに対しならなんでも呼べて、引数に比較したい対象の参照を取る
cmp()は先程 use した Ordering を返す
match文を使って正確に Ordering のどれであるかを判断する
Ordering はenum
Ordering enum は3つのバリアントを持つ。 Less 、 Equal そして Greater。
— ちゃまぐ (@tyamaguc07) 2018年12月5日
バリアント=variant。単語の意味として「わずかに異なるもの」
match 文ではある型の値を取って、それぞれの可能な値に対する「腕」を作れる
Ordering には3種類あるので、3つの腕を作る
Rustには強い静的な型システムがある
— ちゃまぐ (@tyamaguc07) 2018年12月5日
Rustは型推論も持つ
let guess = String::new() と書いた時、guess が文字列である筈だと推論出来るのでわざわざ型を書かなくてもよい
数字の型は色々ある(数値型、32bit数のi32、符号なし32bit数のu32、64bit不動小数点数のf64などなど)
Rustでは以前の guess の定義を新しいもので「隠す」ことが出来る。
— ちゃまぐ (@tyamaguc07) 2018年12月5日
このように隠すことをシャドーイングという。
最初 String であった guess を u32 に変換したい、というような状況でよく使われる
シャドーイングのおかげで 別々の名前を考える必要はなくなり、 guess の名前を再利用出来る
Stringのtrim()メソッドは文字列の最初と最後にある空白を取り除く
— ちゃまぐ (@tyamaguc07) 2018年12月6日
Stringのparse()メソッドは文字列を何かの数値へとパースする、その際にどの型の数値が欲しいのかを伝える必要がある
サンプルでは、let guess: u32で型を伝えている
Rustはエラーの可能性があるのに処理していないことを教えてくれる
— ちゃまぐ (@tyamaguc07) 2018年12月6日
expect()は、エラーがあったらpanic!を呼び出しクラッシュする
parse()はResultを返す
ResultはenumでOkとErrのvariantsを持つ
— ちゃまぐ (@tyamaguc07) 2018年12月6日
Ok(num)と表現するとき、Ok に内包された値を num という名前に設定している
Err(_)と表現するときは、エラーの内容を気にしないので名前ではなく _ を使っている
Rust開発環境 on Docker で Cargo new で "could not determine the current user, please set $USER" エラー
root@7e6ba33a449a:/projects# cargo new hogehoge error: Failed to create project `hogehoge` at `/projects/hogehoge` Caused by: could not determine the current user, please set $USER root@7e6ba33a449a:/projects# ls -al total 4 drwxr-xr-x 5 root root 160 Dec 2 05:22 . drwxr-xr-x 1 root root 4096 Dec 2 05:21 .. drwxr-xr-x 6 root root 192 Dec 2 04:58 helloworld drwxr-xr-x 4 root root 128 Dec 2 05:22 hogehoge drwxr-xr-x 6 root root 192 Dec 2 05:20 new_hello_world
エラーになる。(ディレクトリは作成される) メッセージの通り、現在のユーザが特定出来ないというもの。
解決方法はいくつかある。
- docker run 時に --env USER=ユーザ名 をつける
- DockerfileにENV USER=ユーザ名をつける
Rust Tutorialをやっていくにあたっては、2の手段が楽だと思ったので採用。
Rust開発環境作成 with docker
- github にリポジトリ作成
- https://hub.docker.com/_/rust/ からDockerfileを取得
- 現時点では1.30.1のDockerfile https://github.com/rust-lang-nursery/docker-rust/blob/c34d6993e8f9403c18f348de85787f1585fd3375/1.30.1/stretch/Dockerfile
curl -LO https://raw.githubusercontent.com/rust-lang-nursery/docker-rust/c34d6993e8f9403c18f348de85787f1585fd3375/1.30.1/stretch/Dockerfile
- イメージビルド
build -t learn-rust .
- エラー
failed to dial gRPC: unable to upgrade to h2c, received 502
- Docker Version 18.06.1-ce-mac73 から 2.0.0.0-mac81 (29211) にアップグレードして解決
- エラー
- docker run
- docker run -it learn-rust
- 各種バージョン確認
root@5bb70de85a33:/# rustup --version
rustup 1.14.0 (1e51b07cc 2018-10-04)
root@5bb70de85a33:/# cargo --version
cargo 1.30.0 (a1a4ad372 2018-11-02)
root@5bb70de85a33:/# rustc --version
rustc 1.30.1 (1433507eb 2018-11-07)
- Hello World
mkdir src
vi src/helloworld.rs
fn main() {
println!("Hello, world!");
}- srcのマウントしてdocker run
docker run -itv $PWD:/project learn-rust
- コンパイル
cd project/src
rustc helloworld.rs
- 実行
./helloworld
Hello, world!
参考
サブタスクなコードレビュー考察
他チームのコードレビューを依頼された場合の考察。
他チームのコードレビュー依頼がある状況と対応方法
該当チームのリードエンジニアのリソースが足りていない
一時的なリソース貸出に当たると思われるので、期日やレビュー範囲などの合意が取れればOKだと考える。
該当チームにリードエンジニア( が存在しない || の実力不足 )
ある一定以上の品質達成が該当チームだけでは成し得ない状況だと思われる。
この状態でのレビューは想像以上に高いコストとなることもあり、
最悪、何度も発生する差し戻しなどで、プロジェクト全体の進捗が滞ってしまう事もありえる。
そうならないように、品質的にはNGでもレビューOKとするルールを設定する必要があると考る。
例えば、以下のルールを設定する。
- 一定の差し戻し回数を超えたものに関してはレビューOKとする
- レビューは行うが、修正は行わないものとする
- 今後の開発時に意識してほしい観点を伝えることを目的としたレビュー
まとめ
他チームのコードレビューは、レビューに関するルールの合意をとって実施するべき。