Rustの勉強(その10)
何かを作ることをあまり目的とせず、英語のドキュメントを読書する的なアプローチで勉強を進めております。Feedlyの未読をキュレーションしてBitBarに、とかは考えてるんですが。というかその前にBitBarちゃんと使えよ、とか。
mao-instantlife.hatenablog.com
コメントとドキュメントコメント
//
がコメントで、 ///
または //!
がドキュメントコメントとなっています。ドキュメントコメントにはマークダウン書式が有効で、rustdocツールでhtmlを生成時にスタイルが適用されるらしいです。rustdocツールのドキュメントをまだ読んでいないため、スタイルをカスタマイズできるかどうかはわかりません。
ただ、英語力がなくて //!
をどういう時に使うか理解できませんでした。「アイテムを含んでいる」という表現があったんですがどういうことでしょうか?少なくとも現状は ///
があれば事足りそうなので、忘れることにしますが(ヲイ
if
Scalaと同じくifは式なので、以下のような書き方ができます。
let x = 5; let y = if x==5 { "five" } else if x ==6 { "six" } else { "unknown" };
型は合わせないといけないようです。例えば else if
以降をそれぞれ別の型を返すようなコードにすると、以下のようなコンパイルエラーになります。
src/main.rs:4:35: 4:93 error: if and else have incompatible types: expected `_`, found `()` (expected integral variable, found ()) [E0308] src/main.rs:4 let y = if x == 5 {"five"} else if x == 6 { 6 } else { println!("x is not six or five!") }; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
エラーの注釈からして、バインドする変数に型が指定されていない場合は最初に出てきた型で変数の型が推論される模様。それを受ける変数の型を指定していればエラーが変わります。
src/main.rs:4:26: 4:32 error: mismatched types: expected `i32`, found `&'static str` (expected i32, found &-ptr) [E0308] src/main.rs:4 let y:i32 = if x == 5 {"five"} else if x == 6 { 6 } else { println!("x is not six or five!") }; ^~~~~~ src/main.rs:4:26: 4:32 help: run `rustc --explain E0308` to see a detailed explanation src/main.rs:4:62: 4:95 error: mismatched types: expected `i32`, found `()` (expected i32, found ()) [E0308] src/main.rs:4 let y:i32 = if x == 5 {"five"} else if x == 6 { 6 } else { println!("x is not six or five!") }; ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ src/main.rs:4:62: 4:95 note: in this expansion of println! (defined in <std macros>) src/main.rs:4:62: 4:95 help: run `rustc --explain E0308` to see a detailed explanation error: aborting due to 2 previous errors Could not compile `study_for_rust`.
ループ
loop
または while
または for
の3種類で、使い方や構文はだいたい他の言語と似てます。 for
の書式もJavaでおなじみのあの2種類っぽい感じで用意されています。ループを抜ける、または次のループに移行するのが break
や continue
なのも同じ。ただまあ、Scalaに少し慣れてきた頃から思ってたんですが、リスト操作があればあんまり使わないかも。Javaもなるべくループ使いたくない症候群になっているので。
Enumerate
これは変わっているというか地味に嬉しいやつです。
for (i, j) in (5..10).enumerate() { println!("i = {} and j = {}", i, j); }
上記はイテレータでも可能で、インデックス(0スタート)と値をタプルとして両方バインドしてくれます。実例をすぐに思い出せないけど、このパターン結構使ってる記憶があるので、地味にありがたい。
ラベル
'outer: for... 'inner: for...
のようにループにラベルをつけて break
や continue
に対して適用するループを指定することが可能です。ラベルに飛びたい時は break ラベル名('を含む)
みたいな感じにかきます。
オーナーシップ
コンパイル機能としてはRustのユニークなものの一つで、Rust開発者はこれに親しまなければならない
、とのことで、大体のRust初心者はこの概念に悪戦苦闘している、とのこと。かなり脅されていますが、メモリ安全に寄与するための機能なので譲る気はない模様です。
なんでこんなにめんどくさいことをやっているか、という言い訳めいたもの
Rustはメモリの安全性とスピードを最も重視しているから
だそうです。Rustのメモリコストには貢献するけど、学習コストは高いよ、らしい。
これを抑えることで、メモリの安全性は基本的にコンパイル時に解決出来て実行時には考慮しなくていいらしいのですが、これってコンパイルできていればメモリリークの心配はないよ、ということなんでしょうか。後、また出てきましたね、ゼロコスト抽象(zero-cost abstractions)。まだこの言葉の実態がよくつかめていません。
変数のオーナーシップ
変数は、「誰にバインドされたのか」というプロパティを持ち、そのバインドのスコープから外れたら、Rustはリソースを解放する仕様です。そのスコープとして単純なのはブロック。
Move Semantics
Rustは値は一つの変数にバインドされることを保証していて、
let v = vec[1, 2, 3]; let v2 = v;
のような再バインドはできるけど、変数のバインドが v2
に移ることを指して、 v
からは外れます。
したがって、
let v = vec[1, 2, 3]; let v2 = v; println!("v[0] is {}", v[0]);
はコンパイルエラーになります。
オーナーシップを受け取る関数を定義した場合
も同じくエラー、
fn take(v: Vec<i32>) { // what happens here isn’t important. } let v = vec![1, 2, 3]; take(v); println!("v[0] is: {}", v[0]);
って書いてあるんですが、オーナーシップを受け取る関数ってどういうこと?
よくわからなくなったところで
以下次号。。。