Rust を始めた雑感
はじめに
新年度でアンニュイな気持ちになったので、気分を変えたくなりました。
こういうときは何かに没頭するのが一番ということで、難しいと噂の Rust を始めようと思いました。
このスクラップでは、 Rust を触った雑感をつらつらと書いていきます。
インストール
最初の鬼門ですね。
公式サイトよりRUSTUP-INIT.EXE(64-BIT)をインストールしようとしたら、C++とWindows 10用のパッケージが必要だと言われました。
Visual studioで該当のパッケージをインストールしましたが、まだ警告が消えない。
困ったときは Google is friend
素晴らしいインストラクションを見つけて、必要なパッケージをインストールしました(画像は引用元より転載しています)。
(画像は引用元より転載しています)
やっと本番 rustup インストール
RUSTUP-INIT.EXE(64-BIT)のインストールに再挑戦しました。
前節のインストラクションに従って、1 の Default を選択しました。
あっという間にインストール完了しました。
VS codeのインストール
便利らしいので、VS codeをインストールして、下記の2つの拡張機能もインストールしました。
- rust-analyzer 拡張機能
- CodeLLDB 拡張機能
VS codeは動作が軽快で良いですね。
これで、Rustを使う準備が整いました。
Hello World
インストラクションによるとcargo new testproj
という呪文を唱えることで新しいプロジェクトを作成できるようです。
どこにコマンドを打ち込めばよいのだろう
Google is friendということで、画面の上側のNew Terminal
を選択して、画面下部にTerminalが出てきました。
プロジェクト作成
呪文cargo new testproj
を唱えて、新しいプロジェクトを生成しました。
プロジェクトファイルの中を見ていくと、main.rsというファイルを見つけました。
Main 関数を格納するファイルだと思います。
Main 関数の上にRunがあるので、これを押すと、プログラムが実行されるようです。
Hello
さっそく実行してみましょう。
……おお、TerminalにHello World ! の文字が出ていますね。
インストールまでの雑感
Visual studio communityをインストールすればそれでOKのC#がおかしいだけで、Rustも手軽に始めることができると感じました。
サンプルコードを見てみる
新しい言語を学ぶときはサンプルコードを眺めるのが一番、ということで本家で紹介されているコードを眺めてみます。
1. Hello World
特にいうことはない感じですね。
// This is a comment, and is ignored by the compiler.
// You can test this code by clicking the "Run" button over there ->
// or if you prefer to use your keyboard, you can use the "Ctrl + Enter"
// shortcut.
// This code is editable, feel free to hack it!
// You can always return to the original code by clicking the "Reset" button ->
// This is the main function.
fn main() {
// Statements here are executed when the compiled binary is called.
// Print text to the console.
println!("01: Hello World!");
}
1.1. Comments
式の間にコメントを挟んでも良いのはコンパイル言語の強みだと思う。
fn main() {
// This is an example of a line comment.
// There are two slashes at the beginning of the line.
// And nothing written inside these will be read by the compiler.
// println!("Hello, world!");
// Run it. See? Now try deleting the two slashes, and run it again.
/*
* This is another type of comment, a block comment. In general,
* line comments are the recommended comment style. But block comments
* are extremely useful for temporarily disabling chunks of code.
* /* Block comments can be /* nested, */ */ so it takes only a few
* keystrokes to comment out everything in this main() function.
* /*/*/* Try it yourself! */*/*/
*/
/*
Note: The previous column of `*` was entirely for style. There's
no actual need for it.
*/
// You can manipulate expressions more easily with block comments
// than with line comments. Try deleting the comment delimiters
// to change the result:
let x = 5 + /* 90 + */ 5; // 式の間にコメントを挟んでも良いのはコンパイル言語の強みだと思う
println!("01: Is `x` 10 or 100? x = {}", x);
}
1.2. Formatted print
文字列出力は難しいですね。
fn main() {
// In general, the `{}` will be automatically replaced with any
// arguments. These will be stringified.
println!("01: {} days", 31);
// Positional arguments can be used. Specifying an integer inside `{}`
// determines which additional argument will be replaced. Arguments start
// at 0 immediately after the format string.
//println!("02: {0}, this is {1}. {1}, this is {0}", "Alice", "Bob");
// As can named arguments.
println!("{subject} {verb} {object}",
object="the lazy dog",
subject="the quick brown fox",
verb="jumps over");
// Different formatting can be invoked by specifying the format character
// after a `:`.
println!("03: Base 10: {}", 69420); // 69420
println!("04: Base 2 (binary): {:b}", 69420); // 10000111100101100
println!("05: Base 8 (octal): {:o}", 69420); // 207454
println!("06: Base 16 (hexadecimal): {:x}", 69420); // 10f2c
println!("07: Base 16 (hexadecimal): {:X}", 69420); // 10F2C
// You can right-justify text with a specified width. This will
// output " 1". (Four white spaces and a "1", for a total width of 5.)
println!("08: {number:>5}", number=1);
// You can pad numbers with extra zeroes,
// and left-adjust by flipping the sign. This will output "10000".
println!("09: {number:0<5}", number=1);
// You can use named arguments in the format specifier by appending a `$`.
println!("10: {number:0>width$}", number=1, width=5);
// Rust even checks to make sure the correct number of arguments are used.
println!("11: My name is {0}, {1} {0}", "Bond", "James");
// FIXME ^ Add the missing argument: "James"
// Only types that implement fmt::Display can be formatted with `{}`. User-
// defined types do not implement fmt::Display by default.
#[allow(dead_code)] // disable `dead_code` which warn against unused module
struct Structure(i32);
// This will not compile because `Structure` does not implement
// fmt::Display.
//println!("This struct `{}` won't print...", Structure(3));
// TODO ^ Try uncommenting this line
// For Rust 1.58 and above, you can directly capture the argument from a
// surrounding variable. Just like the above, this will output
// " 1", 4 white spaces and a "1".
let number: f64 = 1.0;
let width: usize = 5;
println!("12: {number:>width$}");
}
1.2.1. Debug
:? って何だろう?
// Derive the `fmt::Debug` implementation for `Structure`. `Structure`
// is a structure which contains a single `i32`.
#[derive(Debug)]
struct Structure(i32);
// Put a `Structure` inside of the structure `Deep`. Make it printable
// also.
#[derive(Debug)]
struct Deep(Structure);
fn main() {
// Printing with `{:?}` is similar to with `{}`.
println!("01: {:?} months in a year.", 12);
println!("02: {1:?} {0:?} is the {actor:?} name.",
"Slater",
"Christian",
actor="actor's");
// `Structure` is printable!
println!("03: Now {:?} will print!", Structure(3));
// The problem with `derive` is there is no control over how
// the results look. What if I want this to just show a `7`?
println!("04: Now {:?} will print!", Deep(Structure(7)));
}
1.2.2. Display
今更気づいたのですが、Rustではコンパイルすると型推論までしてくれるのですね。
use std::fmt; // Import `fmt`
// A structure holding two numbers. `Debug` will be derived so the results can
// be contrasted with `Display`.
#[derive(Debug)]
struct MinMax(i64, i64);
// Implement `Display` for `MinMax`.
impl fmt::Display for MinMax {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Use `self.number` to refer to each positional data point.
write!(f, "({}, {})", self.0, self.1)
}
}
// Define a structure where the fields are nameable for comparison.
#[derive(Debug)]
struct Point2D {
x: f64,
y: f64,
}
// Similarly, implement `Display` for `Point2D`.
impl fmt::Display for Point2D {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Customize so only `x` and `y` are denoted.
write!(f, "x: {}, y: {}", self.x, self.y)
}
}
fn main() {
let minmax = MinMax(0, 14);
println!("01: Compare structures:");
println!("02: Display: {}", minmax);
println!("03: Debug: {:?}", minmax);
let big_range = MinMax(-300, 300);
let small_range = MinMax(-3, 3);
println!("04: The big range is {big} and the small is {small}",
small = small_range,
big = big_range);
let point = Point2D { x: 3.3, y: 7.2 };
println!("05: Compare points:");
println!("06: Display: {}", point);
println!("07: Debug: {:?}", point);
// Error. Both `Debug` and `Display` were implemented, but `{:b}`
// requires `fmt::Binary` to be implemented. This will not work.
// println!("What does Point2D look like in binary: {:b}?", point);
}
1.2.2.1. Testcase: List
構造体を組み込み関数のコンソール書き出しで表示するための、オーバーライドが書いてあったようだ。
use std::fmt; // Import the `fmt` module.
// Define a structure named `List` containing a `Vec`.
// ベクトル型で構成されたListという名前の構造体……なんなんだこれは
struct List(Vec<i32>);
// ユーザーが定義した構造体に要素を描画する関数
// C#の場合、クラスの{}内にメソッドを記述したが、Rustの場合、別の個所に書くことができるらしい
impl fmt::Display for List {
// & は何か意味があるらしいが、今の時点ではでていない
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Extract the value using tuple indexing,
// and create a reference to `vec`.
let vec = &self.0;
write!(f, "[")?;
// Iterate over `v` in `vec` while enumerating the iteration
// count in `count`.
// for文の条件式の書き方はC#とPythonの合いの子といったところ
for (count, v) in vec.iter().enumerate() {
// For every element except the first, add a comma.
// Use the ? operator to return on errors.
if count != 0 { write!(f, ", ")?; }
write!(f, "{}", v)?;
}
// Close the opened bracket and return a fmt::Result value.
write!(f, "]")
}
}
fn main() {
let v = List(vec![1, 2, 3, 2, 1]);
println!("01: {}", v);
}
1.2.3. Formatting
よくわからない
use std::fmt::{self, Formatter, Display};
struct City {
name: &'static str,
// Latitude
lat: f32,
// Longitude
lon: f32,
}
impl Display for City {
// `f` is a buffer, and this method must write the formatted string into it.
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let lat_c = if self.lat >= 0.0 { 'N' } else { 'S' };
let lon_c = if self.lon >= 0.0 { 'E' } else { 'W' };
// `write!` is like `format!`, but it will write the formatted string
// into a buffer (the first argument).
write!(f, "{}: {:.3}°{} {:.3}°{}",
self.name, self.lat.abs(), lat_c, self.lon.abs(), lon_c)
}
}
#[derive(Debug)]
struct Color {
red: u8,
green: u8,
blue: u8,
}
fn main() {
for city in [
City { name: "Dublin", lat: 53.347778, lon: -6.259722 },
City { name: "Oslo", lat: 59.95, lon: 10.75 },
City { name: "Vancouver", lat: 49.25, lon: -123.1 },
].iter() {
println!("{}", *city);
}
for color in [
Color { red: 128, green: 255, blue: 90 },
Color { red: 0, green: 3, blue: 254 },
Color { red: 0, green: 0, blue: 0 },
].iter() {
// Switch this to use {} once you've added an implementation
// for fmt::Display.
println!("{:?}", *color);
}
}
2. Primitives
よくわからない。
コンパイラ君がたくさん指摘してくれた。
fn main() {
// Variables can be type annotated.
let logical: bool = true;
let a_float: f64 = 1.0; // Regular annotation
let an_integer = 5i32; // Suffix annotation
// Or a default will be used.
let default_float = 3.0; // `f64`
let default_integer = 7; // `i32`
// A type can also be inferred from context.
let mut inferred_type = 12; // Type i64 is inferred from another line.
inferred_type = 4294967296i64;
// A mutable variable's value can be changed.
let mut mutable = 12; // Mutable `i32`
mutable = 21;
// Error! The type of a variable can't be changed.
//mutable = true;
// Variables can be overwritten with shadowing.
let mutable = true;
}
2.1. Literals and operators
数値型の扱いが面白い。
この辺りはモダンな言語だと感じた。
fn main() {
// Integer addition
// 符号なし整数と符号あり整数の和
println!("01: 1 + 2 = {}", 1u32 + 2);
// Integer subtraction
// 符号あり整数と符号あり整数の差
println!("02: 1 - 2 = {}", 1i32 - 2);
// TODO ^ Try changing `1i32` to `1u32` to see why the type is important
// ↑の指示に従うとエラーが出る
// Scientific notation
// e云々の表記を使用できる
println!("03: 1e4 is {}, -2.5e-3 is {}", 1e4, -2.5e-3);
// Short-circuiting boolean logic
// 基本的な論理演算
println!("04: true AND false is {}", true && false);
println!("05: true OR false is {}", true || false);
println!("06: NOT true is {}", !true);
// Bitwise operations
// ビット処理
// 乱数生成時に役立ちそう
println!("07: 0011 AND 0101 is {:04b}", 0b0011u32 & 0b0101);
println!("08: 0011 OR 0101 is {:04b}", 0b0011u32 | 0b0101);
println!("09: 0011 XOR 0101 is {:04b}", 0b0011u32 ^ 0b0101);
println!("10: 1 << 5 is {}", 1u32 << 5);
println!("11: 0x80 >> 2 is 0x{:x}", 0x80u32 >> 2);
// Use underscores to improve readability!
// アラビア数字の見た目が分かりやすくなるように , を使いたいがプログラム言語では使用できない問題を
// 下線で代用している
println!("12: One million is written as {}", 1_000_000u32);
}
2.2. Tuples
// Tuples can be used as function arguments and as return values.
// tupleの順番を入れ替える関数
// 関数の書き方
// C#: (public, static等) 戻り値, メソッド名, 引数
// Rust: fn(関数定義) 関数名, 引数, -> 戻り値
fn reverse(pair: (i32, bool)) -> (bool, i32) {
// `let` can be used to bind the members of a tuple to variables.
let (int_param, bool_param) = pair;
// return文がいらないらしい
(bool_param, int_param)
}
// The following struct is for the activity.
#[derive(Debug)]
struct Matrix(f32, f32, f32, f32);
fn main() {
// A tuple with a bunch of different types.
// 型推論が強力
let long_tuple = (1u8, 2u16, 3u32, 4u64,
-1i8, -2i16, -3i32, -4i64,
0.1f32, 0.2f64,
'a', true);
// Values can be extracted from the tuple using tuple indexing.
// tupleの要素を指定する場合は .i(要素番号) で指定する
println!("01: Long tuple first value: {}", long_tuple.0);
println!("02: Long tuple second value: {}", long_tuple.1);
// Tuples can be tuple members.
// Tupleの中にTupleをいれることができる
let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);
// Tuples are printable.
// 全ての要素を表示したい場合, {:?} と書く
println!("03: tuple of tuples: {:?}", tuple_of_tuples);
// But long Tuples (more than 12 elements) cannot be printed.
//let too_long_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
//println!("Too long tuple: {:?}", too_long_tuple);
// TODO ^ Uncomment the above 2 lines to see the compiler error
// Tupleは異なる型を格納できる
let pair = (1, true);
println!("04: Pair is {:?}", pair);
// Tupleの要素の順番を入れ替える関数の戻り値を表示する
println!("05: Uhe reversed pair is {:?}", reverse(pair));
// To create one element tuples, the comma is required to tell them apart
// from a literal surrounded by parentheses.
// Tupleを生成する時の最後の要素にカンマをつけると、null(?)を追加できる
println!("06: One element tuple: {:?}", (5u32,));
// 1個だけだと単体の変数になる
println!("07: Just an integer: {:?}", (5u32));
// Tuples can be destructured to create bindings.
let tuple = (1, "hello", 4.5, true);
// Tupleの要素を変数に格納する
let (a, b, c, d) = tuple;
println!("08: {:?}, {:?}, {:?}, {:?}", a, b, c, d);
// Tupleライクな構造体
let matrix = Matrix(1.1, 1.2, 2.1, 2.2);
println!("09: {:?}", matrix);
}
2.3. Arrays and Slices
use std::mem;
// This function borrows a slice.
// 配列のスライスは値の貸出であり、型名の前に & をつける
fn analyze_slice(slice: &[i32]) {
println!("First element of the slice: {}", slice[0]);
println!("The slice has {} elements", slice.len());
}
fn main() {
// Fixed-size array (type signature is superfluous).
// 配列を生成する時は、型と要素数を指定する
let xs: [i32; 5] = [1, 2, 3, 4, 5];
// All elements can be initialized to the same value.
// 全て同じ値の配列は [要素の値; 要素数] で指定する
let ys: [i32; 500] = [0; 500];
// Indexing starts at 0.
// 配列の第0番目と第1番目を表示する
println!("01: First element of the array: {}", xs[0]);
println!("02: Second element of the array: {}", xs[1]);
// `len` returns the count of elements in the array.
// 配列の要素数
println!("03: Number of elements in array: {}", xs.len());
// Arrays are stack allocated.
// 配列のメモリ使用量
println!("04: Array occupies {} bytes", mem::size_of_val(&xs));
// Arrays can be automatically borrowed as slices.
// 配列の値を貸し出す
println!("05: Borrow the whole array as a slice.");
analyze_slice(&xs);
// Slices can point to a section of an array.
// They are of the form [starting_index..ending_index].
// `starting_index` is the first position in the slice.
// `ending_index` is one more than the last position in the slice.
// 配列の第1番目から第3番目までをスライスして関数に貸し出す
println!("06: Borrow a section of the array as a slice.");
analyze_slice(&ys[1 .. 4]);
// Example of empty slice `&[]`:
let empty_array: [u32; 0] = [];
assert_eq!(&empty_array, &[]);
assert_eq!(&empty_array, &[][..]); // Same but more verbose
// Arrays can be safely accessed using `.get`, which returns an
// `Option`. This can be matched as shown below, or used with
// `.expect()` if you would like the program to exit with a nice
// message instead of happily continue.
// for文と配列との組み合わせは、Pythonに近い
for i in 0..xs.len() + 1 { // Oops, one element too far!
match xs.get(i) {
Some(xval) => println!("{}: {}", i, xval),
None => println!("Slow down! {} is too far!", i),
}
}
// Out of bound indexing causes runtime error.
//println!("{}", xs[5]);
}
3.1. Structures
// An attribute to hide warnings for unused code.
#![allow(dead_code)]
// 名前と年齢のfieldを持つPerson構造体
#[derive(Debug)]
struct Person {
name: String,
age: u8,
}
// A unit struct
// fieldを持たない構造体
struct Unit;
// A tuple struct
// tuple構造体
struct Pair(i32, f32);
// A struct with two fields
// xy平面上の点の構造体
struct Point {
x: f32,
y: f32,
}
// Structs can be reused as fields of another struct
// 長方形構造体
struct Rectangle {
// A rectangle can be specified by where the top left and bottom right
// corners are in space.
top_left: Point,
bottom_right: Point,
}
fn main() {
// Create struct with field init shorthand
// Person構造体の初期化
let name = String::from("Peter");
let age = 27;
let peter = Person { name, age };
// Print debug struct
// 構造体の要素を全て描画する
println!("01: {:?}", peter);
// Instantiate a `Point`
// Point構造体の初期化
// 構造体を初期化する場合は、上記の変数代入か、 初期化時に設定するかの方法がある
let point: Point = Point { x: 10.3, y: 0.4 };
// Access the fields of the point
// 構造体の要素を指定する時はtupleと同じ方法で指定する
println!("02: point coordinates: ({}, {})", point.x, point.y);
// Make a new point by using struct update syntax to use the fields of our
// other one
// よくわからない
let bottom_right = Point { x: 5.2, ..point };
// `bottom_right.y` will be the same as `point.y` because we used that field
// from `point`
println!("03: second point: ({}, {})", bottom_right.x, bottom_right.y);
// Destructure the point using a `let` binding
// なにこれ
let Point { x: left_edge, y: top_edge } = point;
// 長方形構造体の初期化
let _rectangle = Rectangle {
// struct instantiation is an expression too
top_left: Point { x: left_edge, y: top_edge },
bottom_right: bottom_right,
};
// Instantiate a unit struct
let _unit = Unit;
// Instantiate a tuple struct
let pair = Pair(1, 0.1);
// Access the fields of a tuple struct
println!("04: pair contains {:?} and {:?}", pair.0, pair.1);
// Destructure a tuple struct
let Pair(integer, decimal) = pair;
println!("05: pair contains {:?} and {:?}", integer, decimal);
}
3.2. Enums
よくわかりませんでした。
// Create an `enum` to classify a web event. Note how both
// names and type information together specify the variant:
// `PageLoad != PageUnload` and `KeyPress(char) != Paste(String)`.
// Each is different and independent.
enum WebEvent {
// An `enum` variant may either be `unit-like`,
PageLoad,
PageUnload,
// like tuple structs,
KeyPress(char),
Paste(String),
// or c-like structures.
Click { x: i64, y: i64 },
}
// A function which takes a `WebEvent` enum as an argument and
// returns nothing.
fn inspect(event: WebEvent) {
match event {
WebEvent::PageLoad => println!("page loaded"),
WebEvent::PageUnload => println!("page unloaded"),
// Destructure `c` from inside the `enum` variant.
WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
WebEvent::Paste(s) => println!("pasted \"{}\".", s),
// Destructure `Click` into `x` and `y`.
WebEvent::Click { x, y } => {
println!("clicked at x={}, y={}.", x, y);
},
}
}
fn main() {
let pressed = WebEvent::KeyPress('x');
// `to_owned()` creates an owned `String` from a string slice.
let pasted = WebEvent::Paste("my text".to_owned());
let click = WebEvent::Click { x: 20, y: 80 };
let load = WebEvent::PageLoad;
let unload = WebEvent::PageUnload;
inspect(pressed);
inspect(pasted);
inspect(click);
inspect(load);
inspect(unload);
}
3.2.1. use
列挙型を使うときは、C#でいうSwitch文のような使い方をするのか?
// An attribute to hide warnings for unused code.
#![allow(dead_code)]
// 列挙型に値を入れることができるとかなんとか
enum Status {
Rich,
Poor,
}
enum Work {
Civilian,
Soldier,
}
fn main() {
// Explicitly `use` each name so they are available without
// manual scoping.
// クレートとはいったい?
use crate::Status::{Poor, Rich};
// Automatically `use` each name inside `Work`.
use crate::Work::*;
// Equivalent to `Status::Poor`.
// ステータスという変数に列挙型の値と同じものを代入する
let status = Poor;
// Equivalent to `Work::Civilian`.
let work = Civilian;
// Rustのmutchは、C#でいうSwitch文に当たるのか?
match status {
// Note the lack of scoping because of the explicit `use` above.
Rich => println!("01: The rich have lots of money!"),
Poor => println!("02: The poor have no money..."),
}
match work {
// Note again the lack of scoping.
Civilian => println!("03: Civilians work!"),
Soldier => println!("04: Soldiers fight!"),
}
}
3.2.2. C-like
C言語風に記述できるらしいが、C言語に詳しくないのでよくわかりませんでした。
// An attribute to hide warnings for unused code.
#![allow(dead_code)]
// enum with implicit discriminator (starts at 0)
// 列挙型の文字を数字にcastできるのか !?
enum Number {
Zero,
One,
Two,
}
// enum with explicit discriminator
enum Color {
Red = 0xff0000,
Green = 0x00ff00,
Blue = 0x0000ff,
}
fn main() {
// `enums` can be cast as integers.
// castはasなのか
println!("01: zero is {}", Number::Zero as i32);
println!("02: one is {}", Number::One as i32);
println!("03: roses are #{:06x}", Color::Red as i32);
println!("04: violets are #{:06x}", Color::Blue as i32);
}
3.2.3. Testcase: linked-list
構造体に関数を付与する方法が学びになった。
use crate::List::*; // なんだこれは?
// 構造体の定義
enum List {
// Cons: Tuple struct that wraps an element and a pointer to the next node
// 要素とポインターのtupleを Cons と呼ぶそうだ
Cons(u32, Box<List>),
// Nil: A node that signifies the end of the linked list
// listの終わりを明治しているようだ
Nil,
}
// Methods can be attached to an enum
// 構造体に関数を付与する
impl List {
// Create an empty list
// コンストラクタ
fn new() -> List {
// `Nil` has type `List`
Nil
}
// Consume a list, and return the same list with a new element at its front
// tupleの要素を加える関数
fn prepend(self, elem: u32) -> List {
// `Cons` also has type List
Cons(elem, Box::new(self))
}
// Return the length of the list
// 要素数を返す関数
fn len(&self) -> u32 {
// `self` has to be matched, because the behavior of this method
// depends on the variant of `self`
// `self` has type `&List`, and `*self` has type `List`, matching on a
// concrete type `T` is preferred over a match on a reference `&T`
// after Rust 2018 you can use self here and tail (with no ref) below as well,
// rust will infer &s and ref tail.
// See https://doc.rust-lang.org/edition-guide/rust-2018/ownership-and-lifetimes/default-match-bindings.html
match *self {
// Can't take ownership of the tail, because `self` is borrowed;
// instead take a reference to the tail
Cons(_, ref tail) => 1 + tail.len(),
// Base Case: An empty list has zero length
Nil => 0
}
}
// Return representation of the list as a (heap allocated) string
// 要素を文字列に変換して返す関数?
fn stringify(&self) -> String {
match *self {
Cons(head, ref tail) => {
// `format!` is similar to `print!`, but returns a heap
// allocated string instead of printing to the console
format!("{}, {}", head, tail.stringify())
},
Nil => {
format!("Nil")
},
}
}
}
fn main() {
// Create an empty linked list
// 構造体に付与した関数を使用する時はコロン2つを並べるようだ
// C#のピリオドに対応するのか?
let mut list = List::new();
// Prepend some elements
list = list.prepend(1);
list = list.prepend(2);
list = list.prepend(3);
// Show the final state of the list
println!("01: linked list has length: {}", list.len());
println!("02: {}", list.stringify());
}
3.3. constants
Rustにもグローバル変数があるんですね。
あと、if文をラムダ式のような使い捨ての式のように扱うこともできるんですね。
// Globals are declared outside all other scopes.
// グローバル変数、貴様C言語の生まれ変わりか!?
static LANGUAGE: &str = "Rust";
const THRESHOLD: i32 = 10;
// 定数の閾値より大きいか否かのbool変数を返す関数
fn is_big(n: i32) -> bool {
// Access constant in some function
n > THRESHOLD
}
fn main() {
let n = 16;
// Access constant in the main thread
println!("01: This is {}", LANGUAGE);
println!("02: The threshold is {}", THRESHOLD);
// はえ~、if文をラムダ式のような使い捨ての式としても扱うことができるんですね
println!("03: {} is {}", n, if is_big(n) { "big" } else { "small" });
// Error! Cannot modify a `const`.
// 定数を書き換えようとするとエラーになる
//THRESHOLD = 5;
// FIXME ^ Comment out this line
}
4. Variable Bindings
変数の宣言に let が必要なのはVBAに似てる。
しかし、変数名の最初にアンダーバーを付けると、コンパイラの警告を止めることができる。
古いのかモダンなのか分からなくなる。
fn main() {
// 変数の宣言は let が必要...VBAかな?
let an_integer = 1u32;
let a_boolean = true;
let unit = ();
// copy `an_integer` into `copied_integer`
// 値の複製
let copied_integer = an_integer;
println!("01: n integer: {:?}", copied_integer);
println!("02: A boolean: {:?}", a_boolean);
println!("03: Meet the unit value: {:?}", unit);
// The compiler warns about unused variable bindings; these warnings can
// be silenced by prefixing the variable name with an underscore
// 変数名の最初にアンダーバーをつけると、コンパイラの警告が出てくなくなる。
// 逆に使うべきではないと思う。
let _unused_variable = 3u32;
// 未使用の変数へのコンパイラの警告は必要だと考える
let noisy_unused_variable = 2u32;
// FIXME ^ Prefix with an underscore to suppress the warning
// Please note that warnings may not be shown in a browser
}
4.1. Mutability
デフォルトでは変数を書き換えることができない、というのは面白い性質ですね。
fn main() {
// 書き換えできない変数
let _immutable_binding = 1;
// 書き換え可能な変数
let mut mutable_binding = 1;
println!("01: Before mutation: {}", mutable_binding);
// Ok
// 書き換え可能な変数を書き換える
mutable_binding += 1;
println!("02: After mutation: {}", mutable_binding);
// Error!
//_immutable_binding += 1;
// FIXME ^ Comment out this line
}
4.2. Scope and Shadowing
シャドウイングはモダンな感じ。
fn main() {
// 変数の初期化
let shadowed_binding = 1;
{
// ここの変数は、main関数の最初の方で宣言した変数
println!("01: before being shadowed: {}", shadowed_binding);
// This binding *shadows* the outer one
// 1つ深いスコープで同じ名前の変数を定義する
let shadowed_binding = "abc";
// スコープを脱出するまでは、新しい変数を使用する
println!("02: shadowed in inner block: {}", shadowed_binding);
println!("02-01: shadowed in inner block: {}", shadowed_binding);
}
// スコープを脱出したら、元の変数に戻る
println!("03: outside inner block: {}", shadowed_binding);
// This binding *shadows* the previous binding
// 元の変数と同じスコープで宣言すると、変数の書き換えと見てよいのか?
let shadowed_binding = 2;
println!("04: shadowed in outer block: {}", shadowed_binding);
}
4.3. Declare first
書き換えができないだけで、変数の初期化のタイミングは自由度が高い。
fn main() {
// Declare a variable binding
// 変数名を宣言したが、初期化していない
let a_binding;
{
let x = 2;
// Initialize the binding
// 1つ深いスコープで初期化するのはOK
a_binding = x * x;
}
println!("01: a binding: {}", a_binding);
// 別の変数を宣言したが、初期化していない
let another_binding;
// Error! Use of uninitialized binding
// 変数を初期化する前に参照するとエラーになる
//println!("another binding: {}", another_binding);
// FIXME ^ Comment out this line
// 変数を初期化するのは後でもOK
another_binding = 1;
println!("02: another binding: {}", another_binding);
}
4.4. Freezing
シャドウイングの注意点。
fn main() {
// 変数の初期化
// 書き換え可能な変数
let mut _mutable_integer = 7i32;
{
// Shadowing by immutable `_mutable_integer`
// シャドウイング変数の初期化
// ややこしいが、元の変数と同じ変数名と値を持つ変数を作成した
// ただし、この変数は書き換え不可である
let _mutable_integer = _mutable_integer;
// Error! `_mutable_integer` is frozen in this scope
//_mutable_integer = 50;
// FIXME ^ Comment out this line
// `_mutable_integer` goes out of scope
}
// Ok! `_mutable_integer` is not frozen in this scope
_mutable_integer = 3;
}
5.1. Casting
キャストで値が小さくなるのは、組み込みだからですかね。
// Suppress all warnings from casts which overflow.
#![allow(overflowing_literals)]
fn main() {
// Rustの浮動小数点はf32, f64の2種類
let decimal = 65.4321_f32;
// Error! No implicit conversion
// 暗黙的な型変換は許容されない
//let integer: u8 = decimal;
// FIXME ^ Comment out this line
// Explicit conversion
// 明示的なcastのみ許容される
let integer = decimal as u8;
let character = integer as char;
// Error! There are limitations in conversion rules.
// A float cannot be directly converted to a char.
//let character = decimal as char;
// FIXME ^ Comment out this line
// キャスト結果を表示する
println!("01: Casting: {} -> {} -> {}", decimal, integer, character);
// when casting any value to an unsigned type, T,
// T::MAX + 1 is added or subtracted until the value
// fits into the new type
// 1000 already fits in a u16
// オーバーフローしない場合
println!("02: 1000 as a u16 is: {}", 1000 as u16);
// 1000 - 256 - 256 - 256 = 232
// Under the hood, the first 8 least significant bits (LSB) are kept,
// while the rest towards the most significant bit (MSB) get truncated.
// オーバーフローする場合
println!("03: 1000 as a u8 is : {}", 1000 as u8);
// -1 + 256 = 255
println!("04: -1 as a u8 is : {}", (-1i8) as u8);
// For positive numbers, this is the same as the modulus
// 正の整数に対する、8ビット自然数のキャストと、256の余りが同じになる
println!("05: 1000 mod 256 is : {}", 1000 % 256);
// When casting to a signed type, the (bitwise) result is the same as
// first casting to the corresponding unsigned type. If the most significant
// bit of that value is 1, then the value is negative.
// Unless it already fits, of course.
// オーバーフローしない
println!("06: 128 as a i16 is: {}", 128 as i16);
// 128 as u8 -> 128, whose value in 8-bit two's complement representation is:
// オーバーフローする
println!("07: 128 as a i8 is : {}", 128 as i8);
// repeating the example above
// 1000 as u8 -> 232
println!("08: 1000 as a u8 is : {}", 1000 as u8);
// and the value of 232 in 8-bit two's complement representation is -24
println!("09: 232 as a i8 is : {}", 232 as i8);
// Since Rust 1.45, the `as` keyword performs a *saturating cast*
// when casting from float to int. If the floating point value exceeds
// the upper bound or is less than the lower bound, the returned value
// will be equal to the bound crossed.
// 300.0 as u8 is 255
println!("10: 300.0 as u8 is : {}", 300.0_f32 as u8);
// -100.0 as u8 is 0
println!("11: -100.0 as u8 is : {}", -100.0_f32 as u8);
// nan as u8 is 0
println!("12: nan as u8 is : {}", f32::NAN as u8);
// This behavior incurs a small runtime cost and can be avoided
// with unsafe methods, however the results might overflow and
// return **unsound values**. Use these methods wisely:
// C#のunsafeに近いことができるのか?
// 後に回そう
unsafe {
// 300.0 as u8 is 44
println!("13: 300.0 as u8 is : {}", 300.0_f32.to_int_unchecked::<u8>());
// -100.0 as u8 is 156
println!("14: -100.0 as u8 is : {}", (-100.0_f32).to_int_unchecked::<u8>());
// nan as u8 is 0
println!("15: nan as u8 is : {}", f32::NAN.to_int_unchecked::<u8>());
}
}
5.2. Literals
書き換え不可の変数を引数にする場合は、貸出&
をつける必要があるようです。
fn main() {
// Suffixed literals, their types are known at initialization
// 型を明示するのは基本
let x = 1u8;
let y = 2u32;
let z = 3f32;
// Unsuffixed literals, their types depend on how they are used
// 型を明示しない場合、コンパイラが推論してくれる
let i = 1;
let f = 1.0;
// `size_of_val` returns the size of a variable in bytes
// メモリ使用量を返す
// 貸出(borrow)にしないとエラーになる !?
println!("01: size of `x` in bytes: {}", std::mem::size_of_val(&x));
println!("02: size of `y` in bytes: {}", std::mem::size_of_val(&y));
println!("03: size of `z` in bytes: {}", std::mem::size_of_val(&z));
println!("04: size of `i` in bytes: {}", std::mem::size_of_val(&i));
println!("05: size of `f` in bytes: {}", std::mem::size_of_val(&f));
}
5.3. Inference
Rustのコンパイラの型推論が強力ですね。
インスタンス生成の後の行での最初の代入から、インスタンスの型を推論してくれるようです。
fn main() {
// Because of the annotation, the compiler knows that `elem` has type u8.
// 8ビット自然数の変数
let elem = 5u8;
// Create an empty vector (a growable array).
// 書き換え可能な可変長配列を作成する
// 後のコードから、この可変長配列はu8を格納する可変長配列だと推論される
let mut vec = Vec::new();
// At this point the compiler doesn't know the exact type of `vec`, it
// just knows that it's a vector of something (`Vec<_>`).
// Insert `elem` in the vector.
// 可変長配列に要素を加える
vec.push(elem);
// Aha! Now the compiler knows that `vec` is a vector of `u8`s (`Vec<u8>`)
// TODO ^ Try commenting out the `vec.push(elem)` line
println!("01: {:?}", vec);
}
5.4. Aliasing
ピンとこなかった。
// `NanoSecond`, `Inch`, and `U64` are new names for `u64`.
type NanoSecond = u64;
type Inch = u64;
type U64 = u64;
fn main() {
// `NanoSecond` = `Inch` = `U64` = `u64`.
let nanoseconds: NanoSecond = 5 as U64;
let inches: Inch = 2 as U64;
// Note that type aliases *don't* provide any extra type safety, because
// aliases are *not* new types
// 型が同じ場合足し算ができるけど、意味を考えましょうね、という話
println!("01: {} nanoseconds + {} inches = {} unit?",
nanoseconds,
inches,
nanoseconds + inches);
}
6.1. From and Into
Frommの方。よくわからない。
use std::convert::From;
#[derive(Debug)]
struct Number {
value: i32,
}
impl From<i32> for Number {
fn from(item: i32) -> Self {
Number { value: item }
}
}
fn main() {
let num = Number::from(30);
println!("01: My number is {:?}", num);
}
6.1. From and Into
Intoの方。よくわからない。
use std::convert::From;
#[derive(Debug)]
struct Number {
value: i32,
}
impl From<i32> for Number {
fn from(item: i32) -> Self {
Number { value: item }
}
}
fn main() {
let int = 5;
// Try removing the type annotation
let num: Number = int.into();
println!("01: My number is {:?}", num);
}
6.2. TryFrom and TryInto
よくわからない。
use std::convert::TryFrom;
use std::convert::TryInto;
#[derive(Debug, PartialEq)]
struct EvenNumber(i32);
impl TryFrom<i32> for EvenNumber {
type Error = ();
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value % 2 == 0 {
Ok(EvenNumber(value))
} else {
Err(())
}
}
}
fn main() {
// TryFrom
assert_eq!(EvenNumber::try_from(8), Ok(EvenNumber(8)));
assert_eq!(EvenNumber::try_from(5), Err(()));
// TryInto
let result: Result<EvenNumber, ()> = 8i32.try_into();
assert_eq!(result, Ok(EvenNumber(8)));
let result: Result<EvenNumber, ()> = 5i32.try_into();
assert_eq!(result, Err(()));
}
6.3. To and from Strings
// 基本的なパッケージ
use std::fmt;
// 円の構造体
struct Circle {
radius: i32
}
// fmtパッケージの関数を円構造体にオーバーロードするのか?
impl fmt::Display for Circle {
// 構造体に表示関数を付与する
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Circle of radius {}", self.radius)
}
}
fn main() {
let circle = Circle { radius: 6 };
// to_stringどこから出てきたのだ
println!("01: {}", circle.to_string());
}
7. Expressions
変数定義時に使い捨て関数を使うことができるのはモダンな感じで素敵
fn main() {
// ここは変数の定義。特にいうことはない
let x = 5u32;
// 変数を定義するときに{}; で囲むと、使い捨ての関数で定義できるらしい。
let y = {
let x_squared = x * x;
let x_cube = x_squared * x;
// This expression will be assigned to `y`
// セミコロンなしの最後の行が戻り値になる
x_cube + x_squared + x
};
let z = {
// The semicolon suppresses this expression and `()` is assigned to `z`
// 最後の行にセミコロンを加えると、要素無しのtuple型になる
2 * x;
};
println!("01: x is {:?}", x);
println!("02: y is {:?}", y);
println!("03: z is {:?}", z);
}
8.1. if/else
Pythonに近い書き方
fn main() {
let n = 5;
if n < 0 {
print!("01: {} is negative", n);
} else if n > 0 {
print!("02: {} is positive", n);
} else {
print!("03: {} is zero", n);
}
let big_n =
if n < 10 && n > -10 {
// println!はprint!と違うのか
println!("04: , and is a small number, increase ten-fold");
// This expression returns an `i32`.
10 * n
} else {
println!("05: , and is a big number, halve the number");
// This expression must return an `i32` as well.
n / 2
// TODO ^ Try suppressing this expression with a semicolon.
};
// ^ Don't forget to put a semicolon here! All `let` bindings need it.
println!("06: {} -> {}", n, big_n);
}
8.2. loop
無限ループって怖くね?
fn main() {
let mut count = 0u32;
println!("01: Let's count until infinity!");
// Infinite loop
// 無限ループの文法があるのか!?
loop {
count += 1;
if count == 3 {
println!("three");
// Skip the rest of this iteration
continue;
}
println!("{}", count);
// ループ脱出は普通だ
if count == 5 {
println!("OK, that's enough");
// Exit this loop
break;
}
}
}
8.2.1. Nesting and labels
これは便利
#![allow(unreachable_code)]
fn main() {
// 多重ループも記述できる!
'outer: loop {
println!("01: Entered the outer loop");
'inner: loop {
println!("02: Entered the inner loop");
// This would break only the inner loop
//break;
// This breaks the outer loop
break 'outer;
}
println!("03: This point will never be reached");
}
println!("04: Exited the outer loop");
}
8.2.2. Returning from loops
無限ループ式使い捨て関数による変数の初期化はモダン言語だ
fn main() {
let mut counter = 0;
// 変数定義の使い捨て関数には無限ループを使用することもできる
let result = loop {
counter += 1;
if counter == 10 {
// 無限ループ脱出時の
break counter * 2;
}
};
print!("01: result is {}\n", result);
//assert_eq!(result, 20);
}
8.3. while
C言語とほぼ同じ
fn main() {
// A counter variable
let mut n = 1;
// Loop while `n` is less than 101
// C言語とほぼ同じ
while n < 101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
// Increment counter
n += 1;
}
}
8.4. for and range
実家のような安心感
fn main() {
// `n` will take the values: 1, 2, ..., 100 in each iteration
// 実家のような安心感
for n in 1..101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
}
8.5. match
Rustがモダンな言語なんだなあというのが分かるコード。
これは便利。
fn main() {
let number = 13;
// TODO ^ Try different values for `number`
println!("01: Tell me about {}", number);
// switch文に近い
match number {
// Match a single value
1 => println!("02: One!"),
// Match several values
2 | 3 | 5 | 7 | 11 => println!("03: This is a prime"),
// TODO ^ Try adding 13 to the list of prime values
// Match an inclusive range
13..=19 => println!("04: A teen"),
// Handle the rest of cases
_ => println!("05: Ain't special"),
// TODO ^ Try commenting out this catch-all arm
}
let boolean = true;
// Match is an expression too
// 関数型言語の書き味
let binary = match boolean {
// The arms of a match must cover all the possible values
false => 0,
true => 1,
// TODO ^ Try commenting out one of these arms
};
println!("06: {} -> {}", boolean, binary);
}
8.5.1.1. tuples
fn main() {
let triple = (0, -2, 3);
// TODO ^ Try different values for `triple`
println!("01: Tell me about {:?}", triple);
// Match can be used to destructure a tuple
// tupleに対するmatch構文は慣れるしかないと思う
match triple {
// Destructure the second and third elements
// 変数を条件にした場合、条件ではなく、戻り値?にするようだ
(0, y, z) => println!("02: First is `0`, `y` is {:?}, and `z` is {:?}", y, z),
(1, ..) => println!("03: First is `1` and the rest doesn't matter"),
(.., 2) => println!("04: last is `2` and the rest doesn't matter"),
(3, .., 4) => println!("05: First is `3`, last is `4`, and the rest doesn't matter"),
// `..` can be used to ignore the rest of the tuple
_ => println!("06: It doesn't matter what they are"),
// `_` means don't bind the value to a variable
}
}
8.5.1.2. arrays/slices
慣れがいるなあ
fn main() {
// Try changing the values in the array, or make it a slice!
let array = [1, -2, 6];
// 配列に対するmatch構文
match array {
// Binds the second and the third elements to the respective variables
[0, second, third] =>
println!("01: array[0] = 0, array[1] = {}, array[2] = {}", second, third),
// Single values can be ignored with _
[1, _, third] => println!(
"02: array[0] = 1, array[2] = {} and array[1] was ignored",
third
),
// You can also bind some and ignore the rest
[-1, second, ..] => println!(
"03: array[0] = -1, array[1] = {} and all the other ones were ignored",
second
),
// The code below would not compile
// [-1, second] => ...
// Or store them in another array/slice (the type depends on
// that of the value that is being matched against)
[3, second, tail @ ..] => println!(
"04: array[0] = 3, array[1] = {} and the other elements were {:?}",
second, tail
),
// Combining these patterns, we can, for example, bind the first and
// last values, and store the rest of them in a single array
[first, middle @ .., last] => println!(
"05: array[0] = {}, middle = {:?}, array[2] = {}",
first, middle, last
),
}
}
8.5.1.3. enums
matchの例
// `allow` required to silence warnings because only
// one variant is used.
// 色構造体、なんだ?
#[allow(dead_code)]
enum Color {
// These 3 are specified solely by their name.
Red,
Blue,
Green,
// These likewise tie `u32` tuples to different names: color models.
RGB(u32, u32, u32),
HSV(u32, u32, u32),
HSL(u32, u32, u32),
CMY(u32, u32, u32),
CMYK(u32, u32, u32, u32),
}
fn main() {
let color = Color::RGB(122, 17, 40);
// TODO ^ Try different variants for `color`
println!("What color is it?");
// An `enum` can be destructured using a `match`.
// 様々なものにmatchを使うことができる
match color {
Color::Red => println!("01: The color is Red!"),
Color::Blue => println!("02: The color is Blue!"),
Color::Green => println!("03: The color is Green!"),
Color::RGB(r, g, b) =>
println!("04: Red: {}, green: {}, and blue: {}!", r, g, b),
Color::HSV(h, s, v) =>
println!("05: Hue: {}, saturation: {}, value: {}!", h, s, v),
Color::HSL(h, s, l) =>
println!("06: Hue: {}, saturation: {}, lightness: {}!", h, s, l),
Color::CMY(c, m, y) =>
println!("07: Cyan: {}, magenta: {}, yellow: {}!", c, m, y),
Color::CMYK(c, m, y, k) =>
println!("08: Cyan: {}, magenta: {}, yellow: {}, key (black): {}!",
c, m, y, k),
// Don't need another arm because all variants have been examined
}
}
8.5.1.4. pointers/ref
matchなのに、値あるいは参照を渡す前提の書き方があるようだ。
よくわからない。
fn main() {
// Assign a reference of type `i32`. The `&` signifies there
// is a reference being assigned.
let reference = &4;
match reference {
// どういうことだ
// If `reference` is pattern matched against `&val`, it results
// in a comparison like:
// `&i32`
// `&val`
// ^ We see that if the matching `&`s are dropped, then the `i32`
// should be assigned to `val`.
&val => println!("01: Got a value via destructuring: {:?}", val),
}
// To avoid the `&`, you dereference before matching.
match *reference {
// どういうことだ
val => println!("02: Got a value via dereferencing: {:?}", val),
}
// What if you don't start with a reference? `reference` was a `&`
// because the right side was already a reference. This is not
// a reference because the right side is not one.
let _not_a_reference = 3;
// Rust provides `ref` for exactly this purpose. It modifies the
// assignment so that a reference is created for the element; this
// reference is assigned.
let ref _is_a_reference = 3;
// Accordingly, by defining 2 values without references, references
// can be retrieved via `ref` and `ref mut`.
let value = 5;
let mut mut_value = 6;
// Use `ref` keyword to create a reference.
match value {
// 参照渡し?
ref r => println!("03: Got a reference to a value: {:?}", r),
}
// Use `ref mut` similarly.
match mut_value {
// 参照渡し?
ref mut m => {
// Got a reference. Gotta dereference it before we can
// add anything to it.
*m += 10;
println!("04: We added 10. `mut_value`: {:?}", m);
},
}
}
8.5.1.5. structs
matchはswitch文の条件分岐と関数の値取り出しを両立させることもできるようだ。
モダンな感じ。
fn main() {
// 構造体を設定するときは、関数の中でも外でも可能
struct Foo {
x: (u32, u32),
y: u32,
}
// Try changing the values in the struct to see what happens
// 構造体の初期化
let foo = Foo { x: (2, 3), y: 2 };
// matchは条件分岐と値取り出しの両方の機能があるようだ
match foo {
// 上の方の条件の優先順位が高い
Foo { x: (1, b), y } => println!("01: First of x is 1, b = {}, y = {} ", b, y),
// you can destructure structs and rename the variables,
// the order is not important
Foo { y: 2, x: i } => println!("02: y is 2, i = {:?}", i),
// and you can also ignore some variables:
// 構造体のfiledの内、特定のfieldにだけ興味がある場合、残りを .. で表すようだ
Foo { y, .. } => println!("03: y = {}, we don't care about x", y),
// this will give an error: pattern does not mention field `x`
//Foo { y } => println!("y = {}", y),
}
}
8.5.2. Guards
前のレスに書いた条件分岐と関数の値取り出しの両立が実装されているようだ。
あと、構造体の初期化が難しい。
enum Temperature {
Celsius(i32),
Fahrenheit(i32),
}
fn main() {
// 構造体のインスタンス化時には、全ての要素を初期化する必要はないらしい。
let temperature = Temperature::Celsius(88);
// ^ TODO try different values for `temperature`
// 同じスコープでのシャドウイング
let temperature = Temperature::Fahrenheit(114);
// lambda式に近い
match temperature {
Temperature::Celsius(t) if t > 30 => println!("01: {}C is above 30 Celsius", t),
// The `if condition` part ^ is a guard
Temperature::Celsius(t) => println!("02: {}C is below 30 Celsius", t),
Temperature::Fahrenheit(t) if t > 86 => println!("03: {}F is above 86 Fahrenheit", t),
Temperature::Fahrenheit(t) => println!("04: {}F is below 86 Fahrenheit", t),
}
}
8.5.3. Binding
関数もmatchの引数にすることができる。
// A function `age` which returns a `u32`.
// リテラルを返す関数
fn age() -> u32 {
15
}
fn main() {
println!("01: Tell me what type of person you are");
// matchが柔軟すぎる
// 関数も比較できる
match age() {
0 => println!("02: I haven't celebrated my first birthday yet"),
// Could `match` 1 ..= 12 directly but then what age
// would the child be? Instead, bind to `n` for the
// sequence of 1 ..= 12. Now the age can be reported.
n @ 1 ..= 12 => println!("03: I'm a child of age {:?}", n),
n @ 13 ..= 19 => println!("04: I'm a teen of age {:?}", n),
// Nothing bound. Return the result.
n => println!("05: I'm an old person of age {:?}", n),
}
}
8.6. if let
オプション型とは何だろうか。
経緯が分かると腹落ちできそう。
fn main() {
// All have type `Option<i32>`
// オプション型というものがあるようだ
let number = Some(77);
let letter: Option<i32> = None;
let emoticon: Option<i32> = None;
// The `if let` construct reads: "if `let` destructures `number` into
// `Some(i)`, evaluate the block (`{}`).
// わかるけどさあ、という感じ
if let Some(i) = number {
println!("01: Matched {:?}!", i);
}
// If you need to specify a failure, use an else:
// 同上
if let Some(i) = letter {
println!("02: Matched {:?}!", i);
} else {
// Destructure failed. Change to the failure case.
println!("03: Didn't match a number. Let's go with a letter!");
}
// Provide an altered failing condition.
let i_like_letters = false;
// 腹落ちしない
if let Some(i) = emoticon {
println!("04: Matched {:?}!", i);
// Destructure failed. Evaluate an `else if` condition to see if the
// alternate failure branch should be taken:
} else if i_like_letters {
println!("05: Didn't match a number. Let's go with a letter!");
} else {
// The condition evaluated false. This branch is the default:
println!("06: I don't like letters. Let's go with an emoticon :)!");
}
}
8.8. while let
オプション型は、所有権とかそのあたりが関係しているのかな?
fn main() {
// Make `optional` of type `Option<i32>`
// オプション型の意義がよくわからない。
let mut optional = Some(0);
// This reads: "while `let` destructures `optional` into
// `Some(i)`, evaluate the block (`{}`). Else `break`.
// 無限ループ脱出の別の書き方という感じ
while let Some(i) = optional {
if i > 9 {
println!("01: Greater than 9, quit!");
// オプション型を None にすることで、次のループの開始条件を崩しているようだ
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
// ^ Less rightward drift and doesn't require
// explicitly handling the failing case.
}
// ^ `if let` had additional optional `else`/`else if`
// clauses. `while let` does not have these.
}
9. Functions
そういえばC言語では関数定義はmain関数の前でしたね。
自由に配置できることから、Rustはモダンな言語なんだというのが分かる。
// Unlike C/C++, there's no restriction on the order of function definitions
fn main() {
// We can use this function here, and define it somewhere later
// C言語では、main関数の前に関数を定義する必要があったが、Rustは関数定義の場所が自由である。
fizzbuzz_to(100);
}
// Function that returns a boolean value
// 自然数を自然数で割り切れるかを検証する関数
// 組み込み系の変数短縮文化が見られる
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
// Corner case, early return
if rhs == 0 {
return false;
}
// This is an expression, the `return` keyword is not necessary here
// 時々でていた関数の戻り値の別の書き方。
// セミコロンなしの最後の行が戻り値になる
lhs % rhs == 0
}
// Functions that "don't" return a value, actually return the unit type `()`
// ある自然数のFitbuzzを検証する
fn fizzbuzz(n: u32) -> () {
if is_divisible_by(n, 15) {
println!("fizzbuzz");
} else if is_divisible_by(n, 3) {
println!("fizz");
} else if is_divisible_by(n, 5) {
println!("buzz");
} else {
println!("{}", n);
}
}
// When a function returns `()`, the return type can be omitted from the
// signature
// 1からある自然数までのFitbuzzを検証する
fn fizzbuzz_to(n: u32) {
for n in 1..=n {
fizzbuzz(n);
}
}
9.1. Methods
[引用](9.1. Methods)
構造体にメソッドを付与することでクラスライクなものを作ることができる。
C言語の後継な感じがする。
// 点の構造体
struct Point {
x: f64,
y: f64,
}
// Implementation block, all `Point` associated functions & methods go in here
// 構造体に付与する関数をメソッドと呼ぶようだ
impl Point {
// This is an "associated function" because this function is associated with
// a particular type, that is, Point.
//
// Associated functions don't need to be called with an instance.
// These functions are generally used like constructors.
// 初期化時のfieldに原点をとるメソッド
fn origin() -> Point {
Point { x: 0.0, y: 0.0 }
}
// Another associated function, taking two arguments:
// コンストラクタも自分で付与するようだ
fn new(x: f64, y: f64) -> Point {
Point { x: x, y: y }
}
}
// 長方形構造体
struct Rectangle {
p1: Point,
p2: Point,
}
// 長方形構造体にメソッドを付与する
impl Rectangle {
// This is a method
// `&self` is sugar for `self: &Self`, where `Self` is the type of the
// caller object. In this case `Self` = `Rectangle`
// 面積を計算するメソッド
fn area(&self) -> f64 {
// `self` gives access to the struct fields via the dot operator
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
// `abs` is a `f64` method that returns the absolute value of the
// caller
((x1 - x2) * (y1 - y2)).abs()
}
// 外周の長さを計算するメソッド
fn perimeter(&self) -> f64 {
let Point { x: x1, y: y1 } = self.p1;
let Point { x: x2, y: y2 } = self.p2;
2.0 * ((x1 - x2).abs() + (y1 - y2).abs())
}
// This method requires the caller object to be mutable
// `&mut self` desugars to `self: &mut Self`
// fieldを貸し出すためのメソッド?
fn translate(&mut self, x: f64, y: f64) {
self.p1.x += x;
self.p2.x += x;
self.p1.y += y;
self.p2.y += y;
}
}
// `Pair` owns resources: two heap allocated integers
// Box型のfieldを2つもつ構造体
struct Pair(Box<i32>, Box<i32>);
impl Pair {
// This method "consumes" the resources of the caller object
// `self` desugars to `self: Self`
// デストラクト?
fn destroy(self) {
// Destructure `self`
let Pair(first, second) = self;
println!("Destroying Pair({}, {})", first, second);
// `first` and `second` go out of scope and get freed
}
}
fn main() {
// 構造体の初期化
let rectangle = Rectangle {
// Associated functions are called using double colons
p1: Point::origin(),
p2: Point::new(3.0, 4.0),
};
// Methods are called using the dot operator
// Note that the first argument `&self` is implicitly passed, i.e.
// `rectangle.perimeter()` === `Rectangle::perimeter(&rectangle)`
// 長方形の外周の長さ
println!("01: Rectangle perimeter: {}", rectangle.perimeter());
// 長方形の面積
println!("02: Rectangle area: {}", rectangle.area());
// 正方形の構造体
let mut square = Rectangle {
p1: Point::origin(),
p2: Point::new(1.0, 1.0),
};
// Error! `rectangle` is immutable, but this method requires a mutable
// object
//rectangle.translate(1.0, 0.0);
// TODO ^ Try uncommenting this line
// Okay! Mutable objects can call mutable methods
square.translate(1.0, 1.0);
println!("03: Square perimeter: {}", square.perimeter());
println!("04: Square area: {}", square.area());
// pair構造体の初期化
let pair = Pair(Box::new(1), Box::new(2));
pair.destroy();
// Error! Previous `destroy` call "consumed" `pair`
//pair.destroy();
// TODO ^ Try uncommenting this line
}
FizzBuzz
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
P55
ちょっと寄り道。
日本語書籍の方が頭に入りますね。
// RustでFizzBuzz問題を繰り返す
fn main() {
// 1から100まで繰り返す
for i in 1..101 {
// 条件を1つずつ繰り返す
if i % 3 == 0 && i % 5 == 0 {
println!("FizzBuzz");
}
else if i % 3 == 0 {
println!("Fizz");
}
else if i % 5 == 0 {
println!("Buzz");
}
else {
println!("{}", i);
}
}
}
P 60
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
フリーランチ定理ぎみに考えるのはよくないと感じました。
fn main() {
// 1から100まで繰り返す
for i in 1..51 {
if i % 3 == 0 || i % 10 == 3 {
println!("A");
}
else if i >= 30 && i <= 39 {
println!("A");
}
else {
println!("{}", i);
}
}
}
P 64
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
九九を表示するプログラム。
全ての要素にカンマを付けると、csv化したときに無駄な列が出てくるので良くない。
fn main() {
for y in 1..10 {
for x in 1..10 {
// 半角3文字幅で出力する
print!("{:3},", x * y);
}
println!("");
}
}
P 66
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
九九を表示するプログラムで、行の途中の値にだけカンマを付ける場合のプログラム。
map
が遅延処理で、collect
が配列化で、join
が文字列結合かな?
fn main() {
for y in 1..10 {
let s = (1..10)
.map(|x| format!("{:3}", x * y))
.collect::<Vec<String>>()
.join(",");
println!("{}", s);
}
}
P 67
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
Rustはマクロが活躍するらしい。
println!
は改行付き文字列表示のマクロで、print!
は改行なしとのこと。
fn main() {
for y in 1926..2027 {
print!("西暦{}年 = ", y);
if y > 2019 {
println!("令和{}年", y - 2019 + 1);
}
else if y == 2019 {
println!("令和元年");
}
else if y > 1989 {
println!("令和{}年", y - 1989 + 1);
}
else if y == 1989 {
println!("平成元年");
}
else if y > 1926 {
println!("昭和{}年", y - 1926 + 1);
}
else if y == 1926 {
println!("昭和元年");
}
}
}
P 71
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
フィボナッチ数列を表示するプログラム。
for文でローカル変数を割り当てる必要もないようだ。
fn main() {
let mut a = 1;
let mut b = 1;
println!("{}", a);
println!("{}", b);
for _ in 0..30 {
println!("{}", a + b);
let tmp = a;
a = b;
b = tmp + b;
}
}
P 76
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
Rustの変数が基本的に値変更不可であることの説明の流れで出された練習問題。
意図しない書き換えがバグの原因の場合が多いため、このような仕様らしい。
fn main() {
// PCの定価
let pc_price = 98000_f32;
// 送料
let a_ship_fee = 1200_f32;
let a_rate = 0.8_f32;
let b_ship_fee = 0_f32;
let b_rate = 0.9_f32;
// 買い物金額
println!("A社の価格 = {}円", pc_price * a_rate + a_ship_fee);
println!("B社の価格 = {}円", pc_price * b_rate + b_ship_fee);
}
P 80
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
人が考えるときは条件を絞っていくやりかたが多いけど、プログラムの場合、総当たりの方が早いよね。
fn main() {
let price = 3950;
for i500 in 0..11 { // 500円玉の枚数
for i100 in 0..4 { // 100円
for i50 in 0..11 { // 50円
let total = i500 * 500 + i100 * 100 + i50 * 50;
if price == total { // 総額が希望の価格と等しい場合、その組み合わせを表示する
// {:数字}は表示桁数のマクロのようだ
// {:?}は表示桁数をよしなにしてくれる
println!("500円x{:2}枚 + 100円x{:3}枚 + 50円x{:4}枚 = {:?}円",
i500, i100, i50, total);
}
}
}
}
}
P 83
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
整数型の最大値と最小値を確認する。
ところで、構造体のfieldやtupleの要素にアクセスするときはピリオドを使って、型に組み込まれているプロパティなどにアクセスするときはコロンを並べるようですね。
fn main() {
println!("符号あり整数");
println!("i8 = [{} - {}]", i8::MIN, i8::MAX);
println!("i16 = [{} - {}]", i16::MIN, i16::MAX);
println!("i32 = [{} - {}]", i32::MIN, i32::MAX);
println!("i64 = [{} - {}]", i64::MIN, i64::MAX);
println!("\n符号なし整数");
println!("u8 = [{} - {}]", u8::MIN, u8::MAX);
println!("u16 = [{} - {}]", u16::MIN, u16::MAX);
println!("u32 = [{} - {}]", u32::MIN, u32::MAX);
println!("u64 = [{} - {}]", u64::MIN, u64::MAX);
}
P 85
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
おつり計算再び。
テキストでは硬貨の枚数に+1しておりますが、今回は=終わりにしたほうが良さそうですね。
fn main() {
// おつりの金額
let price = 3950;
// 硬貨の枚数
let count500 = 10;
let count100 = 3;
let count50 = 10;
for i500 in 0..=count500 { // 500円玉の枚数
for i100 in 0..=count100 { // 100円
for i50 in 0..=count50 { // 50円
let total = i500 * 500 + i100 * 100 + i50 * 50;
if price == total { // 総額が希望の価格と等しい場合、その組み合わせを表示する
// {:数字}は表示桁数のマクロのようだ
// {:?}は表示桁数をよしなにしてくれる
println!("500円x{:2}枚 + 100円x{:3}枚 + 50円x{:4}枚 = {:?}円",
i500, i100, i50, total);
}
}
}
}
}
P 89
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
文字列の処理はchar型に対応する数値の処理で行うのか。
知らなかった。
fn main() {
let enc = encrypt("I LOVE YOU", 3);
let dec = encrypt(&enc, -3);
println!("{} => {}", enc, dec);
}
fn encrypt(text: &str, shift: i16) -> String{
let code_a = 'A' as i16; // 文字コードを数値に変換する
let code_z = 'Z' as i16;
let mut result = String::new(); // 文字列型の初期化
for ch in text.chars() { // char型の配列に変換して、コードの変換を行う
let mut code = ch as i16;
if code_a <= code && code <= code_z {
code = (code - code_a + shift + 26) % 26 + code_a; // アルファベット26文字の変換
}
result.push((code as u8) as char); // i16だとエラーになる。なんで?
}
return result;
}
P 92
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
Rustにおける関数の戻り値は、return
以外にもあるよ、という例だそうです。
fn main() {
let a = 3_i64;
let b = 5_i64;
let ex1 = kakezan(a, b);
print!("{} x {} = {}", a, b, ex1);
}
fn kakezan(a: i64, b: i64) -> i64 {
a * b
}
P 95
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
クロージャー(他言語のラムダ式)の例。
モダンな言語らしいと感じました。
fn main() {
let enc = encrypt("I LOVE YOU", 3);
let dec = encrypt(&enc, -3);
println!("{} => {}", enc, dec);
}
fn encrypt(text: &str, shift: i16) -> String {
let a = 'A' as i16;
// クロージャー(他言語のlambda式の変数は|変数|と書く)
let is_az = |c| 'A' <= c && c <= 'Z'; // 文字がAとZの間にあるかを確認するラムダ式
let conv = |c| ( ( (c - a + shift + 26) % 26 + a) as u8) as char; // 文字をシフトするラムダ式
let enc1 = |c| if is_az(c) { conv(c as i16) } else { c }; // 上記2式を統合した式
// chars()で文字の配列に分割して、
// map()で各要素のシフトを行い、
// collectで文字列に結合する
return text.chars().map( |c| enc1(c) ).collect();
}
P 99
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
配列を引数にする場合、要素数がリテラルなのは厳しいと感じた。
自由に選択したいが、今はまだそのときではないのだろう。
fn main() {
let mut primes = [0; 100];
get_primes(&mut primes);
println!("{:?}", primes);
}
// 素数を判定する関数
fn is_prime(n: usize) -> bool {
for i in 2..n {
if n % i == 0 {
return false;
}
}
return true;
}
// 要素数100個の配列に素数を格納する
fn get_primes(primes: &mut[usize; 100]) { // 引数の配列の要素数が固定なのはきついと感じる。
let mut i = 2;
let mut count = 0;
while count < 100 { // ここと引数の100を自分で選択できるようになると一皮むけそうだ
if is_prime(i) {
primes[count] = i;
count += 1;
}
i += 1;
}
}
P 102
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
参照という言い方が紛らわしいだけで、本質はポインターですね。
この時点でポインターを意識する必要があるため、やはりRustは硬派だと感じました。
C#のunsafe
を勉強したおかげでここの理解がすんなりできたと思います。
fn main() {
let mut v = 10;
println!("変更前の変数の値: {:5}", v);
// C#でいうところのref, outと&mutがほぼ同じなのか
set_value100(&mut v);
println!("変更後の変数の値: {:5}", v);
}
fn set_value100(arg: &mut u32) { // -> なしはvoid型のようだ
*arg = 100; // 値を変えるのに*をつける必要があるのか?
// &で参照呼出しをしているので、argそのものはポインターを指す。
// ポインターの保存場所の変数の値を変えるために * をつけて実体化させるとのこと
// C# のポインターと実態との関係に似ている
}
P 104
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
for文の使い方のコード。(紙面よりちょっと多い)
硬派さの中にモダンな仕様が入り込んでいて、面白いと感じました。
fn main() {
println!("1から10までの総和を計算する");
let mut total = 0 as u64;
// リテラルfor文 その1
for i in 0..11 {
total += i;
}
println!("\nリテラルfor文 その1: {}", total);
// リテラルfor文 その2
total = 0;
for i in 0..=10 {
total += i;
}
println!("\nリテラルfor文 その2: {}", total);
// foreachライク
total = 0;
let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for i in nums {
total += i;
}
println!("\nforeachライク: {}", total);
// for文の条件文に配列の長さを活用する場合
total = 0;
for i in 0..nums.len() {
total += nums[i];
}
println!("\nfor文の条件文に配列の長さを活用する場合: {}", total);
}
P112 - 113
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
コマンドラインによる制御のお話。
Cargoというのがキーになるようだ。
Visual studioに慣れているとコマンドラインは戸惑いますね。
fn main() {
let msg_list = [
"勤勉な人の計画は必ず成功につながり",
"せっかちな人は必ず貧乏へと向かう。"
];
for msg in msg_list {
println!("{}", msg);
}
}
P116-117
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
C#のNugetに当たるのがRustのCrateのようだ。
大きな整数型を扱うためにnum-bigint
とうCrateをインストールしたようだ。
今回のプログラムはコマンドラインの出番が多かった。
プログラマになると、コマンドラインを使いこなすようになるのかな。
fn main() {
let v = BigInt::from(1234);
println!("{}^5678 = {}", v, v.pow(5678));
}
use num_bigint::BigInt;
P125
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
サイコロのように1から6までの自然数の乱数を生成するプログラム。
tomlファイル(設定ファイル)に使用したいCrate(ライブラリ)を書き込む。
tomlファイルにバージョン番号を書いておくことで、破壊的変更の影響を小さくできる。
fn main() {
// 乱数生成のインスタンス
let mut rng = rand::thread_rng();
for _ in 0..5 {
// 乱数を生成する
let dice = rng.gen_range(1..=6);
println!("{}", dice);
}
}
use rand::Rng; // 乱数のCrate
P129, 130
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
迷路を描画するプログラム。
多重配列はジャグ配列がデフォルトのようだ。
use rand::Rng;
const MAP_N: usize = 25;
fn main() {
// 乱数生成器
let mut rng = rand::thread_rng();
// 迷路の初期化
// 多重配列の初期化が面白い
// ジャグ配列がデフォルト?
let mut maze = [ [0; MAP_N]; MAP_N];
// 外周を壁にする
for n in 0..MAP_N {
maze[n][0] = 1;
maze[0][n] = 1;
maze[n][MAP_N - 1] = 1;
maze[MAP_N - 1][n] = 1;
}
// 2マス
for y in 2..MAP_N-2 { // ()がいらない! 便利!
for x in 2..(MAP_N - 2) {
// 奇数の位置は通路にする
if x % 2 == 1 || y % 2 == 1 {continue;}
maze[y][x] = 1;
// 上下左右のいずれかを壁にする
let r = rng.gen_range(0..=3);
match r {
0 => maze[y - 1][x] = 1, // 上
1 => maze[y + 1][x] = 1, // 下
2 => maze[y][x - 1] = 1, // 右
3 => maze[y][x + 1] = 1, // 左
_ => {}, // Switch文のdefault的な文?
}
}
}
let tiles = [" ", "ZZ"];
for y in 0..MAP_N {
for x in 0..MAP_N {
print!("{}", tiles[maze[y][x]]);
}
println!("");
}
}
P137
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
ビンゴカードを生成するプログラム。
配列の要素をシャッフルするメソッドは組み込まれていているように見えたが、use rand::seq::SliceRandom;
を宣言したことで配列にshuffle
メソッドが追加されるようだ。
// 配列をシャッフルするのに必要な宣言
use rand::seq::SliceRandom;
fn main() {
// 1から75までの数値を配列に代入
let mut nums = [0; 75];
for i in 0..nums.len() {nums[i] = i + 1;}
//println!("初期化した配列の要素");
//nums.map(|n| print!("{:4}", n));
// シャッフル
let mut rng = rand::thread_rng();
nums.shuffle(&mut rng);
//println!("\n要素を入れ替えた配列の要素");
//nums.map(|n| print!("{:4}", n));
// カードを表示する
for y in 0..5 {
for x in 0..5 {
let i = y * 5 + x;
if i == 12 {
print!(" *,");
}
else {
print!("{:3},", nums[i]);
}
}
println!("");
}
}
P139-141
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
いくつかのベクター型を生成するプログラムをまとめました。
雑感ですが、強い制約と利便性を両立するために、マクロ!
を定義しているのかなと思いました。
fn main() {
println!("\nマクロでベクトル型を生成する");
let nums = vec![1, 2, 3];
println!("{:?}", nums);
println!("\nコンストラクタ関数で生成する");
let mut nums2 = Vec::new();
nums2.push(1);
nums2.push(2);
nums2.push(3);
println!("{:?}", nums2);
println!("\nu32型を指定してベクトル型を生成する");
let a_vec: Vec<u32> = vec![100, 200, 300];
for a in a_vec {
println!("{}", a);
}
println!("\n&str型を指定してベクトル型を生成する");
let s_vec: Vec<&str> = vec!["犬", "猫", "鶏"];
for s in s_vec {
println!("{}", s);
}
}
P142
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
ベクトル型でビンゴカード作成のプログラムを書き直しました。
数値計算の場合、可変長配列であるベクトル型の方が向いていそうな気がしますね。
use rand::seq::SliceRandom;
fn main() {
// ベクトル型の初期化
let mut nums = vec![];
for i in 1..=75 {nums.push(i);}
// 拡張性が高いなあ
let mut rng = rand::thread_rng();
nums.shuffle(&mut rng);
for i in 0..25 {
if i == 12 {
print!(" *");
}
else {
print!("{:3}", nums[i]);
}
if i % 5 == 4 {println!("");}
else {print!(",");}
}
}
P147
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
標準ライブラリの中にある入力メソッドを学びました。
C#のConsole君のほうが簡単な気がしました。
fn main() {
// 身長と体重を入力する
let height_cm = input("身長(cm)は? ");
let weight = input("体重(kg)は? ");
// BMIを計算する
let height = height_cm / 100.0;
let bmi = weight / height.powf(2.0);
println!("BMi = {:1}", bmi);
// 分岐はPythonとほぼ同じ
if bmi < 18.5 { println!("低体重"); }
else if bmi < 25.0 {
println!("普通体重");
}
else if bmi < 30.0 {
println!("肥満1度");
}
else if bmi < 35.0 {
println!("肥満2度");
}
else if bmi < 40.0 {
println!("肥満3度");
}
else {
println!("肥満4度");
}
}
// スラッシュ3つで関数のコメントに切り替わる
// Markdownで記入できる
// この下の行から関数のコメントになる
/// ## コンソールに入力した文字列を数値に変換する関数
/// ### 引数
/// prompt: &str, 何の値を入力して欲しいかの説明
/// ### 戻り値
/// 数値
fn input(prompt: &str) -> f64 {
println!("{}", prompt); // 何の値を入力して欲しいかの説明を表示する
let mut s = String::new();
// std: 標準ライブラリ
// io: 入出力ライブラリ
// stdin(): 入出力を司るインスタンス
// read_line(&mut s): 文字列の入力を受け付ける関数。C#のoutと近い取り扱いのようだ
// expect("入力エラー"): パニック(想定された例外処理)を返す
std::io::stdin().read_line(&mut s).expect("入力エラー");
// trim(): 文字列の前後の空白、タブ、改行を取り除く
// parse::<f64>(): 浮動小数型に変換する
return s.trim().parse::<f64>().expect("数値変換エラー");
}
P159
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
ハッシュテーブルのお話。
IT系のお仕事だとよく使うのでしょうね。
いわゆる辞書型というやつですかね。
fn main() {
// ハッシュテーブルのインスタンス初期化
let mut c_map = HashMap::new();
// 下記の辞書キー登録までが1セットである。(登録しないとエラーになる)
c_map.insert("A", 0);
c_map.insert("B", 0);
c_map.insert("C", 0);
// 集計
for vote in V_DATA.split(',') {
c_map.insert(vote, c_map[vote] + 1);
// 下のは実装されていない
//c_map[vote] += 1;
}
// 結果を表示する
for key in ["A", "B", "C"] {
println!("{}: {:>2}", key, c_map[key]);
}
}
// ハッシュテーブルとは、データ構造の一つで、
// 標識(キー:key)と対応する値(value)のペアを単位としてデータを格納し、
// キーを指定すると対応する値を高速に取得できる構造。
use std::collections::HashMap;
// 投票データ
// 今回はハッシュテーブルの勉強のため、投票データは定数とする
const V_DATA: &str = "C,C,A,A,A,B,C,C,B,B,B,C,B,C,B,A,C,C,B,C,C,C";
P165
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
cargo run
の後に数字や文字列を入力すると、プログラム全体への引数になるようだ。
C言語のときからあったのかしら?
fn main() {
// args(): cargo run の後に空白やカンマ区切りで入力した文字(列)をプログラムの引数とする
let args = std::env::args();
let mut total = 0.0;
// for文のローカル変数にtupleを使うことができるのはモダン
// enumerate(): rangeのようなもの
for (index, string) in args.enumerate() {
if index == 0 {continue;}
let num: f64 = match string.parse() {
Ok(parsed_value) => parsed_value,
Err(_) => 0.0,
};
total += num;
}
println!("{}", total);
}
P188
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
再帰関数の呼び出し。
C言語を学んでいたときはよくわからなかったのですが、C#やPythonなどの経験が活きているのか、腹落ちできました。
fn main() {
println!("{}", sum(10));
}
/// # 再帰的に自然数を加えていく関数
/// 再帰関数は関数呼び出しが終わるまで続く
/// ## e.g. n = 10 の場合のアルゴリズム
/// ### 再帰的な関数の呼び出し
/// #### sum(n = 10)
/// 10 + sum(n = 9) を返す
/// #### sum(n = 9)
/// 9 + sum(n = 8) を返す
/// #### sum(n = 8)
/// 8 + sum(n = 7) を返す
///
/// ...(中略)...
/// #### sum(n = 1)
/// 1 + sum(n = 0) を返す
/// #### sum(n = 0)
/// 0 を返す
/// ### 計算結果の集約
/// #### sum(n = 1)
/// 1 + sum(n = 0) = 1 + 0 = 1 を返す
/// #### sum(n = 2)
/// 2 + sum(n = 1) = 2 + 1 = 3 を返す
/// #### sum(n = 3)
/// 3 + sum(n = 2) = 3 + 2 = 5 を返す
///
/// ...(中略)...
/// #### sum(n = 9)
/// 9 + sum(n = 8) = 9 + 36 = 45 を返す
/// #### sum(n = 10)
/// 10 + sum(n = 9) = 10 + 45 = 55 を返す
///
/// fin.
fn sum(n: i32) -> i32 {
if n <= 0 { return 0;}
return n + sum(n - 1);
}
P189
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
再帰関数その2。
フィボナッチ数列の再帰関数の例です。
再帰関数は、必ず呼び出しなしの戻り値を作る必要があるようですね。
fn main() {
for i in 1..20 {
println!("{}", fib(i));
}
}
fn fib(n: i64) -> i64 {
if n <= 1 { return 0;}
if n == 2 { return 1;}
return fib(n - 2) + fib(n - 1);
}
P194
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
main.rsのディレクトリにあるファイルを検索するようです。
use std::fs; // ディレクトリのファイルをごにょごにょするやつ
fn main() {
// ファイル一覧を取得するらしい
// expect: んー、何か腹落ちしない
let files = fs::read_dir(".").expect("不正なパスです");
for ent in files {
// unwrap: expectとセットのようだ。
// よくわからない
let entry = ent.unwrap();
let path = entry.path();
let file_name = path.to_str().unwrap_or("不正なファイル名");
println!("{}", file_name);
}
}
ちょっと疲れたので別の本に移ります
P14
ゼロから学ぶRust システムプログラミングの基礎から線形型システムまで
真偽評価の違いを示すプログラム。
非短絡評価を使う機会はあるのだろうか
fn main() {
println!("短絡評価");
println!("{}", call_a() || call_b());
println!("非短絡評価");
println!("{}", call_a() | call_b());
}
fn call_a() -> bool {
println!("call a");
true
}
fn call_b() -> bool {
println!("call b");
true
}
P25
ゼロから学ぶRust システムプログラミングの基礎から線形型システムまで
関数の引数に関数を取ることができるのは面白い
fn main() {
println!("Rustがモダンな言語であることを示す例。");
println!("C#の場合関数やメソッドを引数に取る場合ラムダ式に限られていたように思うが、Rustの場合全ての関数を引数に取ることができるようだ");
let a = 10;
let b = 2;
// 関数ポインタの説明とあるがよくわからない
println!("{}", do_it(add, a, b));
println!("{}", do_it(mul, a, b));
}
/// 2つの非負の整数を引数に取りかつ非負の整数を返す関数、およびその関数の引数を引数に取る関数
fn do_it(f: fn(u32, u32) -> u32, a: u32, b: u32) -> u32 {
f(a, b) // 面白い記入法。returnがそんなに嫌いですか?
}
/// 2つの非負の整数の和を返す関数
fn add(a: u32, b: u32) -> u32 {
a + b
}
/// 2つの非負の整数の積を返す関数
fn mul(a: u32, b: u32) -> u32 {
return a * b; // この書き方は安心する
}
P65
ゼロから学ぶRust システムプログラミングの基礎から線形型システムまで
所有権周りのお話。
プリミティブ型以外は所有権の貸し出しなどを考える必要がある。
fn main() {
// 準備
struct H2O {} // 水分子
struct O2 {} // 酸素分子
struct H2 {} // 水素分子
// 燃焼のクロージャー(関数)
// 引数の前にアンダーバーを入れるのはPythonっぽい
// 同じ名前の引数を使いまわしているからこのようなことになるのでしょう。
let burn = |_h2_1: H2, _h2_2: H2, _o2: O2| (H2O{}, H2O{});
// 具体例
let h2_1 = H2{}; // 水素分子その1
let h2_2 = H2{}; // 水素分子その2
let o2 = O2{}; // 酸素分子
// 水素分子を燃焼させる
// この関数はコンパイルできる
let (h2o_1, h2o_2) = burn(h2_1, h2_2, o2);
// 既に変数を消費しているので、同じ引数を2度使用することができない
//let (h2o_1, h2o_2) = burn(h2_1, h2_2, o2);
}
P65
ゼロから学ぶRust システムプログラミングの基礎から線形型システムまで
トレイト(インターフェイス)のお話。
構造体にメソッドやプロパティを付与する。
C#に比べると手間に感じる。
fn main() {
// 構造体をprintln!で表示するためのライブラリ
// 関数の内側にusing宣言を書くことができるのはモダン
use std::fmt::{Display, Formatter};
// 複素数構造体
struct ComplexNumber {
real: f64,
img: f64,
}
// 複素数構造体にトレイトを実装する
impl Display for ComplexNumber {
// println!で表示するためのプロパティの実装
fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
// write!マクロはformatter, 基本文字列, 値, 次の値, ... と書くようだ
write!(f, "{} + {}i", self.real, self.img)
}
}
// 具体例
let c = ComplexNumber {real: 3.0, img: 4.0};
println!("{}", c);
println!("{c}"); // この書き方もOK
}
P208
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
所有権の例。
C#だと参照型のため、g1を書き換えるとg2も書き換わるので注意してください、と言われた気がします。
他の言語での注意してくださいを、Rustでコンパイルエラーにしてくれるのは親切。
fn main() {
let g1 = String::from("穏やかな心は体に良い");
let g2 = g1; // 所有権をg2に移動する
println!("{}", g1);
}
P212
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
プリミティブ型(コンパイル時点でメモリサイズが分かる変数)は所有権移動が発生しないとのこと。
fn main() {
// スタック領域に値を保存するプリミティブ型は値コピーのため、
// 所有権の移動は起きない
let g1 = 30;
let g2 = g1;
println!("{}", g1);
println!("{}", g2);
}
P218
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
所有権移動が起きないように、 & をつけて参照渡しにする。
ふーんといった感じ。
fn main() {
// 所有権が移ってしまう変数を表示したい場合には、引数の頭に & をつける
let show_message = |message: &String| println!("クロージャーによる表示: {}", message);
// 具体例
let g1 = String::from("過ちを見過ごす人は美しい");
show_message(&g1); // &付引数
println!("main関数のハードコードでの表示: {}", g1); // 所有権が移っていないので、これを実行できる
}
P229, 230
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
タプルのあれこれ。
要素の呼び出しは、Pythonにおけるpandasの列名呼び出しに近い書き心地。
fn main() {
// タプルのインスタンス生成
let banana = ("バナナ", 300);
let apple = ("リンゴ", 200);
// 価格の合計
// Pythonのpandasの列名呼び出しに近い書き心地
let total = banana.1 + apple.1;
// タプルの要素を表示するクロージャー
// テキストに乗っている関数はクロージャーで書き直した方が楽
let print_tuple = |item: &(&str, i64)| println!("{}を{}円で購入", item.0, item.1);
// 表示
print_tuple(&banana);
print_tuple(&apple);
println!("合計{}円です", total);
}
P231
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
Tupleをフィールドにもつ構造体のあれこれ。
数をこなすとなんとなく頭に入ってきますね。
fn main() {
// 具体的なItem構造体の生成
let banana = Item("バナナ".to_string(), 300);
let apple = Item("リンゴ".to_string(), 200);
let mango = Item("マンゴー".to_string(), 500);
// 上記の構造体を可変長配列にまとめる
let items = vec![banana, apple, mango];
// 各Item構造体の商品名と価格を表示して、合計を計算する
let total = print_and_sum_items(&items);
println!("合計{}円です", total);
}
// 商品名と価格のタプル構造体
struct Item(String, i64);
// Item構造体の商品名と価格を表示する関数
// 構造体専用の関数を定義することで、トレイトを付与しなくて済む
fn print_tuple(item: &Item) {
println!("{}を{}円で購入", item.0, item.1);
}
// Item構造体の可変長配列Vecの価格の合計を計算する関数
fn print_and_sum_items(items: &Vec<Item>) -> i64 {
let mut total = 0;
for it in items {
print_tuple(&it);
total += it.1;
}
total
}
P231
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
P231のプログラムをタプルなしの構造体で書き直した。
しかし、文字列を表示する時のダブルクォーテーションが残っていたので、こちらはどこかおかしい。
どこがおかしいのだろうか。
fn main() {
// Item構造体を可変長配列にまとめる
let banana = Item{name: "バナナ".to_string(), price: 300};
let apple = Item{name: "リンゴ".to_string(), price: 200};
let mango = Item{name: "マンゴー".to_string(), price: 500};
let items = vec![banana, apple, mango];
// 各Item構造体の商品名と価格を表示して、合計を計算する
let total = print_and_sum_items(&items);
println!("合計{}円です", total);
}
// 商品名と価格をフィールドにもつ構造体
struct Item {
name: String,
price: i64,
}
use std::fmt; // println!マクロに書き込むための基本ライブラリ
impl fmt::Display for Item {
// Item構造体をprintln!マクロに割り当てた時に表示する値を定義する
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "商品名: {}, 価格:{}", self.name, self.price)
}
}
// Item構造体の可変長配列Vecの価格の合計を計算する関数
// Item構造体を書き換えたのに合わせて直している
fn print_and_sum_items(items: &Vec<Item>) -> i64 {
let mut total = 0;
for it in items {
println!("{}", &it);
total += it.price;
}
total
}
P233
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
配列の初期化の話。
ハードコーディングの場合は良いが、だんだんと外部変数で初期化したくなるが、どのようにしたら良いのだろうか。
fn main() {
// 配列の初期化
let points: [i32; 5] = [80, 40, 50, 90, 84];
print_array(&points);
}
/// 配列の各要素と、配列の要素数を表示する関数
fn print_array(array: &[i32; 5]) {
println!("配列の各要素 -> {:?}", array);
println!("配列の要素数 -> len = {}", array.len());
}
P236
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
スライス型の全要素の和を計算する関数周り。
これは便利。
Rustで数値計算を行う場合、スライスをいかにうまく扱うかが鍵になりそうだ。
fn main() {
// 配列の要素の総和を計算する
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
println!("array = {:?}\nthen, total is {}",
array, // 全要素を表示するために {:?} でよしなにやってもらう
sum_slice(&array[..])); // &変数[..] と書くと全要素のスライスを取得できる
let vector = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
println!("\nvector = {:?}\nthen, total is {}", vector, sum_slice(&vector[..]));
}
/// ## 整数のスライス型の総和を返す関数
/// スライス型のメリットは、**配列とベクトル型の両方を扱うことができる**こと。
/// ### 引数
/// items: &[i64] 整数のスライス型
/// ### 戻り値
/// i64 要素の合計
fn sum_slice(items: &[i64]) -> i64 {
let mut total = 0;
for i in items {
total += i;
}
total
}
P241
手を動かして考えればよくわかる 高効率言語 Rust 書きかた・作りかた
2章で計算したBMI計算を、構造体と関数で計算してみる話。
fn main() {
let ichiro = Body{weight: 80.0, height: 165.0};
let jiro = Body{weight: 65.0, height: 170.0};
println!("Ichiro = {:.1}", ichiro.calculate_bmi());
println!("Jiro = {:.1}", jiro.calculate_bmi());
}
// 体型の構造体
struct Body {
weight: f64,
height: f64,
}
impl Body {
/// BMIを計算する関数
fn calculate_bmi(&self) -> f64 {
self.weight / (self.height/100.0).powf(2.0)
}
//メソッド付与としてlambda式を実装すすることはできない
//let calculate_bmi = |&self| self.weight / (self.height/100.0).powf(2.0);
}
モジュールの構成
Rustにおけるモジュール分割を理解する | Bamboo Note
を見ながらモジュール管理 = フォルダ管理 を勉強中です。
IDE(Visual studio)の恩恵が無くなると大変ですね。
2023/5/1
自分が理解できていなかっただけで、引用元が正しかったです。
失礼いたしました。
crateでstructを公開しようとしましたが、not findのエラーが出る。
crates.ioの中で構造がわかりやすかったcrateを眺めてみる。
leafwing-input-manager
を見ると、rootディレクトリlib.rs
があるディレクトリと同じ階層にファイルを作成すると、ファイル名がモジュール名になるとのこと。
下のフォルダのデータを呼ぶ方法が分からないので、コードが長くなるけど、rootディレクトリと同じディレクトリにコードを書いていく。
IDE(Visual studio)に戻りたいよお
cargo publish
を行いすぎて回数制限を食らってしまったが、よく考えるとcargo doc --open
でローカル環境で確認すればよかったのでは?
IDE(Visual studio)に戻りたい
色々とCrateを公開したりひっこめたりを繰り返す中でなんとなくわかってきました。
やはり手を動かすことが大事ですね。
crateの方向性が定まりました
簡素なモジュール呼び出しで疑似乱数を使えるようにするライブラリです。
多くの素晴らしいライブラリが公開されている中での車輪の再発名としては、なかなか良い差別化ができたと思います。
P374 画像処理
画像を描画する場合、(x座標のピクセル数, y座標のピクセル数, 描画のクロージャー)で画像データを生成できるとのこと。
なかなか面白い。
use image;
fn main() {
// 白色をRgbで定義
let white = image::Rgb::<u8>([255, 255, 255]);
// 赤色をRgbで定義
let red = image::Rgb::<u8>([255, 90, 90]);
// 1マスのサイズ
let w = 64;
// 市松模様を描くクロージャー
let draw = |x, y| {
let (xi, yi) = (x / w, y / w);
match (xi % 2, yi % 2) {
(0, 0) => white,
(1, 0) => red,
(0, 1) => red,
(1, 1) => white,
(_, _) => panic!("error"),
}
};
// クロージャーを指定してImageBufferを生成
let img = image::ImageBuffer::from_fn(512, 512, draw);
// ファイルへ保存
img.save("ichimatsu.png").unwrap();
}
P386 波形合成
音声関係はhoundクレートを使うようです。
半公式のクレートが多いようです。
use std::f32::consts::PI;
use hound::WavWriter;
// 定数を宣言
const SAMPLE_RATE: u32 = 44100;
const TONE: f32 = 440.0;
fn main() {
// WAVファイルのフォーマットを指定
let spec = hound::WavSpec {
channels: 1,
sample_rate: SAMPLE_RATE,
bits_per_sample: 16,
sample_format: hound::SampleFormat::Int,
};
//WavWriterのオブジェクトを生成
let mut fw = WavWriter::create("a.wav", spec).unwrap();
// 連続でサイン波を書き込む
let samples = SAMPLE_RATE * 3;
for t in 0..samples {
let v = ( (t as f32 / SAMPLE_RATE as f32) * TONE * 2.0 * PI).sin();
fw.write_sample((v * i16::MAX as f32) as i16).unwrap();
}
}
P429 非同期処理の話
mpsc::channel::<(i64, i64)>()
の左側の変数がTaskのキーで、右側が結果のようだ。
send()
メソッドでTaskを依頼して、recv()
メソッドでTask結果を受け取るようだ。
難しい問題を解決するため、まずは基本を押さえておきたいと思います。
use std::sync::mpsc;
use std::thread;
use std::time::{Instant, Duration};
fn main() {
// 求めたいフィボナッチ数の一覧
let request_nums = [30, 10, 25, 4, 15, 20, 3];
// C#のDateTime.Nowに相当するインスタンス
let start_time = Instant::now();
// スレッド間通信のチャンネルインスタンス
// txが送信機で、rxが受信機のようだ
let (tx, rx) = mpsc::channel::<(i64, i64)>();
// 連続してスレッドを生成し計算を行う
for num in request_nums {
// 送信機の複製
// 一段高いスコープの複製であることが肝のようだ
let sender = tx.clone();
// スレッドを作成する
// C#のTask<T>に近い匿名関数のようだ。
thread::spawn (move || {
// 戻り値の値を束縛する
let answer = fib(num);
// タプルを送って、タプルを返すようだ
sender.send( (num, answer) ).unwrap()
});
}
// 生成したスレッドの数を数える
let mut job = request_nums.len();
// 計算結果を得る
// 送信したTaskが完了するまで無限ループを続ける
loop {
// rx.recv()がmpsc::channelで定義したTaskの戻り値の組のようだ
// Ok()関数は引数に下位のスコープで使用する変数を記入して、実態を = の右辺につなげることができる
if let Ok( (sent_num, answer) ) = rx.recv() {
job -= 1; // 残りTask数の計数
// Taskの結果を表示する
println!("[結果] fib ({}) = {} (残り = {})", sent_num, answer, job);
if job <= 0 {
show_time(start_time);
break;
}
}
// 各スレッドに対する待ち時間として、300ミリ秒のスリープを入れている?
thread::sleep(Duration::from_millis(300));
}
}
// 再帰的にフィボナッチ数を調べる関数
// 関数呼び出しの回数をg(n)とすると、指数関数的に増加するため、大きな引数は実用的ではない
// g(n = 1) = g(n = 2) = 1
// g(3) = 3
// g(4) = 6
// g(5) = 22
fn fib(n: i64) -> i64 {
if n == 1 {return 0;}
if n == 2 {return 1;}
return fib(n - 2) + fib(n - 1);
}
// 経過時間を表示する関数
fn show_time(start_time: Instant) {
let elapsed = start_time.elapsed();
println!("実行時間: {:?}", elapsed);
}
引数と実行時間の話
疑似乱数を計算する場合、乱数生成器の状態は可変である必要があるため、可変参照かCell
を引数に取ります。
Clippyから変数が多すぎるからタプルや配列にまとめてね、というアドバイスをもらったので、引数の違いによる実行時間を調べてみました。
結論としては、プリミティブ型引数のままで良いかと思いました。
結果
Function 1 execution time: 1417μs - プリミティブ型引数5個を可変参照で渡す
Function 2 execution time: 1437μs - 要素5個のタプルを可変参照で渡す
Function 3 execution time: 2408μs - セル型引数5個を可変参照で渡す
Function 4 execution time: 2413μs - セル型要素5個のタプルを可変参照で渡す
use std::cell::Cell;
use std::time::Instant;
fn main() {
// 関数の実行時間を計測する
let mut a: u32 = 1u32;
let mut b: u32 = 2u32;
let mut c: u32 = 3u32;
let mut d: u32 = 4u32;
let mut e: u32 = 5u32;
let start_time = Instant::now();
for _ in 0..100_000 {
function_1(&mut a, &mut b, &mut c, &mut d, &mut e);
}
let elapsed_time = start_time.elapsed().as_micros();
println!("Function 1 execution time: {}μs - プリミティブ型引数5個を可変参照で渡す", elapsed_time);
let mut abced: (u32, u32, u32, u32, u32) = (1, 2, 3, 4, 5);
let start_time = Instant::now();
for _ in 0..100_000 {
function_2(&mut abced);
}
let elapsed_time = start_time.elapsed().as_micros();
println!("Function 2 execution time: {}μs - 要素5個のタプルを可変参照で渡す", elapsed_time);
let cell1 = Cell::new(1);
let cell2 = Cell::new(2);
let cell3 = Cell::new(3);
let cell4 = Cell::new(4);
let cell5 = Cell::new(5);
let start_time = Instant::now();
for _ in 0..100_000 {
function_3(&cell1, &cell2, &cell3, &cell4, &cell5);
}
let elapsed_time = start_time.elapsed().as_micros();
println!("Function 3 execution time: {}μs - セル型引数5個を可変参照で渡す", elapsed_time);
let cell_tuple = (
Cell::new(1),
Cell::new(2),
Cell::new(3),
Cell::new(4),
Cell::new(5),
);
let start_time = Instant::now();
for _ in 0..100_000 {
function_4(&cell_tuple);
}
let elapsed_time = start_time.elapsed().as_micros();
println!("Function 4 execution time: {}μs - セル型要素5個のタプルを可変参照で渡す", elapsed_time);
}
fn function_1(var1: &mut u32, var2: &mut u32, var3: &mut u32, var4: &mut u32, var5: &mut u32) -> u32 {
let sum = *var1 + *var2 + *var3 + *var4 + *var5;
*var1 = sum % 10;
sum
}
fn function_2((var1, var2, var3, var4, var5): &mut (u32, u32, u32, u32, u32)) -> u32 {
let sum = *var1 + *var2 + *var3 + *var4 + *var5;
*var1 = sum % 10;
sum
}
fn function_3(var1: &Cell<u32>, var2: &Cell<u32>, var3: &Cell<u32>, var4: &Cell<u32>, var5: &Cell<u32>) -> u32 {
let sum = var1.get() + var2.get() + var3.get() + var4.get() + var5.get();
var1.set(sum % 10);
sum
}
fn function_4(tuple: &(Cell<u32>, Cell<u32>, Cell<u32>, Cell<u32>, Cell<u32>)) -> u32 {
let sum = tuple.0.get() + tuple.1.get() + tuple.2.get() + tuple.3.get() + tuple.4.get();
tuple.0.set(sum % 10);
sum
}
元のChatGPTへの質問
Rustの質問です。
次の条件を満たすコードを書いてください。
①4つの関数をmain関数で実行する。
②それぞれの関数を10万回実行して、10万回分の実行時間を計測する
③main関数の最後に2つの関数の実行時間をprintln!で表示する
④1番目の関数は、u32型の変数5個を可変参照で引数に取り、5個の変数の和を返す。
さらに、1番目の変数を5個の変数の和を10で割った余りに書き換えます。
さらに、引数は別々の変数とします
⑤2番目の関数は、u32型5個のタプルを可変参照で引数に取り、5個の要素の和を返す。
さらに、1番目の要素を5個の要素の和を10で割った余りに書き換えます。
⑥3番目の関数は、Cell<u32>型の変数5個を可変参照で引数に取り、5個の変数の和を返す。
さらに、1番目の変数を5個の変数の和を10で割った余りに書き換えます。
さらに、引数は別々の変数とします
⑦4番目の関数は、Cell<u32>型5個のタプルを可変参照で引数に取り、5個の要素の和を返す。
さらに、1番目の要素を5個の要素の和を10で割った余りに書き換えます。