https://doc.rust-lang.org/book/second-edition/
Apache License Version 2.0
Common Programing Concepts
多くの言語に共通する機能がRustではどう実現されているか
3.1 Variables and Mutability
bash
cargo new
で実験するプロジェクトを作る.
Rustでは,変数はデフォルトではimmutableである.つまり,一度変数に値を代入するとその後変更できない.
src/main.rs
fn main() {
let x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
cargo runでビルドして実行してみると,
error[E0384]: re-assignment of immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| - first assignment to `x`
3 | println!("The value of x is: {}", x);
4 | x = 6;
| ^^^^^ re-assignment of immutable variable
とエラーが出る.こうして,Rustはプログラマが予期しない代入を防いでいる.
しかし,いちいち新しい変数を用意するのは不便なときもある.そこで
src/main.rs
fn main() {
let mut x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
とすると,
Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.34 secs
Running `target/debug/variables`
The value of x is: 5
The value of x is: 6
このようにletによる変数宣言と同時にmutによって変数をmutableにすることが出来る.
3.2 Differences Between Variables and Constants
定数とimmutableな変数は似ているが,いくつか違いがある.
1. immutable変数はmutによってmutableにできる.
2. 定数にはプログラムのどこからでも参照できる.
3. 定数はプログラム実行時に計算される値は代入できない.つまり,コンパイル時に決定される値しか代入できない.
Shadowing
新しい変数をすでにある変数と同じ名前で宣言することをshadowingという.このとき,新しい変数ははじめの変数をshadowしているといい,はじめの変数は新たな変数にshadowされたという.
src/main.rs
fn main() {
let x = 5;
let x = x + 1;
let x = 2 * x;
println!("The value of x is: {}", x);
}
shell
ren@ren-ThinkCentre-Edge72:~/Projects/variables$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/variables`
The value of x is: 12
mutableな変数でも,宣言したあとで型を変えることはできない(静的型付).
src/main.rs
fn main() {
let mut spaces = " ";
spaces = spces.len();
}
shell
ren@ren-ThinkCentre-Edge72:~/Projects/variables$ vim src/main.rs
Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
error[E0308]: mismatched types
--> src/main.rs:3:14
|
3 | spaces = spaces.len()
| ^^^^^^^^^^^^ expected &str, found usize
|
= note: expected type `&str`
found type `usize`
変数名を型を変えながら使いまわしたいとき,shadowingが有効.
Data Types
Rustは静的型付言語だが,変数宣言時に型を指定しなくても,コンパイラが推測してくれる.しかし,プログラマが型を指定しなければならない場合もある.
Scalar Types
整数,浮動小数点数,真理値,文字は長さが固定されている変数であり,scalarという.
Integer Types (自然数型)
自然数型は符号のあるなしとビット長によっての10種類がある.ビット長は8, 16, 32, 64, そしてアーキテクチャ依存のsize長がある.符号付きならi,符号なしならuを頭につけて,ビット長を尾につけてその型を指定できる.例えば符号なし32bitなら u32 である. Rustのデフォルトはi32で,64bit CPUでもこれが最も早く処理できることが多い.
src/main.rs
fn main() {
let x:i32 = 2147483648;
println!("x is {}", x);
}
shell
Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
warning: literal out of range for i32
--> src/main.rs:2:13
|
2 | let x = 2147483648;
| ^^^^^^^^^^
|
= note:
Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
Running `target/debug/variables`
x is -2147483648
オーバーフローする値を代入しても,オーバーフローしないような型にしてくれるわけではない.コンパイルエラーにはならず,コンパイル時に警告される.
Floating-Point Types (浮動小数点数型)
小数点以下が表現できる.32bit長(f32)と64bit長(f64)があるが,速さはほとんど同じなので64bit長がよく使われる.
Numeric Operations
算術的演算はpythonと同じ記法で行える.しかし,自然数と浮動小数点数の演算を直接行うことはできない.
src/main.rs
fn main() {
let a = 3;
let b = 3.0;
let c = a + b;
println!("int plus float: {}", c);
}
shell
Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
error[E0277]: the trait bound `{integer}: std::ops::Add<{float}>` is not satisfied
--> src/main.rs:4:13
|
4 | let c = a + b;
| ^^^^^ the trait `std::ops::Add<{float}>` is not implemented for `{integer}`
|
= note: no implementation for `{integer} + {float}`
error: aborting due to previous error
error: Could not compile `variables`.
To learn more, run the command again with --verbose.
The Boolean Type
真理値はboolによって指定できて,trueかfalseの値だけを取る.
型を指定せずとも,trueかfalseを宣言時に代入すれば,コンパイラがbool型と推測する.
The Character Type
1文字を表現できるスカラー型はcharで,内部ではUnicodeを保持する.
Compound Types
長さが2以上ある変数の型.tupleとarrayがある.
Grouping Values into Tuples
必ずしも共通でない型の値たちを一つの変数に代入する型.カッコでくくり,コンマで要素を区切る.タプル定義でも型推測は行われるが,プログラマが型を指定することも出来る.タプルの要素を取り出す方法は2つある.
1. pattern matching: タプルの長さと同じ個数の変数にタプルを代入し,求めたい要素の入っている変数にアクセスする
2. index: タプルの一番左の要素を0として,求めたい変数のインデックスがiなら,tupple.iでアクセスできる.
src/main.rs
fn main() {
let tup = (500, 6.4, 1);
let typed_tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z ) = tup;
println!("The value of y is: {}", y);
println!("The value of y is: {}", typed_tup.1);
}
shell
Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
~ 略 (一度も使っていない変数があると怒られる) ~
Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
Running `target/debug/variables`
The value of y is: 6.4
The value of y is: 6.4
Arrays
tuppleと違って,すべての要素が同じ型でなければならない.また,長さは固定である.定義するときは’[ ]’でくくって、コンマで要素を区切る.arrayもtuppleのようなインデックスが振られているが,アクセスするにはarray[i]と書く.arrayの長さに反するインデックスでアクセスしようとすると,コンパイルエラーは起きないが実行時にpanicする.
src/main.rs
fn main() {
let a = [1, 2, 3, 4, 5];
let index = 10;
let element = a[index];
println!("The value of element is: {}", element);
}
shell
Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
Running `target/debug/variables`
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.
3.3 How Functions Work
新しくfunctionsプロジェクトを作る.
関数はfn <関数名>() {}で定義され,{}の中身が実際に実行される.2つ以上関数をつくって,片方からもう片方の関数を利用することが出来る.
src/main.rs
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
shell
Compiling functions v0.1.0 (file:///home/ren/Projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.36 secs
Running `target/debug/functions`
Hello, world!
Another function.
Function Parameters
関数の引数は,fn name () {}の()の中で指定する.引数の名前だけでなく,型も同時に指定しなければならない.
src/main.rs
fn main() {
another_function(1, 2);
}
fn another_function(x:i32, y) {
println!("x is {}", x);
println!("y is {}", y);
}
shell
Compiling functions v0.1.0 (file:///home/ren/Projects/functions)
error: expected one of `:` or `@`, found `)`
--> src/main.rs:6:29
|
6 | fn another_function(x:i32, y) {
| ^ expected one of `:` or `@` here
error[E0425]: cannot find value `y` in this scope
--> src/main.rs:8:25
|
8 | println!("y is {}", y);
| ^ did you mean `x`?
error: aborting due to 2 previous errors
error: Could not compile `functions`.
Statements and Expressions
Rustはepxressionベースの言語である.statementとexpressionの違いは,後者が返り値を返すが,前者はそうでないという点である.
変数宣言や関数定義はstatementであるから,返り値を返さない.よって
let x = (let y = 6);
はコンパイルできない.
一方で,expressionはプログラムの大半を占めている.
fn main() {
let x = 5;
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
最後に;のない行はexpressionであり,;を加えるとstatementになる.
Functions with Return Values
関数はfn name () -> type {}によって返り値を返すようにすることが出来る.返される値は,{}の中の最後のexpressionの値である.
src/main.rs
fn five() -> i32 {
4;
5
}
fn main() {
let x = five();
println!("The value of x is: {}", x);
}
shell
Compiling functions v0.1.0 (file:///home/ren/Projects/functions)
Finished dev [unoptimized + debuginfo] target(s) in 0.34 secs
Running `target/debug/functions`
The value of x is: 5
返り値が返されると定めた関数にepxressionがない場合,コンパイルエラーになる.
src/main.rs
fn plus_one(x:i32) -> i32 {
x + 1;
}
fn main() {
let x = plus_one(5);
println!("The value of x is: {}", x);
}
shell
Compiling functions v0.1.0 (file:///home/ren/Projects/functions)
error[E0308]: mismatched types
--> src/main.rs:1:27
|
1 | fn plus_one(x:i32) -> i32 {
| ___________________________^
2 | |
3 | | x + 1;
4 | | }
| |_^ expected (), found i32
|
= note: expected type `()`
found type `i32`
help: consider removing this semicolon:
--> src/main.rs:3:8
|
3 | x + 1;
| ^
error: aborting due to previous error
error: Could not compile `functions`.
To learn more, run the command again with --verbose.
Control Flow
条件分岐とループ
if expression
if condition_1 {codeblock_1} else if condition_2 {codeblock_2} … else {codeblock_0}
condition_1から順にconditionがtrueか調べられ,condition_kがtrueならcodeblock_kが実行され,すべてのconditionがfalseなら,codeblock_0が実行される.elseがない場合,何も実行されない.codeblockたちをifのarmと呼ぶことがある.conditionは,評価するとBoolを返すexpressionでなければならない.
src/main.rs
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
shell
Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.35 secs
Running `target/debug/branches`
condition was true
Using if in a let statement
ifはexpressionなので,letを使った変数宣言の右辺に置くことが出来る.
src/main.rs
fn main() {
let condition = true;
let number = if condition {
5
} else {
6
};
println!("The value of number is: {}", number);
}
shell
Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.34 secs
Running `target/debug/branches`
The value of number is: 5
ここで,変数numberにはconditionの真偽によって{5}, {6}のコードブロックの返り値が代入されるが,このコードブロックたちの返す値の型が異なっていると,numberの型が推測できず,コンパイルエラーになる.
src/main.rs
fn main() {
let condition = true;
let number = if condition {
5
} else {
"six"
};
println!("The value of number is: {}", number);
}
shell
Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
error[E0308]: if and else have incompatible types
--> src/main.rs:3:16
|
3 | let number = if condition {
| ________________^
4 | | 5
5 | | } else {
6 | | "six"
7 | | };
| |_______^ expected integral variable, found &str
|
= note: expected type `{integer}`
found type `&str`
error: aborting due to previous error
error: Could not compile `branches`.
Repetition with Loops
Rustはloop,while,forの3種類のループを実装している.
Repeating Code with loop
明示的にbreakされるまでループを続ける.
src/main.rs
fn main() {
loop {
println!("again!");
}
}
shell
again!
Conditional Loops with while
ループ中毎回条件を評価し,条件がfalseになったらループを中断する.
src/main.rs
fn main() {
let mut number = 3;
while number != 0 {
println!("{}", number);
number = number - 1;
}
println!("LIFTOFF!!!");
}
shell
Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.35 secs
Running `target/debug/branches`
3
2
1
LIFTOFF!!!
Looping Through a Collection with for
tuppleやarrayの要素ごとに処理を実行したいとき,forが使える.また,決まった回数ループしたい場合,Range型にforを使って実現できる.
src/main.rs
fn main() {
for number in (1..4).rev() {
println!("{}!", number)
}
println!("LIFTOFF!!!");
}
shell
Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
Finished dev [unoptimized + debuginfo] target(s) in 0.38 secs
Running `target/debug/branches`
3!
2!
1!
LIFTOFF!!!
Summary (練習)
1. 華氏を摂氏に変換するプログラムを書け.
2. n番目のフィボナッチ数を計算するプログラムを書け
- src/main.rs
use std::io;
fn main() {
println!("Input F temperature!");
let mut f = String::new();
io::stdin().read_line(&mut f).expect("Failed to read line.");
let f: f64 = f.trim().parse().expect("Please type a number!");
let c = (f - 32.0) * 5.0 / 9.0;
println!("Fahrenheit {} is Celsius {}", f, c);
}
shell
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/branches`
Input F temperature!
911
Fahrenheit 911 is Celsius 488.3333333333333
- src/main.rs
use std::io;
fn main() {
println!("Input integer n!");
let mut n = String::new();
io::stdin().read_line(&mut n).expect("Failed to read line.");
let n: u32 = n.trim().parse().expect("Plase type an intger!");
let ans = fibb(n);
println!("The kth fibbonacci number is: {}", ans);
}
fn fibb(k: u32) -> u32 {
let ans:u32 = if k == 1 {
1 } else if k == 2 {
1 } else { fibb(k-1) + fibb(k-2)};
ans
}
shell
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/branches`
Input integer n!
12
The kth fibbonacci number is: 144