Rustとは
- Type safety
- Memory safety
- Concurrency and performance
にフォーカスした言語とのこと。なるほどちょっと面白そうね、ということで触ってみます。
Rustのインストール
curl -s https://static.rust-lang.org/rustup.sh | sudo sh
結構ダウンロードまでかかる・・・
自動でRustとその上のパッケージ管理システムであるCargoも入るみたいです。
mypc:examples shot$ rustc --version
rustc 0.13.0-nightly (ffc111889 2014-12-12 21:07:19 +0000)
mypc:examples shot$ cargo --version
cargo 0.0.1-pre-nightly (0f6667c 2014-12-08 21:19:01 +0000)
Cargoはここみると早そう。
http://doc.crates.io/guide.html
Hello World
下記サイトで色々サンプルがあって、まずはここを写経。
http://rustbyexample.com/
コードも全部公開しているので、ひとまずclone.
git clone https://github.com/rust-lang/rust-by-example.git
まずはHello World. fnがファンクション(macroと呼ぶみたい)のようですねー。ファンクションの呼び出しは!を使うようです。
// This is the main function
fn main() {
// The statements here will be executed when the compiled binary is called
// Print text to the console
println!("Hello Rust!");
}
実行バイナリはrustcでコンパイルして生成するようです。やってみましょう。
mypc:hello shot$ rustc hello.rs
mypc:hello shot$ ./hello
Hello Rust!
Formatted Print
次はprint系マクロで整形しながら出力してみます。
fn main() {
// 普通のprintの呼び出し
print!("January has ");
// {}でプレースホルダーが使える。型の話は次で。iはintegerぽい
println!("{} days", 31i);
println!("-----------------")
// {0}, {1}のような数値つきプレースホルダーも使える
println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob");
// 名前付きはわかりやすくて、嬉しい
println!("{subject} {verb} {predicate}",
predicate="over the lazy dog",
subject="the quick brown fox",
verb="jumps");
// `:`で特殊フォーマットみたいだけど、今の所よくわかってない
println!("{} of {:b} people know binary, the other half don't", 1i, 2i);
// プレースホルダーと引数の数があってないとエラーになる
println!("My name is {0}, {1} {0}", "Bond");
}
rustcでコンパイルエラーを出すと、きちんとしたエラーが出ますね。うん、わかりやすい。
mypc:print shot$ rustc print.rs
print.rs:25:14: 25:39 error: invalid reference to argument `1` (there is 1 argument)
print.rs:25 println!("My name is {0}, {1} {0}", "Bond");
^~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
というわけでエラーの出るコードをなおして、実行。
mypc:print shot$ rustc print.rs
mypc:print shot$
mypc:print shot$ ./print
January has 31 days
-----------------
Alice, this is Bob. Bob, this is Alice
the quick brown fox jumps over the lazy dog
1 of 10 people know binary, the other half don't
My name is Bond, James Bond
Literals and Operators
次はリテラルと演算子。
fn main() {
// 普通の足し算
println!("1 + 2 = {}", 1u + 2);
// 型をきちんとあわせないと結果が、という例
println!("1i - 2 = {}", 1i - 2);
println!("1u - 2 = {}", 1u - 2);
// &&, ||, !などのブーリアンオペレーターが使える
println!("true AND false is {}", (true && false));
println!("true OR false is {}", true || false);
println!("NOT true is {}", !true);
// ビット演算
println!("0011 AND 0101 is {:04b}", 0b0011u & 0b0101);
println!("0011 OR 0101 is {:04b}", 0b0011u | 0b0101);
println!("0011 XOR 0101 is {:04b}", 0b0011u ^ 0b0101);
println!("1 << 5 is {}", 1u << 5);
println!("0x80 >> 2 is 0x{:x}", 0x80u >> 2);
// アンスコでリーダビリティあがると言ってるけど、ちょっと微妙かな・・・
println!("One million(1_000_000) is written as {}", 1_000_000u);
}
まあ一般的な演算子が使えそうです。
Variables
ちょっとコードをなおして、変数渡しだけではなく、もう一度代入してみた(letってきてるのでアトミックで出来ない想定)。
fn main() {
//letで変数を定義
let an_integer = 1u;
let a_boolean = true;
let unit = ();
// 変数のコピー
let copied_integer = an_integer;
println!("An integer: {}", copied_integer);
println!("A boolean: {}", a_boolean);
println!("Meet the unit value: {}", unit);
an_integer = 123u;
println!("An integer: {}", copied_integer);
// コンパイラーが利用していない変数は警告。アンスコつけると、警告しないみたい
let _unused_variable = 3u;
let noisy_unused_variable = 2u;
}
コンパイルしてみると、出来ない。エラーも丁寧な印象で、再アサインした所と元の設定のところの2つが出る。実用レベルはまだわかんないけど、良い感じです。
mypc:variables shot$ rustc variables.rs
variables.rs:14:2: 14:19 error: re-assignment of immutable variable `an_integer`
variables.rs:14 an_integer = 123u;
^~~~~~~~~~~~~~~~~
variables.rs:3:9: 3:19 note: prior assignment occurs here
variables.rs:3 let an_integer = 1u;
^~~~~~~~~~
error: aborting due to previous error
というわけでコードで再アサインしているところを取り除いてコンパイル・実行。unusedな変数もきっちり捕捉されていてよい感じです。_で始めた変数はひっかからないみたい。
5cf93891c702:variables ohtani$ rustc variables.rs
variables.rs:16:9: 16:30 warning: unused variable: `noisy_unused_variable`, #[warn(unused_variables)] on by default
variables.rs:16 let noisy_unused_variable = 2u;
^~~~~~~~~~~~~~~~~~~~~
というわけで基本は変数はletで定義して、イミュータブルであることがわかりました。というわけで、変更可能な変数はmutで定義するようです。
fn main() {
//変更不可能
let _immutable_variable = 1i;
//mut定義で変数を変更可能に出来る
let mut mutable_variable = 1i;
println!("Before mutation: {}", mutable_variable);
// 変更
mutable_variable += 1;
println!("After mutation: {}", mutable_variable);
// もちろんlet定義だけの変数は変更不可能
//_immutable_variable += 1;
}
mypc:mut shot$ rustc mut.rs
mypc:mut shot$ ./mut
Before mutation: 1
After mutation: 2```
スコープと変数宣言のところはなんか当たり前だったので、省略で。
- スコープが異なる変数は使えない
- 変数の初期化する前には変数は使えない
くらいかなあ。変数は宣言時にもう利用する状態にしとけってことで。
Types
型はRustで決められたものの他に自分でエイリアスのように作る事も出来るけど、新しい型が作られる訳じゃなさそう。基本的には明示的なキャストをas xxxで実施するみたい。型を初期で決めないようにも定義できるみたいだけど、あとで決定できる要素(つまり値の代入)が無い場合には、コンパイルエラーになるみたい。Inferenceって言うみたいっす。
// `NanoSecond` is a new name for `u64`
type NanoSecond = u64;
type Inch = u64;
// Use an attribute to silence warning
#[allow(non_camel_case_types)]
type uint64_t = u64;
// TODO ^ Try removing the attribute
fn main(){
// タイプ側にアノテーションがついている場合
let a_float: f64 = 1.0;
// 値側にタイプをつける
let mut an_integer = 5i;
// mutのついた変数でも型を変更する事は出来ない
// an_integer = true;
println!("{}", a_float);
println!("{}",an_integer);
let decimal = 65.4321_f32;
// 暗黙的なキャストは許可されない
// let integer: u8 = decimal;
// 明示的なキャスト
let integer = decimal as u8;
let character = integer as char;
println!("Casting: {} -> {} -> {}", decimal, integer, character);
// エイリアス
// `NanoSecond` = `Inch` = `uint64_t` = `u64`
let nanoseconds: NanoSecond = 5 as uint64_t;
let inches: Inch = 2 as uint64_t;
println!("{} nanoseconds + {} inches = {} unit?",
nanoseconds,
inches,
nanoseconds + inches);
let xx = 1u8;
let yy = 2u;
let zz = 3f32;
let i = 1;
let f = 1.0;
println!("size of `x` in bytes: {}", std::mem::size_of_val(&xx));
println!("size of `y` in bytes: {}", std::mem::size_of_val(&yy));
println!("size of `z` in bytes: {}", std::mem::size_of_val(&zz));
println!("size of `i` in bytes: {}", std::mem::size_of_val(&i));
//これをコメントアウトすると、i, fが型が決まらないため、エラー
let _constraint_i = xx + i;
let _constraint_f = zz + f;
}
mypc:type shot$ rustc type.rs
type.rs:48:6: 48:11 error: cannot determine a type for this local variable: cannot determine the type of this integer; add a suffix to specify the type explicitly [E0102]
type.rs:48 let i = 1;
^~~~~
error: aborting due to previous error
Expression
Rustではほぼ全部がエクスプレッションなので、値を返す事になっています。なので、式として書いても最後には値を返す事になるのですが、返したくない場合は最後をセミコロンで終えると()を返すみたい。うう、これはまりそうだな・・・
Expressionは値を返すもので、Statementは値を返さないものという区分けのようだ。letがStatementで他はExpressionであると。この概念は大事だな(正確にはDeclaritive statement vs Expression statement)。
ちょっとコードで。
fn main(){
let x = 5u;
//最後の行がyに割当
let y = {
let xx = x * x;
let xxx = xx * x;
xxx + xx + x
};
//;をつけると、サプレスされて()が割当
let z = {
2 * x;
};
println!("x is {}", x);
println!("y is {}", y);
println!("z is {}", z);
let n2 = if n == 5i { n * n } else { n };
//let n2:int = if n == 5i { n * n; } else { n; };
println!("{}", n2);
}
先ほどのセミコロンの話にもどってくるけど、仮に上のコードで
let n2:int = if n == 5i { n * n; } else { n; };
をコメントアウトをとって使おうとすると、アノテーションとしてはintを期待しているのに、ifクローズの中でセミコロンがあるため、サプレスされて()が返る。Rustの中で、()はunitと呼ばれ、特殊な型であるみたい。というわけで下記のようなコンパイルエラーになる。
mypc:expression shot$ rustc expr.rs
expr.rs:40:15: 40:48 error: mismatched types: expected `int`, found `()` (expected int, found ())
expr.rs:40 let n2:int = if n == 5i { n * n; } else { n; };
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
うーん、このケースの使いどころはまだわかっていない・・・
ここまでのまとめ
Rustをここまで触ってみて、なかなかシンプルでType safetyもある言語だということはわかった。もうちょっと触ってみないと真価はわからないかもだけど。長くなってきたのでこの辺で切ってみる。