Rustを触ってみよう
冬休みの間、何かプログラミングの勉強をしたくなったので、前から気になっていたRustを勉強してみることにした。勉強しながら感じたことをつらつらと書いてみました。特にまとまっていません。
以下のURLを参照しながら勉強しています。 https://doc.rust-lang.org/book/ch01-02-hello-world.html
まずは Hello, World!
fn main() {
println!("Hello, world!");
}
試しにエンドコロン削除 特に問題ないし、
println!
が気になったので説明を読むとこれはマクロのコールだと、
ためしに!
を削除してみる、 エラーになった。
error[E0423]: expected function, found macro `println`
--> main.rs:2:5
|
2 | println("Hello World!");
| ^^^^^^^ help: use `!` to invoke the macro: `println!`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0423`.
コンパイラからの情報見るに関数期待していてマクロがきたと言っているので、println
はマクロとして先に定義しているのか、それとも記法が違うのか。Ch19で説明とあるから一旦従う
Hello, Cargo!
先程は rustc
でコンパイルしたが、rust は cargo というマネジメントツールを使うみたい。以下のようにやることでプロジェクトなんかも作れちゃう。
cargo new hello_world
cd hello_world
$ tree -a
.
├── Cargo.toml
├── .git
│ ├── config
│ ├── description
│ ├── HEAD
│ ├── info
│ ├── objects
│ └── refs
├── .gitignore
└── src
└── main.rs
git情報が既に入っているのが、いいですね。 src ディレクトリの中に Hello, World! が既に入っているのもいいですね。
Programming a Guessing Game
Guessing Game を作りながらプログラミングを学ぶ。
let foo = 5; // immutable
let mut bar = 5; // mutable
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
usingを使うことでパッケージを短縮できるが、必須ではない。このあたりはJavaチック。
&mut guess
For now, all you need to know is that like variables, references are immutable by default. Hence, you need to write &mut guess rather than &guess to make it mutable
&の参照型はデフォルトでは変更不可。したがって&mut guess
と書くことで変更可能にすることが出来る。 &guess
だと変更不可。
次のお題は
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => println!("You win!"),
}
を追加することで入力と生成された文字との比較をして推測ゲームを完成させる。
上が比較なのはわかるし、Ordering::Lessとかの後の=>
はラムダなのかなでも、カールブラケットの中に入れているが不思議。
match
は多言語での switch
でっ結果はコンパイルエラー、型不一致。 入力値はStringなので、という話 じゃ入力値を数字に変換しましょう
let guess: u32 = guess.trim().parse()
.expect("Please type a number!");
なんか不思議な書き方。
We create a variable named guess. But wait, doesn’t the program already have a variable named guess? It does, but Rust allows us to shadow the previous value of guess with a new one. This feature is often used in situations in which you want to convert a value from one type to another type. Shadowing lets us reuse the guess variable name rather than forcing us to create two unique variables, such as
guess_str
andguess
for example. (Chapter 3 covers shadowing in more detail.)
でとりあえず、変数を新しく用意する必要はないと、このへんはjavascript的な書き方できるけど、きっちり裏では型もってそうなイメージ。
let guess: u32
とりあえずここがわからん
u32
: unsigned 32 bit numberi32
: 32 bit number
guess: u32
って何かなと思ったら。
let guess: u32 = guess.parse().unwrap();
と let guess = guess.parse::<u32>();
は等価だそうだ。後者のほうが私はわかりやすいけど、慣れたら前者のほうが早くかけるし、式の頭で何が行われるかわかりやすいのかな。
結局 : i32
はただのrustの型宣言のやり方。
// --snip--
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
やはり =>
はラムダ式的なものか、でもLessとかの値がどこからきたのかわからない。 <-fn cmp
の戻り値からきてる。
// --snip--
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
// --snip--
型変換のところで間違えた入力があった場合の変更で、
そうかmatchとセットになっていることがわかるので、
parse 関数の戻り値を match {};
で受けてるようだ。
Common Programming Concepts
Variables and Mutability
基本はimmutableつまりconst
がデフォルトなのかと思ったら。const
もあった。
let
はあくまでmut
を使えるよと言うだけみたい。
fn main() {
const MAX_POINTS: u32 = 100_000;
}
100_000
地味に便利。100,000と同じ感覚
Shadowing
変数の使い回しが出来るってことだけど、注意点はlet
がいるってことか。
再宣言でオーバーライド出来るってことね。
fn main() {
let x = 5;
let x = x + 1;
let x = x * 2;
println!("The value of x is: {}", x);
}
fn main() {
let mut x = 5;
x = x + 1;
x = x * 2;
println!("The value of x is: {}", x);
}
上2つは同じ結果になるけど、前者はShadowingを使っている、以下は変数値を書き換えている。
メモリ的には前者かな おそらく Shadowing した瞬間に前の変数はドロップされるので、メモリ的には変わらない。CPU的にはわからない。ベンチマークそのうちしてみる。
あと、
fn main() {
let spaces = " ";
let spaces = spaces.len();
}
はOKだけど、
fn main() {
let mut spaces = " ";
spaces = spaces.len();
}
はだめ、Shadwingは型の再定義はできない。
次に、
fn main() {
// This binding lives in the main function
let long_lived_binding = 1;
// This is a block, and has a smaller scope than the main function
{
// This binding only exists in this block
let short_lived_binding = 2;
println!("inner short: {}", short_lived_binding);
// This binding *shadows* the outer one
let long_lived_binding = 5_f32;
println!("inner long: {}", long_lived_binding);
}
// End of the block
// Error! `short_lived_binding` doesn't exist in this scope
println!("outer short: {}", short_lived_binding);
// FIXME ^ Comment out this line
println!("outer long: {}", long_lived_binding);
// This binding also *shadows* the previous binding
let long_lived_binding = 'a';
println!("outer long: {}", long_lived_binding);
}
上の例のアウトプットは
inner short: 2
inner long: 5
outer long: 1
outer long: a
となる、スコープ内で定義したShadowはスコープ外では定義されていないので反映されない。
Data Types
多分何回か見に来ることになるからリンクを貼っておく。
https://doc.rust-lang.org/book/ch03-02-data-types.html
気になったのは
- Integer Type に
arch
があること。pythonみたいにどっちになるかもって考えなくても良いし、環境毎に最大値とかとりやすくていい - Binaryの書き方が
0b1111_0000
- Compound Types に Tupleがある
Functions
関数の戻り値の型宣言が面白い
fn five() -> i32 {
5
}
fn plus_one(x: i32) -> i32 {
x + 1
}
return 値はセミコロンなし。 セミコロンありでも無しでも動くと思っていたら。
fn plus_one(x: i32) -> i32 {
x + 1
}
は問題なく動くいて、もし5が入ってきたら、6を返すけど、
fn plus_one(x: i32) -> i32 {
x + 1;
}
は空のタプルを返す。
Control Flow
きちんと for-eachある
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
}
Wrap up
前から触りたかったけど、触る機会が無かったRustを思い立って触っただけ、 今のところは少し癖があるけど結構好きかもしれない。