RustはC言語、C++の置換を目指したProgramming Language。
- 性能
- メモリ安全性
- 安全な並行性
を特徴とする。
Zigと方向性が同じ。
- https://github.com/kd-collective/book-summary/blob/db03f5c7e95ee8ebad5d446e7ed1ddc2a0d49561/src/main.rs#L222
- book-summaryを実行したときにエラーになる
- 配列のインデックスがないといっている
- tomlが含まれているところで実行するとだめっぽい
- 該当箇所的にもそう
- 設定にtitleがないとエラーになる
- だが、titleがなくてもbuildはできる
- メインツールでオプショナルなのに依存するのはおかしいし、不親切なのでせめてメッセージを出すようにしたい
新しいマシンにしたとき発生した。原因不明。ライブラリ名は若干変わるときがある。共通しているのはcustom build commandということ。手元のビルドだけでなく、cargo installも失敗する。
さっぱりわからない。検索しても似たような現象の人は出ない。開発用のライブラリを入れたら直ったという記事もあったが、すでに入っている。
- バージョンを下げてもだめだった
- curlで入れる方法をやり直してもだめだった
- aptで入れ直してもだめだった
- RustのCloneとCopyについての素朴な疑問の回答からコピペ
まず明確にしておかなけばならないのは、Copyトレイトを実装した型で行われる値のコピーと、Cloneトレイトを実装した型で行われる値のclone()は多くの場合は異なる操作だということです。
前者はメモリ上のバイト列を単純にコピーする操作で、Cのmemcpyに相当します。このような操作は一般的にはshallow copy(浅いコピー)と呼ばれます。
後者はその型に実装されたclone()メソッドを呼び出す操作です。どういうことをするかはclone()メソッドの実装に依存しますが、多くの場合はmemcpyよりも複雑なdeep copy(深いコピー)を行います。
たとえばString型は文字列データをVec<u8>型で持ちます。Vec<T>型はT型の各要素を格納するメモリ領域と、3つの要素を持つ構造体で表現されています。その構造体の要素は、前述のメモリ領域を指すポインタ、Vec<T>の容量(capacity)、Vec<T>の長さになっています。
仮にStringがCopyトレイトを実装できたとすると、shallow copyによってコピーされるのはVec<u8>の構造体の部分だけになります。u8型の各要素を格納するメモリ領域はコピーされず、また構造体の中にあるポインタも(memcpy相当なので)みな同じアドレスを指します。つまり、コピーによって作られた複数のStringが、同じu8型の要素を共有することになります。
Copyトレイトはmemcpy相当の操作だけで完全にコピーできる型にしか実装できないようになっています。たとえばu8型の値はmemcpyでコピーすれば十分(他にコピーするものがない)なのでCopyトレイトが実装されています。一方、Vec<T>はmemcpyでは不十分(shallow copyになる)なのでCopyトレイトを実装できません。
一方、Vec<T>のclone()メソッドはdeep copyを行うように実装されています。各要素を格納するメモリ領域が新たに割り当てられ、構造体の値も新たに作られます。また個々の要素も、そのclone()メソッドを呼ぶことでコピーされます。つまり、clone()によって作られた複数のStringは、それぞれが異なるメモリ領域にあるu8型の要素を持つことになります。
さて本題のRustの配列型の初期化構文[s; 2]では、なぜsのところにCopyトレイトを実装した型しか受け付けないかですが、その理由はドキュメントに書かれてないので、あくまでも私の推測を元に説明します。
Rustの配列はあらかじめ言語に組み込まれている型(プリミティブ型)です。また、初期化の構文[s; 2]も言語に組み込まれています。そのような構文からは、clone()のようにRustコードで実装されたメソッドを呼び出すことができないのかもしれません。メソッドは呼べないので、型の実装に依存しないmemcpy相当のコピーを行うのではないでしょうか。memcpy相当の操作で完全にコピーできることを保証するには、sがCopyトレイトを実装していなければなりません。
Vec<T>には配列の初期化にそっくりのvec![s; 2]の構文があります。Vec<T>はプリミティブ型ではなく、Rustコードで実装されたユーザー定義型です。またvec![s; 2]はマクロで実装されており、これもコンパイル時にRustコードに変換されます。そのためだと思いますが、vec![s; 2]ではsがCopyを実装している必要はなく、Cloneを実装していれば初期化できます。
マクロの例。identはidentityの略。
https://github.com/kd-collective/rust/blob/a3beeaa84da241f35888338ded6659938206ff13/src/librustdoc/lint.rs#L66-L83
printlnマクロの定義。
https://github.com/kd-collective/rust/blob/a3beeaa84da241f35888338ded6659938206ff13/library/std/src/macros.rs#L101-L108
Rustにおいて参照はすべてライフタイムを保持する。ライフタイムは、その参照が有効になるスコープ。ライフタイムも暗黙的に推論される。複数の型の可能性があるときには、型を注釈しなければならない。
ライフタイムの主な目的は、ダングリング参照…参照するつもりだったデータ以外のデータを参照してしまうこと…を回避すること。コンパイラは借用チェッカーによってチェックしてエラーを出す。
fn main() {
{
let r;
{
let x = 5;
r = &x;
}
println!("r: {}", r);
}
}
関数のジェネリックなライフタイム。
// 引数は参照である。longest関数に引数の所有権を奪ってほしくないから
fn longest(x: &str, y: &str) -> &str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}
↑戻り値の型はジェネリックなライフタイム引数である引数であるといっている。返している参照が x
か y
のどちらを参照しているか、コンパイラにはわからないから。ifブロックは x
への参照を返し、elseブロックは y
への参照を返すので、どちらかわからない。
エラーを修正するためには、借用チェッカーが解析できるように、参照間の関係を定義するジェネリックなライフタイム引数を追加する。ライフタイム注釈は、参照の生存期間を変えることはない。ライフタイム注釈は、ライフタイムに影響することなく、複数の参照のライフタイムのお互いの関係を記述する。
ライフタイム引数の名前はアポストロフィーで始まらなければならず、通常全て小文字で、ジェネリック型のように短い。慣例的に 'a
という名前を使う。
&i32 // ただの参照
&'a i32 // 明示的なライフタイム付きの参照
&'a mut i32 // 明示的なライフタイム付きの可変参照
ライフタイム注釈をつける。
- 何らかのライフタイム’aに対して、関数は2つの引数を取り、どちらも少なくともライフタイム’aと同じだけ生きる文字列スライスであるとコンパイラに教えるようになった
- 返る文字列スライスもライフタイム’aと同じだけ生きると、コンパイラに教えている。実際にはlongest関数が返す参照のライフタイムは、渡された参照のうち、小さいほうのライフタイムと同じということになる
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}
main();
- ライフタイム引数を指定するとき、いかなる値のライフタイムも変更していない。longest関数は、
x
とy
の正確な生存期間を知っている必要はなく、このシグニチャを満たすようなスコープを’aに代入できることを知っているだけ - 関数にライフタイムを注釈するときは、注釈は関数の本体ではなくシグニチャに付与する
- コンパイラは注釈がなくとも関数内のコードを解析できる。が、関数に関数外からの参照や関数外への参照がある場合、コンパイラが引数や戻り値のライフタイムも自力で解決することはほとんど不可能になる。
- そのライフタイムは関数が呼び出されるたびに異なる可能性があるので、手動でライフタイムを注釈する必要がある
トレイト:共通の振る舞いを定義する - The Rust Programming Language 日本語版
トレイトを使用すると、あるジェネリックが、特定の振る舞いをもつあらゆる型になり得ることを指定できる。
pub trait Summary {
fn summarize(&self) -> String;
}
トレイトを型に実装する。
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
// impl トレイト for 構造体
impl Summary for NewsArticle {
fn summarize(&self) -> String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
fn main() {
let article = NewsArticle {
headline: String::from("Big news!"),
location: String::from("Tokyo"),
author: String::from("Me"),
content: String::from("Birthday"),
};
println!("1 new news: {}", article.summarize());
let tweet = Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
};
println!("1 new tweet: {}", tweet.summarize());
}
main()
制約: 外部のトレイトを外部の型に対して実装できない。コヒーレンス、孤児のルールと呼ばれる特性の一部。この制約によって、他の人のコードが自分のコードを壊したり、その逆が起きないことを保証する。
デフォルト実装。各メソッドのデフォルト実装があると、すべての型に対して実装を要求しないので便利。
pub struct NewsArticle {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
impl Summary for NewsArticle { }
fn main() {
let article = NewsArticle {
headline: String::from("Big news!"),
location: String::from("Tokyo"),
author: String::from("Me"),
content: String::from("Birthday"),
};
println!("New article available! {}", article.summarize());
}
main()
デフォルト実装は、自らのトレイトのデフォルト実装を持たない他のメソッドを呼び出すことができる。↑の場合は実装メソッドがないため、デフォルト実装が使われた。
一部だけデフォルト実装にする例。
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("Read more from {}...", self.summarize_author())
}
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
}
fn main() {
let tweet = Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
};
println!("summarize: {}", tweet.summarize());
println!("summarize_author: {}", tweet.summarize_author());
}
main()
引数itemのsummarizeメソッドを呼ぶ関数notifyを定義する。引数itemはSummaryトレイトを実装している何らかの型。
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("Read more from {}...", self.summarize_author())
}
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
}
// 引数: &impl トレイト
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}
// ↑と等価で、冗長に書いたバージョン。トレイト境界
// 山カッコの中にジェネリックな型引数の宣言を書き、型引数の後ろにコロンを挟んでトレイト境界を置く
// pub fn notify<T: Summary>(item: &T) {
// // 速報! {}
// println!("Breaking news! {}", item.summarize());
// }
fn main() {
let tweet = Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
};
notify(&tweet);
}
main()
トレイトを実装している型を返す。impl Trait構文を戻り値型のところで使うことで、あるトレイトを実装する何らかの型を返す。
pub struct Tweet {
pub username: String,
pub content: String,
pub reply: bool,
pub retweet: bool,
}
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("Read more from {}...", self.summarize_author())
}
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
}
// impl Trait構文を戻り値型のところで使うことで、**あるトレイトを実装する**何らかの型を返す
// 具体的な型を指定してないところがポイント
// これはクロージャとイテレータを扱うときに特に便利。ある関数はIteratorトレイトを実装するある型を返すのだ、と簡潔に指定できる
fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
}
}
fn main() {
let tweet = returns_summarizable();
println!("result: {}", tweet.summarize_author());
}
main()
- ただしimpl Traitの制約として、1種類の型を返す場合にのみ使える
関数に渡したスライスの値の型が、PartialOrdとCopyを実装する限りコンパイルできる、ジェネリックなlargest関数。
fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("The largest char is {}", result);
}
main()
トレイト境界を使用して、メソッド実装を条件分けする。
- Pair<T>は常にnew関数を実装する。
- Pair<T>は、内部の型Tが比較を可能にするPartialOrdトレイトと出力を可能にするDisplayトレイトを実装しているときのみ、cmp_displayメソッドを実装する。
use std::fmt::Display;
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T>{
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}
impl <T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {}", self.x);
} else {
println!("The largest member is y = {}", self.y);
}
}
}
fn main() {
let pair = Pair{ x: 1, y: 2};
pair.cmp_display();
}
main();
別のトレイトを実装するあらゆる型に対するトレイト実装を条件分けできる。トレイト境界を満たすあらゆる型にトレイトを実装することは、ブランケット実装と呼ばれ、Rustの標準ライブラリで広く使用される。
https://github.com/kd-collective/rust/blob/8b954910c59a7a362c60959e93110892b6e9a691/library/alloc/src/string.rs#L2388-L2402
整数はDisplayを実装するので、整数値を対応するString値に変換できる。
fn main() {
println!("{}", 3.to_string());
}
main();
ジェネリック型、トレイト、ライフタイム - The Rust Programming Language 日本語版
enum Option<T> {
Some(T),
None,
}
型Tの値を保持するSomeと、値を何も保持しないNone。
複数のジェネリックな型を使用できる。
enum Result<T, E> {
Ok(T),
Err(E),
}
メソッド定義にも使える。
struct Point<T> {
x: T,
y: T,
}
impl<T> Point<T> {
fn x(&self) -> &T {
&self.x
}
}
fn main() {
let p = Point { x: 5, y: 10};
println!("p.x = {}", p.x());
}
match制御フロー演算子 - The Rust Programming Language 日本語版
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u32 {
match Coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
値に束縛されるパターン。Quarterが保持するenumを増やす。
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
}
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u32 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
// stateに束縛されるのは、UsState::Alabama
println!("state quarter from {:?}!", state);
25
},
}
}
value_in_cents(Coin::Quarter(UsState::Alabama))
Option<T>とのマッチ。
- マッチは包括的なので、もしNoneアームがなかったとしたらエラーを出してくれる。
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
fn main() {
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}
単にif letで短く書ける。
if let Some(thing) = thing {
// 中身の値があるとき実行
}else {
// 中身の値がないとき実行
}
Enumを定義する - The Rust Programming Language 日本語版
enum IpAddr {
V4(String),
V6(String),
}
fn main () {
let home = IpAddr::V4(String::from("127.0.0.1"));
let loopback = IpAddr::V6(String::from("::1"));
}
main()
別の例。
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
Optionも、標準ライブラリにより定義されているEnum。初期化処理(prelude)に含まれているため、明示的にスコープに導入する必要がない。
https://github.com/kd-collective/rust/blob/8b954910c59a7a362c60959e93110892b6e9a691/library/core/src/option.rs#L514-L523
Option値を使って数値型や文字列型を保持する例。
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
Option<T>とTは異なる。Option<T>を使うためには変換が必要になる。nullである場合を明示的に処理する必要がある。
メソッド記法 - The Rust Programming Language 日本語版
newはない。
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle { width: 30, height: 50 };
println!("The area of the rectangle is {} square pixels.", area(&rect1));
}
fn area(rectangle: &Rectangle) -> u32{ rectangle.width *
rectangle.height }
main()
構造体で情報出力するために、debug注釈を追加する。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect = Rectangle { width: 1, height: 1};
println!("rect is {:?}", rect);
}
main()
構造体上にメソッドを実装する。
#[derive(Debug)]
struct Rectangle {
width: i32,
height: i32,
}
impl Rectangle {
// メソッドなので、selfはRectangle。
fn area(&self) -> u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle{ width: 1, height: 1 }
println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}
新しいメソッド。
#[derive(Debug)]
struct Rectangle {
width: i32,
height: i32,
}
impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let rect1 = Rectangle{ width: 30, height: 50 };
let rect2 = Rectangle{ width: 10, height: 40 };
let rect3 = Rectangle{ width: 60, height: 45 };
println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}
main()
ライフタイムを使うと、構造体に他の何かに所有されたデータへの参照を保持させることができる。
構造体を定義し、インスタンス化する - The Rust Programming Language 日本語版
また、一切フィールドのない構造体を定義することもできます!これらは、()、ユニット型と似たような振る舞いをすることから、 ユニット様構造体と呼ばれます。ユニット様構造体は、ある型にトレイトを実装するけれども、 型自体に保持させるデータは一切ない場面に有効になります。トレイトについては第10章で議論します。
pub struct Monster {}
コンパイル前に動的にコードを展開して、実行することで柔軟性を得られる。
https://github.com/kd-collective/specs/blob/f07c484ad3caf8dccbf55c96af4ef1e3475dea28/specs-derive/src/lib.rs#L56-L57
https://github.com/kd-collective/specs/blob/f07c484ad3caf8dccbf55c96af4ef1e3475dea28/specs-derive/src/lib.rs#L71-L75
unwrap() は、 Option<T> 型や Result<T, E> 型の値(つまり、何かしらの値を ラップ している値)から中身の値を取り出す関数。たとえば Option<T> 型の値に対して unwrap() を呼ぶと、それが内包する T 型の値を返す。それらの型には値が入ってない可能性もあり、入ってない場合にはunwrapは失敗する。
rust - Rustの”unwrap()”は何をするものですか? - スタック・オーバーフロー
反復子を使用する - Learn | Microsoft Docs
trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
#[derive(Debug)]
struct Counter {
length: usize,
count: usize,
}
impl Counter {
fn new(length: usize) -> Counter {
Counter {
count: 0,
length,
}
}
}
impl Iterator for Counter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count <= self.length {
Some(self.count)
} else {
None
}
}
}
fn main() {
for number in Counter::new(10) {
println!("{}", number);
}
}
スマートポインタはポインタのように振る舞うだけでなく、追加のメタデータと能力があるデータ構造。 スマートポインタ - The Rust Programming Language 日本語版
Rustでは、boxを使う。
fn main() {
let b = Box::new(5);
println!("b = {}", b);
}
Rustでスマートポインタを利用するのに使う。 スタックではなくヒープにデータを保存する。
使う場面。
- コンパイル時にはサイズを知ることができない型があり、正確なサイズを要求する文脈でその型の値を使用するとき
- 多くのデータがあり、その所有権を移したいが、その際にデータがコピーされないようにしたいとき
- 値を所有する必要があり、特定の型であることではなく、特定のトレイトを実装する型であることのみ気にかけているとき
ヒープのデータを指すBox<T>を使用する - The Rust Programming Language 日本語版
変更したら自動でcargo runしてほしいときがある。
cargo install cargo-watch
cargo watch -x run
println!("hello world!");
println!("{} days", 31);
println!("{0}, this is {1}, {1}, this is {0}", "Alice", "Bob");
println!("{} of {:b} people know binary, the other half doesn't", 1, 2);
println!("{number:>0width$}", number=1, width=6);
println!("{subject} {verb} {object}", object="the lazy dog", subject="the quick brown fox", verb="jumps over");
引数チェックもしてくれる。
println!("My name is {0}, {1} {0}", "Bond");
マーカーの変更。
println!("This struct `{}` won't print...", Structure(3));
#[derive(Debug)]
struct Structure(i32);
println!("This struct `{:?}` won't print...", Structure(3));
どういうわけかデフォルトで展開してくれないので、ライブラリで変換する必要がある。
extern crate shellexpand;
fn main() {
let cwd = format!("{}", shellexpand::tilde("~/"));
Command::new("ls").current_dir(&cwd);
}
OS間のディレクトリの違いを吸収するライブラリ。ミニマルでコードを読みやすい。
EmacsのC言語で書かれた部分をRustに書き直すリポジトリがある。 remacs/remacs: Rust Emacs
高速検索するripgrepは、ほかのどのgrepツールより早いらしい。
- repository
- BurntSushi/ripgrep
System Craftersの動画Integrating Nushell with Emacsのコラボ回で出たパッケージ。 リッチな出力形式、便利コマンドがすごい。
- repository
- nushell/nushell: A new type of shell
借用の解説。
Rustコンパイラ開発のガイド。
OSをrustで書く本。詳しく書かれている。
何かしたいときにうまく型を見つけるためにはどうしたら良いのか。ドキュメントを見てもいまいちわからない。
文字列もしくは整数、みたいな型はどうやって表現するのか。TypeScriptでいうところのunion型みたいな。
ECSとSpecsのドキュメント。
Microsoftのチュートリアル。- 10
RustでのUI作成はなぜつらいか。
デバッグで便利そう。
[[https://qiita.com/yagince/items/e7474839246ced595f7a][[Rust] &strとStringを理解しようと思ったらsliceやmutを理解できてないことに気づいた話 - Qiita]]
入門本。
コマンドラインプログラムを作るチュートリアル。 fn main() {
let body = Body::new(163.0, 75.2, "田中");
body.print_result();
let body = Body::new(158.2, 55.0, "鈴木");
body.print_result();
let body = Body::new(174.2, 54.2, "井上");
body.print_result();
}
struct BmiRange {
min: f64,
max: f64,
label: String,
}
impl BmiRange {
fn new(min: f64, max: f64, label: &str) -> Self {
BmiRange{ min, max, label: label.to_string() }
}
fn test(&self, v: f64) -> bool {
(self.min <= v) && (v < self.max)
}
}
struct Body {
height: f64,
weight: f64,
name: String,
}
impl Body {
fn new(height: f64, weight: f64, name: &str) -> Self {
Body{ height, weight, name: name.to_string() }
}
fn calc_bmi(&self) -> f64 {
self.weight / (self.height / 100.0).powf(2.0)
}
fn print_result(&self) {
let bmi = self.calc_bmi();
let bmi_list = [
BmiRange::new(0.0, 18.5, "低体重"),
BmiRange::new(18.5, 25.0, "普通体重"),
BmiRange::new(25.0, 30.0, "肥満1度"),
BmiRange::new(30.0, 35.0, "肥満2度"),
BmiRange::new(35.0, 40.0, "肥満3度"),
];
let mut result = String::from("不明");
for range in bmi_list {
if range.test(bmi) {
result = range.label.clone();
break;
}
}
println!("{}さん、 BMI={:.1}, 判定={}",
self.name, bmi, result);
}
}
main()
struct Counter {
value: i64,
}
impl Counter {
fn new() -> Self {
Counter { value: 0 }
}
fn inc(&mut self) {
self.value += 1;
println!("value={}", self.value);
}
}
fn count(counter: Option<&mut Counter>) {
match counter{
None => return,
Some(c) => c.inc(),
};
}
fn main() {
let mut a = Counter::new();
count(Some(&mut a));
count(Some(&mut a));
let a = None;
count(a);
}
main();
roguelikeを作る長大なチュートリアル。 とりあえず14章までやり、理解を確かめるため自作改造フェーズに入った。 残りの部分はチュートリアルとしてやるというより、自作するうえで都度参照していく。
git cloneをコード管理するツール。初期化したときに、再度cloneしまくるのがメンドいため。とりあえず完了。