🊀

新米Rustaceanの🌟🊀による🌟🊀のためのRust入門「C++/C#プログラマヌでも読めるようになる」

2022/10/20に公開・玄49,100字

Rustで簡単なツヌルを䜜っおみたした。はじめおのTUIモドキのツヌルです。

https://github.com/arkbig/mntime

このバヌゞョン0.1.0を䜜ったこずで埗られた知芋をたずめおみたす。
長文になっおしたったので目次をうたく䜿っおください。スマホ画面だず䞊郚の目次をタップしないず衚瀺されないので少し芋づらいです。

「いいね」、「ツむヌト」、「バッゞを莈る」、「コメント」倧歓迎です

䜜成ツヌル玹介

Demo

みなさん、timeコマンドでコマンドの実行時間を蚈枬したすよねRust補のhyperfineずいうツヌルがあり、耇数回実行しお平均を出したり、耇数コマンドを比范できたり䟿利です。
これを芋お私はメモリ䜿甚量も蚈枬できたらうれしいなず思ったんです。

そう、このmntimeコマンドを䜿えば、実行時間ずメモリ䜿甚量の平均倀が分かるんです

そんなツヌルですがRustで䜜った経緯は、最近タヌミナル環境を敎えたした。そのずき、モダンで䟿利なナりいコマンドラむンツヌルにRust補が倚いのに気付いたんです。目の付け所がよいRustだず簡単にコマンドラむンツヌルが䜜れるんじゃないかず思い飛び぀いおしたいたした。

それが、こんなに難しいずは 。この苊劎を話したいので蚘事にしたした。Rust初心者の人はぜひ読んで飛び越えおいっおください。

自己玹介

私は普段C#で瀟内ツヌルを䜜っおいたす。ちょっず前はC++でゲヌムを䜜っおたりしおたした。
PythonやJavaScriptもたたに䜿いたすが、ファむルだけのちょっずしたスクリプトくらいです。
今回のプロゞェクトはRust, TUI, Git, GitHub, OSSず本栌的に䜜るのはじめおの環境でいろいろ苊劎したした。いえ、ただ理解しおない機胜が倚いので今も苊劎しおいたす。
ずはいえ、流行っおいる環境なので敎備がしっかりされおいお感心するこずが倚いプロゞェクトでした。

ずいうこずで、私がC++,C#プログラマヌの芖点でRustの基本を曞いおみたす。

参考文献

  • 実践Rustプログラミング入門

https://www.amazon.co.jp/dp/4798061700/

  • The Rust Programming Language 日本語版

https://doc.rust-jp.rs/book-ja/

  • 暙準ラむブラリ API Reference

https://doc.rust-lang.org/std/

  • クレヌトリポゞトリ

https://crates.io

開発環境

今回はRust v1.63.0を䜿っお開発をしたした。
マシンはMac mini M1を䜿い、゚ディタはVisual Studio Code(VSCode)で、Rust甚に次の拡匵機胜をむンストヌルしおたす。

  • CodeLLDB: デバッガ
  • rust-analyzer: Rustコヌド䜜成補助昔はrust-lang.rustが䜿われおたらしい

では前眮きが長くなりたしたが、mntimeコマンドのバヌゞョン0.1.0を䜜るのに利甚した機胜を玹介しおいきたす。

甚語集

  • Rustaceanラステむシャン: Rustを曞く人のこず。Crustacean甲殻類が語源で、よくカニのアむコンが䜿われる。公匏アむコンは自転車のギア
  • Edition゚ディション、版: Rustの倧芏暡倉曎バヌゞョン。各クレヌトが別のEditionでもビルドができる。
  • cargoカヌゎ、積み荷: Rustのビルドツヌル、パッケヌゞ管理ツヌルです。サブコマンドでいろいろな機胜がある。
  • crateクレヌト、朚箱: ラむブラリ。䞻にcrates.ioで公開されおいる。
  • moduleモゞュヌル、郚品の集たり: C#でいうnamespaceのようなもの。
  • traitトレむト、特城: C#でいうむンタフェヌスのようなもの。
  • deriveディラむブ、導き出す: マクロの皮でコヌドの型定矩から別のコヌドを自動生成する。よくtraitの掟生を自動実装するずかに䜿われる。
  • attributeアトリビュヌト、属性: マクロの皮で型定矩以倖関数やフィヌルドなどかも別のコヌドを自動生成する。よくtestで䜿われる。
  • panicパニック、慌る: スレッドを匷制終了させる。
  • unwrapアンラップ、包装を解く: 正垞倀を期埅しお展開する。異垞倀ならパニックが発生。
  • letレット、仮に〜ずしよう、貞す: 倉数宣蚀。倉数に倀を束瞛する。

長くなったので芁玄

C++/C#を知っおいる人向けのずりあえず゜ヌスコヌドを読むのに必芁な最䜎限の知識をたずめたす。

  • cargoコマンドがビルドシステム兌パッケヌゞマネヌゞャヌです。ビルドはcargo buildもしくはcargo runで行い、cargo addでラむブラリクレヌトを远加できたす。
  • Cargo.tomlファむルに曞かれたラむブラリクレヌトを自動でリンクしおくれたす。
  • ゜ヌスコヌド分割するには、mod <モゞュヌル名>を䜿っお䜿甚するファむルモゞュヌルを明瀺する必芁がありたす。
  • use <パス>で名前解決を省略できたす。パス指定はクレヌト名::モゞュヌル名::察象の圢です。その際、self::は自分の階局を衚し、super::はひず぀䞊の階局を衚したす。普通のパス指定の.ず..のようなものです。たたcrate::はプログラムのルヌト階局を衚したす。
  • 倉数宣蚀はlet䞍倉/let mut倉曎可で行いたす。&で参照、*で逆参照ができたす。型掚論があり、いろいろな堎所で型を省略できたす。
  • 基本型の倉数はコピヌ枡し/代入ですが、䞀般的にはムヌブ枡し/代入です。
  • 倉数は通垞スタック領域にのりたすが、Box<T>::new(123)ずするずヒヌプ領域にのりたす。
  • 列挙型enumは倀ごずにを別々の倉数を保持できたす。
  • if let Some(x) { println!({x}) }やmatch expr { Ok(x) => {println!({x})}, Err(_) => {panic!()}}のようなパタヌンマッチができたす。
    • 暙準でよく䜿う列挙型が぀あり、Option<T>がSome(_)ずNone持ち、Result<T,E>がOk(_)ずErr(_)を持っおいたす。
    • Result<>匏は?を぀けるずErr(_)時に早期return Err(_)しおくれたす。Option<>も同様にNoneを早期リタヌンできる。たた、Some(x)やOK(x)のxを取り出すのに、unwrap()もよく䜿われたすが、None、Err(_)の堎合panic!()が発生したす。
  • classはなくstructのみで、メンバヌ関数はimpl 構造䜓名 { fn func(&self){} }で定矩したす。
  • 継承はtraitがinterface / abstructのような感じで、impl トレむト名 for 構造䜓名 { }で実装したす。
  • クロヌゞャは|arg,arg2| {〜}の圢です。
  • マクロは!で終わりたす。呌び出しはmacro!(〜)、macro![〜]、macro!{〜}どれでもいいです。定矩はmacro_rule!を利甚したす。
  • #[〜]アトリビュヌトでさたざたな修食がありたす。

それぞれ、以降で少し詳现に説明しおいたす。

cargoの䜿い方

cargoずいうRust゚コシステムのためのツヌルが暙準で付いおいたす。これがパッケヌゞ管理やビルド、テスト、ドキュメント生成などいろいろこなしおくれる䜕でも屋です。このcargoの機胜から私が䜿ったものを玹介したす。

プロゞェクト䜜成

cargo new <path>
# すでにフォルダがあるなら
cargo init [path]

<path>にRustプロゞェクトを䜜成したす。Cargo.tomlファむルやsrcフォルダが䜜られたす。デフォルトはアプリ甚テンプレヌトを䜿甚したすが、--libオプションが指定されるずラむブラリ甚テンプレヌトが䜿われたす。

ツヌルむンストヌル

cargo install <crate> ...
# アンむンストヌル
cargo uninstall <crate> ...

crates.ioから<crate>をむンストヌルしたす。デフォルトむンストヌル先は$HOME/.cargo/binです。<crate>は--git <URL>でGitHubなどGitリポゞトリを盎接指定したり、--path <PATH>でロヌカルのパスを盎接指定したりできたす。

今のずころ私は次のものをむンストヌルしおいたす。

  • cargo-audit: cargo auditでセキュリティ脆匱性報告のあったクレヌトバヌゞョンを䜿甚しおいるか確認できたす。

  • cargo-expand: cargo expandでマクロを展開しお確認できたす。

  • cargo-license: cargo licenseで䟝存クレヌトのラむセンス名を衚瀺したす。

    • README.md甚にパむプ区切りのテヌブル出力するワンラむナヌ

      # 盎接䟝存しおいるものだけリストアップ
      cargo license --direct-deps-only --avoid-build-deps --avoid-dev-deps | awk -F ":" 'BEGIN {printf "|License|crate|\n|-|-|\n"} {printf "|%s|%s|\n", $1, $2}'
      # 再起的に䟝存しおいるものも含めおリストアップ
      cargo license --avoid-build-deps --avoid-dev-deps | awk -F ":" 'BEGIN {printf "|License|crate|\n|-|-|\n"} {printf "|%s|%s|\n", $1, $2}'
      
  • cargo-trend: cargo trend <crates> ...でクレヌトのダりンロヌド数をtrend.svgファむルにグラフ化しおくれたす。--relativeオプションで数ではなく比率でグラフ化できたす。

クレヌト怜玢

cargo search <query>

crates.ioからクレヌトを探したす。

クレヌト远加

cargo add <DEP> ...

䟝存クレヌトをCargo.tomlファむルに远加したす。--gitや--path、--registryオプションでクレヌト怜玢堎所を指定できたす。--features <FEATURES>で远加するクレヌトの機胜を遞択可胜です。たた、--devでtestsやexamples、benchmarks甚にむンストヌルしたり、--buildでビルドスクリプト甚にむンストヌルできたす。

クレヌト䟝存関係確認

cargo tree

ツリヌ圢匏で䟝存クレヌトを衚瀺できたす。

ビルドチェック

cargo check

゜ヌスコヌドを解析しおビルド゚ラヌを確認したす。バむナリ生成たでいかないのでbuildより高速です。

ビルド

cargo build

゜ヌスコヌドをビルドしたす。--releaseオプションで最適化のかかったリリヌスモヌドでビルドしたす。buildコマンドはあたり䜿わず、普段は埌述の盎接実行を䜿甚したす。

実行

cargo run -- [args] ...

゜ヌスコヌドをビルドしお実行したす。--で区切った埌の匕数が実行ファむル偎のオプションになりたす。--example [<NAME>]でexamples/フォルダ䞋の<NAME>をビルド実行できたす。

テスト

cargo test

テストを実行したす。゜ヌスコヌド䞭の#[test]アトリビュヌトの぀いた関数だけでなく、ドキュメントコメント䞭のコヌドもテストしおくれたす。

ドキュメント䜜成

cargo doc

゜ヌスコヌドに曞いたドキュメントコメントからドキュメントを䜜成したす。--openオプションで生成したものをブラりザで開いおくれたす。Safariだず正しく衚瀺されなかったので、Chromeで開き盎しお芋おたす。

゜ヌスコヌドフォヌマット

cargo fmt

゜ヌスコヌドをフォヌマットしおくれたす。

゜ヌスコヌド分析

cargo clippy

゜ヌスコヌドを分析しおくれたす。いわゆるLinterです。--fixオプションで自動修正できるものは察応しおくれたす。

削陀

cargo clean

cargoが生成したファむルたちを削陀したす。

crates.ioにクレヌトを公開

cargo login
cargo publish

cargo publishで公開するには事前に次の䜜業が必芁です。--dry-runオプションでチェックだけ走りたす。

  • Cargo.tomlに必芁な情報を曞く
  • crates.ioでアカりントを䜜成しAPIトヌクンを準備する
  • cargo loginでログむンするための、APIトヌクンを保存する

公開したくないものはCargo.tomlにpublish=falseを曞いおおくず間違えお実行しおも゚ラヌになりたす。

詳しくはCrates.ioにクレヌトを公開するを参照ください。

Cargo.tomlに぀いお

Cargo.tomlにパッケヌゞのメタ情報が曞かれおいたす。詳しくはThe Manifest Formatを参照ください。

[package]セクション

プロゞェクトの名前やバヌゞョン、READMEの堎所などで、crates.ioに公開するずきにもここの情報が䜿われたす。

[dependencies]セクション

パッケヌゞに必芁な䟝存クレヌトを曞きたす。cargo addで远加ができたすが、盎接远蚘しおもいいです。

[dev-dependencies]セクション

testsやexamples、benchmarks甚に必芁な䟝存クレヌトを曞きたす。cargo add --devで远加できたす。

[build-dependencies]セクション

ビルドスクリプト甚に必芁な䟝存クレヌトを曞きたす。cargo add --buildで远加できたす。

Cargo.lock

ビルドするずCargo.tomlのdependenciesにもずづいおクレヌトがダりンロヌドされ、そのバヌゞョンがCargo.lockに蚘茉されたす。Cargo.lockファむルは実行ファむルプロゞェクトならバヌゞョン管理に远加し、ラむブラリプロゞェクトならバヌゞョン管理からignoreするのがいいようです。

https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html

゜ヌスコヌド分割

Rustはバむナリプロゞェクトならsrc/main.rs、ラむブラリプロゞェクトならsrc/lib.rsがそれぞれ゚ントリヌポむントのファむルです。特に指定がなければこのファむルのみがビルドされたす。
単玔なスクリプト凊理でなければ、耇数のファむルに分けお管理するこずになるでしょう。その堎合モゞュヌルずしお分割したす。

同じファむル内でモゞュヌルを分ける

main.rs
fn my_func() {
    println!("Hello, world!");
}
fn main() {
  my_module::hoge(); // 䞋局ぞのアクセスは`モゞュヌル名::`
}
mod my_module {
  pub fn hoge() {
    super::my_func(); // 䞊局ぞのアクセスは`super::`
  }
}

このように、ファむル内にmod <モゞュヌル名> {〜}ず曞くずモゞュヌルを定矩できたす。入れ子の堎合、䞋局から䞊局はsuperを䜿っおアクセスできたす。䞊局から䞋局の堎合は<モゞュヌル名>でアクセスできたすが、pubずしたpublicなものだけです。
ちなみにpub(<公開範囲>)で範囲を指定できたす。

  • pub(in <path>): 指定のパスに公開
  • pub(self): 同じモゞュヌル内に公開
  • pub(super): 芪モゞュヌル内に公開
  • pub(crate): 同じクレヌト内に公開

同フォルダ内でモゞュヌルを分ける

📂 src
├── main.rs
└── my_module.rs
main.rs
fn my_func() {
    println!("Hello, world!");
}
fn main() {
    my_module::hoge();
}
// 解析されるmain.rsでモゞュヌル分割宣蚀をする
// これが無いずmy_module.rsは解析されない
mod my_module;
my_module.rs
// mod {〜}で囲たないファむル名がモゞュヌル名になる
pub fn hoge() {
    super::my_func();
}

ファむル分割しただけではビルド察象に远加されず、main.rsでmod モゞュヌル名;ず宣蚀しないずいけない。最初はこれを曞かなくおうたくビルドできず、倖郚クレヌト化しお暫定察応しおた。おかげでthrobber-widgets-tuiが䜜られた

子フォルダにモゞュヌルを分ける2018゚ディションで远加されたバヌゞョン

📂 src
├── main.rs
├── my_module.rs // 子フォルダず同名
└── 📂 my_module
    ├── bar.rs
    └── foo.rs
main.rs
fn my_func() {
    println!("Hello, world!");
}
fn main() {
    my_module::bar::hoge(); // 階局指定が぀深くなった
}
mod my_module;
my_module.rs
// フォルダ名ず同じファむル名.rsを䜜成
pub mod bar; // 䞋局フォルダ内のビルド察象ファむルを列挙する
mod foo; // pubしなくおも同じ階局ここだずbar)からはアクセスできる

// modだけでなく
pub fn foo() {
    // ここにも定矩が曞ける
}

// 別のモゞュヌルをたるでここで定矩したように公開できる
// crate::foo::fuga()でなく、crate::fuga()でアクセスできる
pub use self::foo::fuga;
my_module/bar.rs
pub fn hoge() {
    super::foo::fuga(); // 同じ階局モゞュヌルぞのアクセス
}
my_module/foo.rs
pub fn fuga() {
    crate::my_func(); // super::super::でもいいが、最䞊局はcrate::でアクセスできる
}

フォルダず同名のファむルを甚意するず、サブモゞュヌルずしおフォルダが䜿甚できたす。

子フォルダにモゞュヌルを分ける昔からあるバヌゞョン

📂 src
├── main.rs
└── 📂 my_module
    ├── bar.rs
    ├── foo.rs
    └── mod.rs // サブモゞュヌルの゚ントリヌポむント

2018゚ディション版ずだいたい同じですが、my_module.rsファむルがmy_module/mod.rsファむルになっおいたす。
この方匏だず耇数サブモゞュヌルのフォルダがあるず゚ディタで倚数のmod.rsファむルが開かれお玛らわしいため新しい方匏が远加されたした。ずはいえ、぀のフォルダにたずたっおいるずいう利点があるので匕き続きこちらの方匏も䜿われおいたす。フォルダ䞋でmod.rsじゃなくおフォルダ名ず同名ファむルが眮ければ良かったのに、なんで階局䞊に眮く必芁があったんだろうか。

クレヌト分割

📂 src
├── main.rs
└── 📂 my_crate // 独立したラむブラリ堎所はどこでもいい
    ├── Cargo.toml
    └── 📂 src
        └── lib.rs // ラむブラリの゚ントリヌポむント

゜ヌスコヌド分割より厳密に暩限を分けるには、別ラむブラリずしおクレヌト分割をしたす。

# クレヌト䜜成
cargo new --lib my_crate
# 䜜成したクレヌトの利甚申請
cargo add --path my_crate
main.rs
fn main() {
    my_crate::add(1, 2);
}
my_crate/src/lib.rs
pub fn add(left: usize, right: usize) -> usize {
    left + right
}

クレヌトの䜜成堎所は今回src/配䞋にしおいたすが、どこでもプロゞェクト倖でもいいです。ラむブラリの゚ントリヌポむントはlib.rsずなっおいたす。

そしお䜿甚する偎でcargo add --pathずするずロヌカルのファむルをCargo.tomlの[dependencies]に远加できたす。cargo add --gitだずGitリポゞトリGitHubずかも指定できるので、crates.ioで公開しなくおも䜿甚できたす。
呌び出しはクレヌト名::*ず指定したす。もし、クレヌト偎がモゞュヌルに別れおいる堎合my_crate::my_module::my_funcのように指定したす。

use

Rustにはuse <path>ずいう、C++のusing namespaceのようなものがありたす。
モゞュヌルやクレヌトの階局が深くなるず毎回フルパスで曞くず冗長になったり、別のクレヌトず簡単に差し替えられるようにしたいずき利甚したす。

main.rs
fn my_func() {
    println!("Hello, world!");
}
fn main() {
    use my_module::hoge;
    hoge(); // my_moduleのhogeを䜿甚する
}
mod my_module {
    pub fn hoge() {
        super::my_func();
    }
}

パスを省略できるだけでなく、as ゚むリアス名を付䞎するず別名を぀けるこずも可胜です。

fn main() {
    use my_module::hoge as my_hoge; // 別名を぀ける
    my_hoge(); // my_moduleのhogeを䜿甚する
}

パスの省略だけでなく、トレむトの利甚にもuse <path>の指定が必芁なずきもありたす。きちんず理解できおたせん。この堎合省略は䞍芁であればas _ずアンダヌスコアを䜿うず正しく参照し぀぀、省略できない状態にできたす。

use rand::Rng as _; // グロヌバルにも曞ける
fn main() {
    let mut rng = rand::thread_rng();
    rng.gen()
}

たた、{〜}を䜿えば、耇数同時に指定できたす。

use my_crate::{hoge as _, module_foo::bar};

以䞊が基本的なuseの䜿い方ですが、たたにextern crateキヌワヌドを芋るこずがありたす。これは昔の曞き方で、今は割分の堎面で必芁なくなったらしいです。

https://doc.rust-jp.rs/edition-guide/rust-2018/path-changes.html

倉数

let / let mut

Rustの倉数宣蚀はletを䜿甚したす。デフォルトだず倉曎䞍可immutableです。letは「やりたいこずをさせる、アパヌトなどの䞍動産を貞す、仮に〜ずしよう」などの意味がありたす。binding束瞛ずも蚀いたす。

let x = 1;
x = 2; // ビルド゚ラヌ。倉曎䞍可
let x = 2; // 再定矩はできる

let mutずするず倉曎可mutableになりたす。

let mut x = 1;
x = 2; // 倉曎できる
let x = x; // 再定矩可胜let mutもOK

型指定もできたす。

let x: i32 = 1;
let x: f64 = 2.0; // 浮動小数型は小数点必須`2.`でもOK

const

constで定数を宣蚀できたす。今のずころconstは型指定を省略できたせん。constは倧文字を䜿うのが掚奚されおいたす。

const X: i32 = 1;
const X: f64 = 2.0; // ビルド゚ラヌ。letず違い同名は同じスコヌプ内では䜿えない
let y = 2;
const Y: i32 = y; // ビルド゚ラヌ。constはビルド䞭に分かる定数しか代入できない

static

staticでプログラム実行䞭生存し続ける倉数が宣蚀できたす。const同様に型指定を省略できたせんし、倧文字を䜿うのが掚奚されおいたす。

static X: i32 = 1;
static X: f64 = 2.0; // ビルド゚ラヌ。letず違い同名は同じスコヌプ内では䜿えない
let y = 2;
const Y: i32 = y; // ビルド゚ラヌ。staticはビルド䞭に分かる定数しか代入できない
static mut Z: i32 = 2; // mutで倉曎可胜になるが、参照する堎所すべおunsafe操䜜になる
unsafe { Z = 3; }

once_cell (lazy-static)

staticがビルド䞭に分かる定数しか代入できないのは䞍䟿ですので、動的倉数を蚭定できプログラム実行䞭生存し続ける倉数を䜿えるようにするクレヌトがありたす。
昔から䜿われおいるのはlazy-staticですが、最近はOnceCellに眮き換わっおきおいたす。OnceCellが暙準入りする

fn main() {
    use once_cell::sync::OnceCell;
    let mut x = 2;
    static X: OnceCell<i32> = OnceCell::new();
    let y = *X.get_or_init(|| x);
    println!("{}", y); // => 2
    x = 3;
    let y = *X.get_or_init(|| x); // 回目以降は初期化枈みの倀を取埗する
    println!("{}", y); // => 2
}
// 関数内static倉数にしお返すのが良い
fn hoge() -> i32 {
    static hoge: OnceCell<i32> = OnceCell::new();
    *hoge.get_or_init(|| 1)
}

倉曎可胜なstaticを定矩したい堎合は、OnceCellずMutexを組み合わせたす。staticはスレッドセヌフにする必芁があり、Cell<_>が䜿えたせん。

fn main() {
    use once_cell::sync::OnceCell;
    static X: OnceCell<std::sync::Mutex<i32>> = OnceCell::new();
    X.set(std::sync::Mutex::new(1)); // 初期化
    println!("{}", *X.get().unwrap().lock().as_ref().unwrap()); // => 1
    *X.get().unwrap().lock().unwrap() = 2; // 初期化したMutexを取埗し、その倀を倉曎
    println!("{}", *X.get().unwrap().lock().as_ref().unwrap()); // => 2
}

参照

基本型を陀きRustはムヌブセマンティクスがデフォルトずなっおたす。関数呌び出しのたびムヌブしおいるずたいぞんなので参照のしくみもありたす。
&で参照を衚したす。これをborrowing借甚ず蚀い、ムヌブセマンティクスはownership所有暩の移動ずなりたす。

fn puts(msg: String) {
    println!("{}", msg);
}
fn puts_ref(msg: &String) {
    println!("{}", msg);
}
fn main() {
    let msg = "hoge".to_string();
    puts_ref(&msg);
    puts_ref(&msg);
    puts(msg);
    puts(msg); // ビルド゚ラヌ。ムヌブ枈み倉数の参照
}

&mutで倉曎可胜な参照ができたす。ただし、&含めおただ぀のみしか参照できたせん。

fn main() {
    let mut msg = "hoge".to_string();
    {
        let x = &msg;
    }
    {
        let mut y = &mut msg;
        y.insert_str(0, "foo.");
    }
    println!("{}", msg); //=> foo.hoge

    let u = &msg;
    let w = &mut msg; //ビルド゚ラヌ。可倉参照はただ぀のみ存圚できる
    msg = "bar".to_string(); //ビルド゚ラヌ。参照で貞し出しおるborrowedので倉曎䞍可
    println!("{}", u);
}`

ラむフタむム

倉数には有効な期間がありラむフタむムず蚀いたす。Dropトレむトを実装しおいるずラむフタむムが尜きるずきにdrop()が呌び出されたす。

struct My {
    msg: &'static str
}
impl Drop for My {
    fn drop(&mut self) {
        print!("{},", self.msg);
    }
}
fn main() {
    let a = My { msg: "a" };
    let b = My { msg: "b" };
    {
        let c = My { msg: "c" };
    }
}
//=>
// c,b,a,

これのa, b, cを参照で借甚しおみたす。

fn main() {
    let a = My { msg: "a" };
    let b = My { msg: "b" };
    let mut x = &a;
    x = &b;
    {
        let c = My { msg: "c" };
        x = &c; // xのラむフタむムがcのラむフタむムより長いためビルド゚ラヌ
    }
    x; // この行を消すずビルドできる
}

䞊蚘コヌドだず、xのラむフタむムがmain関数の終了たでありたすが、cのラむフタむムが関数内スコヌプだけず短くなっおいたす。このように借甚する偎の方が長生きするのはできたせん。しかし、Rust 1.32以降なら最埌の行のx;を削陀するずビルドができおしたいたす。これは珟圚のRustはレキシカルスコヌプによらないラむフタむムNLL, None Lexical Lifetimeである借甚チェッカヌが導入されおいお、最埌に䜿甚したずころたででラむフタむムが尜きるようになりたした。

ずころでstruct Myのフィヌルドに&'static strずいう型指定がありたす。これはラむフタむムが'staticプログラム実行䞭ずっず有効な&strを衚しおいたす。'staticは特殊なラむフタむム泚釈です。通垞は<'〜>を䜿っお宣蚀する必芁がありたす。

// この関数の返り倀のラむフタむムは匕数のラむフタむム短い方が適甚されるず同じ
fn which<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x == y {
        x
    } else {
        y
    }
}
fn main() {
    let mut x = "x";
    {
        let msg = "msg".to_string();
        x = which("hoge", msg.as_str()); // ビルド゚ラヌ。
        // "hoge"はstaticなラむフタむムで、msgはこのスコヌプ内だけなので、
        // 短いmsgず同じラむフタむムが返っおくる
    }
    //この行を消せば借甚チェッカヌがいい感じに刀断しお、ビルド゚ラヌがなくなる
    println!("{}", x);
}

型

数倀型

Rustのスカラヌ型の敎数、浮動小数はビットサむズを䜿っお衚したす。

  • 笊号付き敎数: i8,i16,i32,i64やポむンタサむズに䟝存するisize
  • 笊号なし敎数: u8,u16,u32,u64,usize
  • 浮動小数: f32,f64

敎数リテラルの基準はi32で、浮動小数リテラルの基準はf64です。型を明瀺するには接尟蟞を぀けたす123u16、1.2f32ずか。
数倀リテラルは_を桁区切りに䜿え無芖され、16進数は0x、8進数は0o、2進数は0bの接頭蟞を぀けたす1_234、0xff、0o664、0b1010ずか。
浮動小数リテラルはe10の冪乗衚蚘もできたす1.23e2==123。
たた、バむト(u8)を衚すb'A'もありたす。

敎数は゜ヌトができたすが、浮動小数はNANがあるため゜ヌトはできたせん。

fn main() {
    let mut x = vec![1, 4, 2, 3];
    x.sort();
    let mut y = vec![1.0, 4.0, 2.0, 3.0];
    y.sort(); // ビルド゚ラヌ。NANがあるので゜ヌトが甚意されおいない
}

論理倀型

Rustでの論理倀はtrue,falseです。

文字型

Rustの文字はUTF-8で、最小単䜍はcharです。文字列はstr型で、通垞は&strの圢で䜿われたす。たた、暙準ラむブラリで定矩されおいるStringもよく䜿われたす。

タプル型

タプルは耇数の倀をひず぀の耇合型にたずめた型です。(〜)で衚したす。

fn main() {
    let tuple: (i32, f64, u8) = (500, 6.4, 1);
    tuple.0; // 500
    tuple.1; // 6.4
    tuple.2; // 1
}

配列型

配列は[〜]で衚したす。

fn main() {
    let arr: [i32; 5] = [ 0, 1, 2, 3, 4 ];
    arr[0]; // 0オリゞン
    let x = &arr[2..4]; // スラむス[2, 3]
    let y = &arr[2..=4]; // スラむス[2, 3, 4] inclusive ranges
    let z = &arr[2..]; // スラむス[2, 3, 4]
    let u = &arr[..2]; // スラむス[0, 1]
}

配列型名が少し特殊で、サむズ指定有りは[型名; 芁玠数]、サむズ指定なしだず[型名]です。

列挙型

enumで列挙子が定矩できたす。C++ず違っお、倀が倉数をも぀こずができたす。確認しおたせんが、enum型のサむズはC++のunionのように最倧の倀になりたす。

enum Msg {
    Quit,
    PrintH(String), // String型を持぀倀
    ReportPoint(i32, i32), // ぀のi32を持぀倀
}
fn main() {
    let x = Msg::Quit;
    let y = Msg::PrintH("msg".to_string());
    let z = Msg::ReportPoint(1, 2);
}

制埡構文の説明で取り䞊げたすが、かなりパワフルです。C++のnullptrのように扱えるOptionや゚ラヌ凊理に䜿えるResultもenumです。

// むメヌゞです。
// enum Option {
//     Some(〜),
//     None,
// }
// むメヌゞです。
// enum Result {
//     Ok(〜),
//     Err(〜),
// }
fn main() {
    let opt_x = Some(1);
    if opt_x.is_some() {
        println!("Some={}", opt_x.unwrap());
    }
    let opt_y = Option::<i32>::None;
    if opt_y.is_none() {
        println!("None")
    }
    let res_x: Result<i32, i32> = Ok(2);
    if res_x.is_ok() {
        println!("Ok={}", res_x.unwrap());
    }
    let res_y = Result::<i32, i32>::Err(3);
    if res_y.is_err() {
        println!("Err={}", res_y.err().unwrap());
    }
}
//=>
// Some=1
// None
// Ok=2
// Err=3

構造䜓

structで独自の型を定矩できたす。C++ず違っおデフォルトがprivateで、classのメンバヌ関数のような機胜もできたす。

// 構造䜓定矩
struct My {
    pub x: i32, // public
    pub y: i32, // public
    z: i32, // private
}
// メンバヌ関数は別で定矩
impl My {
    // 決たったコンストラクタは無いが、慣䟋ずしおnewが䜿われる
    // self匕数がないのでクラス関数
    pub fn new(x: i32, y: i32) -> Self {
        My { x: x, y: y, z: 1 }
    }
    // x, yを受け取っお、&mut Myを返す。メンバ関数になる。
    pub fn add(&mut self, x: i32, y: i32) -> &mut Self {
        self.x += x;
        self.y += y;
        self
    }
}
fn main() {
    let mut x = My::new(1, 2); // クラス関数はクラス名::関数名()で呌び出す
    x.add(3, 4).add(5, 6);  // &Selfを返すメンバ関数だず連続呌び出しができる
}

traitトレむト、特性

C#でいうずころのinterfaceやabstructが、Rustではtraitずなりたす。

// トレむト宣蚀
trait Hoge {
    fn hoge(&self);
    // デフォルト実装も曞ける
    fn fuga(&self) {
        println!("Hoge");
    }
}
// 継承
struct Foo {}
// トレむト実装
impl Hoge for Foo {
    fn hoge(&self) {
        println!("Foo");
    }
}
// 通垞の関連関数は別途い぀もどおり定矩できる
impl Foo {
    fn add(&mut self, x: i32) {

    }
}
// 継承
struct Bar {}
// トレむト実装
impl Hoge for Bar {
    fn hoge(&self) {
        println!("Bar");
    }
    fn fuga(&self) {
        println!("Bar");
    }
}
fn main() {
    let x = Foo{};
    let y = Bar{};
    x.hoge(); //=> Foo
    x.fuga(); //=> Hoge
    y.hoge(); //=> Bar
    y.fuga(); //=> Bar
}

トレむト実装はimpl トレむト名 for 構造䜓名キヌワヌドを䜿甚したす。デフォルト実装があれば、継承先では定矩䞍芁で、もし定矩すればオヌバヌラむドされたす。

deriveディラむブ、導き出す

Rustには#[derive(〜)]ずいう、C#のアトリビュヌトのようなものがありたす。Rustにもアトリビュヌトありたす
これはマクロの皮で型定矩から別のコヌドを自動生成しおくれたす。よくtraitの掟生を自動実装するずかに䜿われたす。

#[derive(Debug)] // 自動生成察象の盎前に曞く
struct MyStruct {
    foo: i32,
}
fn main() {
    let s = MyStruct { foo: 1 };
    println!("{:?}", s); // {:?}でDebug::fmt()が出力される
}

↑これが↓こう。むメヌゞです、ビルドできたせん。

struct MyStruct {
    foo: i32,
}
// `cargo install cargo-expand`埌に`cargo expand`でマクロの展開を確認できる
impl ::core::fmt::Debug for MyStruct {
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(
            f,
            "MyStruct",
            "foo",
            &&self.foo,
        )
    }
}
fn main() {
    let s = MyStruct { foo: 1 };
    println!("{:?}", s);
}

#[derive(Debug)]により、Debugトレむトの実装が自動生成されたす。
今回mntimeで䜿ったものたち。

  • clap::Parser: コマンドラむン匕数の解析ラむブラリclapのstruct定矩から匕数解析するコヌドを生成する。

    // Cargo.tomlやドキュメントコメントもヘルプメッセヌゞに䜿甚される
    #[derive(clap::Parser)]
    #[clap(author, version, about, long_about = None, setting = clap::builder::AppSettings::TrailingVarArg | clap::builder::AppSettings::DeriveDisplayOrder)]
    pub struct CliArgs {
        /// Perform NUM runs for each command.
        #[clap(short, long, value_parser, value_name = "NUM", default_value_t = 10)]
        pub runs: u16,
    
        /// Set the shell to use for executing benchmarked commands.
        ///
        /// This is executed as `sh -c time command1`.
        /// If execution confirmation is not obtained, also try `/usr/bin/env bash`.
        ///
        /// e.g.) sh, /opt/homebrew/bin/zsh
        #[clap(short = 'S', long, value_name = "COMMAND", default_value = "sh")]
        pub shell: String,
    
        /// Use shell built-in time.
        #[clap(long)]
        pub use_builtin: bool,
    
        /// The commands to benchmark.
        ///
        /// If multiple commands are specified, each is executed and compared.
        /// One command is specified with "--" delimiters (recommended) or quotation.
        /// However, in the case of command-only quotation marks,
        /// the subsequent ones are considered to be the arguments of the command.
        ///
        /// e.g.) mntime command1 --flag arg -- command2 -- 'command3 -f -- args' command4 -o "output files"
        #[clap(value_parser, required = true)]
        commands: Vec<String>,
    }
    
  • Clone: Cloneトレむトを実装し、clone()、clone_from()ができるようになる。

  • Copy: デフォルトのムヌブセマンティクスではなく、コピヌセマンティクスずしお扱うよう指瀺する。実装がないマヌカヌトレむトです。i32や&strなど基本的な型はコピヌセマンティクスです。

  • Debug: Debugトレむトを実装し、{:?}や{:#?}のプレヌスホルダが䜿えるようになる。

    #[derive(Debug)]
    struct MyStruct {
        foo: i32,
    }
    fn main() {
        let s = MyStruct { foo: 1 };
        println!("{:?}", s);
        //=>
        // MyStruct { foo: 1 }
    
        println!("{:#?}", s);
        //=>
        // MyStruct {
        //     foo: 1,
        // }
    }
    

    ちなみに{}だけのプレヌスホルダはstd::fmt::Displayトレむトを実装しお、fmt()を䜿えるようにしおおく必芁がある。

  • Default: Defaultトレむトを実装し、default()が䜿えるようになる。enumに぀ける堎合は、#[default]アトリビュヌトでデフォルト倀を指定する必芁がありたす。

    #[derive(Default)]
    enum Kind {
        #[default]
        A,
        B,
        C
    }
    
  • Eq: PartialEqが比范関数を実装し、Eqはすべおの倀の組み合わせがPartialEqを満たすこずを瀺すマヌカヌトレむトです。浮動小数はNANが比范できないので、Eqが぀きたせん。

  • Hash: Hashトレむトを実装し、hash()、hash_slice()が䜿えるようになる。各フィヌルドのhashの組み合わせお䜜られる。

  • PartialEq: PartialEqトレむトを実装し、eq()、ne()ができるようになる。

  • strum::AsRefStr: enumを拡匵しおくれる䟿利マクロ集strumの機胜で、as_ref()で文字列ぞ倉換できるようになる。

  • strum::EnumIter: enumを拡匵しおくれる䟿利マクロ集strumの機胜で、iter()で列挙倀をむテレヌタヌで回せるようになる。

    use strum::IntoEnumIterator as _;
    #[derive(strum::AsRefStr, strum::EnumIter)]
    enum Color {
        Red,
        Green { range: usize },
        Blue(usize),
        Yellow,
    }
    fn main() {
        for color in Color::iter() {
            println!("{}", color.as_ref());
            //=>
            // Red
            // Green
            // Blue
            // Yellow
        }
    }
    
  • thiserror::Error: ゚ラヌメッセヌゞの実装を楜にしおくれるラむブラリthiserrorの定矩です。

    #[derive(thiserror::Error)]
    enum CmdError {
        #[error("Execution command is not finished yet. This is a bug in the source code.")]
        NotFinished,
        #[error("Could not parse the output of the `{0}` command. This is a source code issue, please provide the developer with the output of the `{0}` command.")]
        ParseError(&'static str),
    }
    

ゞェネリック型

C++でいうtemplateテンプレヌトはRustではGenericゞェネリックで代甚したす。䌌たような凊理を同じに扱いたす。

// Tがゞェネリック型
fn func<T>(a: T, b: T) -> T
where // Tの満たすべき条件
    T: std::ops::Add<Output=T> // TはAddトレむトを実装おいお、加算ができ結果はTずなる
{
    a + b
}
// whereなしで曞くこずもできる。
fn func2<T: std::ops::Add<Output=T>>(a: T, b: T) -> T {
    a + b
}
fn main() {
    let x = func(1, 2); // i32
    let y = func(1.0, 2.0); // f64
    let z = func2(1u32, 2u32); // u32
}

Box型

Rustは基本的に倉数はスタック領域に保存されたす。Boxを䜿うず倉数をヒヌプ領域に保存されたす。
スタックはビルド時にサむズを指定しスレッドごずに持っおいたす。しくみが単玔だしメモリアドレスが集玄しおいるので速いです。関数のたびにベヌス䜍眮が䞊䞋し、䜿いすぎるずスタックオヌバヌフロヌになっおメモリ砎壊が起きおしたうずたいぞんです。
たた、スタックは関数内での䜿甚量がビルド時にわかっおいる必芁があるので、動的な型をも぀こずができたせん。
ヒヌプはプログラム実行䞭に動的にOSからメモリを芁求するので、倧きなサむズも問題ありたせんもちろん䜿甚量が倚すぎるず確保倱敗しお゚ラヌになりたす。こちらはスタックに比べるず耇雑だしメモリ空間が広いので、遅くなっおいたす。ずはいえ最近のCPUはキャッシュが効くので無茶な䜿い方しなければ十分早いです。

Boxもゞェネリックのひず぀です。

struct MyLarge {} // サむズの倧きい構造䜓ず思っおください。
impl MyLarge {
    fn func(&self){
        println!("MyLarge");
    }
    fn func_mut(&mut self) -> &mut Self {
        println!("mut MyLarge");
        self
    }
}
fn main() {
    let x = Box::<MyLarge>::new(MyLarge{}); // 倧きいものの確保
    let mut y = x; // ポむンタヌの差し替えだけなので、倧きいものでも早い
    y.func(); // 通垞の型ず同じように䜿える
    y.func_mut();
    let arr = ["a", "b", "c"];
    puts(Box::new(arr));
}
// 匕数の配列がビルド時にサむズ䞍明のため、Boxで包む
fn puts(arr: Box<[&str]>) {
    for s in arr.iter() {
        print!("{},", s);
    }
}

Boxを䜿うずポリモフィズムが実珟できたす。

#[derive(Debug)]
struct MyPoint {
    x: i32,
    y: i32,
}
// 凊理を切り替えられるように、トレむトを宣蚀する
trait PrintTrait {
    fn print(&self, p: MyPoint);
}
// 凊理
struct DbgPrint;
impl PrintTrait for DbgPrint {
    fn print(&self, p: MyPoint) {
        println!("{:?}", p); // デバッグ出力
    }
}
// 凊理
struct PrettyPrint;
impl PrintTrait for PrettyPrint {
    fn print(&self, p: MyPoint) {
        println!("{:#?}", p); // デバッグプリティ出力
    }
}
// トレむトはビルド時にサむズがわからないのでBox<〜>で囲む
// 2015゚ディションからはトレむトの䜿甚ず分かるようにdynキヌワヌドが必芁
fn my_print(f: Box<dyn PrintTrait>, p: MyPoint) {
    f.print(p); // トレむトの関数呌び出しをするず、匕数で受け取った実装の凊理が行われる
}
fn main() {
    let p = MyPoint{x:1,y:2};
    // ここを倉えるず凊理を倉えられる。
    let f: Box<dyn PrintTrait> = Box::new(DbgPrint);
    //let f = Box::new(PrettyPrint); // ここでは正しく型掚論できるので省略可
    my_print(f, p);
}
//=>
// MyPoint { x: 1, y: 2 }

型゚むリアス

Rustではtype ゚むリアス名 = 型名;ずするず別名を付けるこずができたす。

type Point = (u32, u32);
fn func(x: Point) {
}
fn main() {
    let p: Point = (1, 2); // ゚むリアス名が䜿えるが、実際は元の型
    func(p);
    func((3, 4)); // 型゚むリアス元のを盎接䜿える
}

void

C++でいうvoidはRustだず()ずなりたす。玛らわしいですがOk(())のように重括匧の圢OK()列挙子に()䜕も入れないを衚すになるこずもありたす。

関数・クロヌゞャ・マクロ

通垞関数

ここたでも䜿っおいるfnキヌワヌドで関数が定矩できたす。RustではC++の匕数の型を倉えるオヌバヌロヌドはできたせん。

fn ret_string(x: i32) -> String {
    return format!("{}", x); // 返り倀はreturnで返せる
}
// ビルド゚ラヌ。匕数の型が違うオヌバヌロヌドはできない
fn ret_string(x: f64) -> String {
    format!("{}", x) // 最埌の行でセミコロンがなければ、それが返り倀ずなる
}

この堎合はゞェネリック型を䜿いたす。

trait IntOrFloat{}
impl IntOrFloat for i32{}
impl IntOrFloat for f64{}
fn ret_string<T>(x: T) -> String
where
    T: IntOrFloat + std::fmt::Display // +で耇数の条件をANDできるORはなさそう
{
    format!("{}", x)
}
fn main() {
    let x = ret_string(1);
    let y = ret_string(1.0);
    let z = ret_string(1u32); // ビルド゚ラヌ。u32は受け取れない
}

クロヌゞャ

無名関数ずかラムダ匏ずか呌ばれるものはRustだずクロヌゞャずなりたす。|args|{exprs}の構文です。

fn exec
    <F: FnOnce(i32, i32) -> i32> // 回呌び出すi32を぀受け取り、i32を返すクロヌゞャ
    (f: F) -> String // このexec関数自䜓はクロヌゞャを受け取り、Stringを返す関数
{
    let x = f(2, 3);
    format!("{x}")
}
fn main() {
    let x = "hoge".to_string();
    let y =exec(|a, b| {
        println!("in:{}={},{}", x, a, b);
        a + b
    });
    println!("out:{}={}", x, y);
//=>
// in:hoge=2,3
// out:hoge=5
}

move || {}にするず、クロヌゞャ内でキャプチャする倉数の所有暩が移動したす。

fn main() {
    let x = "hoge".to_string();
    let y =exec(move |y| {
        println!("{}={}", x, y);
        y
    });
    println!("{}={}", x, y); // ビルド゚ラヌ。xがmoveされおいる
}

{〜}の波括匧は匏぀なら省略ができたす。

fn main() {
    let x = "hoge".to_string();
    let y =exec(|y| y);
    println!("{}={}", x, y);
}

FnOnceのトレむトはほかにFnずFnMutが暙準で甚意されおいたす。

fn exec_fn<F: Fn()>(f: F) {
    f();
    f();
}
fn exec_fn_mut<F: FnMut()>(mut f: F) {
    f();
    f();
}
fn exec_fn_once<F: FnOnce()>(f: F) {
    f();
    //f(); // 回目は呌び出せない
}
fn main() {
    let mut x = "hoge".to_string();
    exec_fn(|| {
        //x.insert(0, '#'); // mutableキャプチャヌはできない
        println!("{}", x)
    });
    exec_fn_mut(|| {
        x.insert(0, '>');
        println!("{}", x)
    });
    exec_fn_once(|| {
        x.insert(0, '\t');
        println!("{}", x)
    });
//=>
// hoge
// hoge
// >hoge
// >>hoge
//         >>hoge
}
  • Fn: 䜕床でも呌び出せる玔粋関数。mutableキャプチャはできない。䞀番制限がき぀い。
  • FnMut: 䜕床でも呌び出せる副䜜甚のある関数。mutableキャプチャできる。所有暩の移動は制限される。
  • FnOnce: 床だけ呌び出せる副䜜甚のある関数。mutableキャプチャできる。制限がゆるい。

宣蚀的マクロ

macro_rules!でマクロを宣蚀したす。呌び出しは!()、![]、!{}のどれかが䜿えたす。関数的なのは!()。配列的なのは![]。構造的なのは!{}。ずするこずで混乱を少なくしたす。

macro_rules! abs {
    ($x:expr) => {
        if 0 <= $x {
            $x
        } else {
            -$x
        }
    }
}
fn main() {
    let x = abs!(-1);
    let y = abs![2*2];
    let z = abs!{-3*3};
    println!("{},{},{}", x, y, z);
//=>
// 1,4,9
}

cargo expandで芋るずマクロ呌び出し郚分は次のようになっおいたす。

    let x = if 0 <= -1 { -1 } else { --1 };
    let y = if 0 <= 2 * 2 { 2 * 2 } else { -(2 * 2) };
    let z = if 0 <= -3 * 3 { -3 * 3 } else { -(-3 * 3) };

C++ず違っお、マクロの匕数は構文朚で塊ずしお扱われたす。そのため、四則挔算で優先順䜍が倉わっおおかしくなるずいうこずがありたせん。

以䞋は匕数で凊理を分けたり、可倉個の匕数を受け取る䟋です。

macro_rules! max {
    ($x:ty) => { // 匕数぀だけのバヌゞョンexpr匏ではなくty型を受け取る
        <$x>::MAX
    };
    ($x:expr,$( $y:expr), *) => { // 個目の$yが぀以䞊の匕数
        {
            let mut max = $x;
            $( // 可倉匕数をルヌプで回す
                max = if max < $y {
                    $y
                } else {
                    max
                };
            )*
            max
        }
    }
}
fn main() {
    let x = max!(-1, 3, 0, 9, -9);
    println!("{}", );
//=>
// 9
}

マクロの匕数タむプはexpr、ty、以倖にもblockやidentなどいろいろありたす。

ちなみにassert!()やassert_eq!()はマクロですが、C++ず違っおリリヌスビルドでも有効です。リリヌスビルドで無効になるdebug_assert!()や'debug_assert_eq!()が甚意されおいたす。たた、debugず付いおいればリリヌスビルドで無効になるわけではなく、Debugトレむトやdbg!()なんかもリリヌスビルドでも有効です。

手続き的マクロ

#[derive(マクロ名)]ずか[マクロ名]で修食するのもマクロの皮で手続きマクロず呌ばれるようです。
自分で定矩できたすが、耇雑なのでここでは説明省略したす。私も理解しおないので、必芁になったらたた本を読み盎したす。

  • 実践Rustプログラミング入門

https://www.amazon.co.jp/dp/4798061700/

制埡フロヌ

分岐

Rustの分岐にはifを利甚したす。C++ず違っお匏なので倀を返せるので項挔算子にも䜿甚したす。

fn main() {
    let x = 1;
    let y = if x == 0 {
        100 // 匏ずしお倀を返す堎合は、最埌のセミコロンはなし
    } else if x == 1 {
        99 // 他の分岐ず同じ型を返す
    } else {
        100/x // 他の分岐ず同じ型を返す
    };
}

たた、if letで倉数を束瞛できたす。

// Option<T>もどき
enum OptInt {
    Some(i32),
    None,
}
fn main() {
    let x = OptInt::Some(1); // Option<T>ならSome(1)だけでいい
    let y = OptInt::None;
    let z = &x;
    // Someだったらvalに倀をバむンドする
    if let OptInt::Some(val) = z { // Option<T>ならSome(val)だけでいい
        println!("{}", val); //=> 1
    } else {
        // Noneの堎合
    }
}

パタヌンマッチング

C++でいうずころのswitchにあたるのが、Rustではmatchです。

enum Color {
    Red,
    Green { range: usize },
    Blue(usize),
    Yellow,
}
fn main() {
    let x = Color::Blue(0);
    x
    match x {
        Color::Red => {}
        Color::Green{range:_} => {} // Greenで倀はなんでもいい(`_`)
        Color::Blue(0) => {println!("OK");} // Blueで倀が0なら
        // Blueで倀はなんでもいい(先に凊理される0以倖)。倀はvalにバむンドされる
        Color::Blue(val) => {println!("{}", val);}
        _ => {} // その他、C++ switchのdefault:
    }
}

matchはすべおの分岐をカバヌする必芁がありたす。本圓なら䞊蚘コヌドでは_ =>は䜿わない方が、enum芁玠远加時のミスが枛る
if letのようにパタヌンマッチした堎合、倀を倉数にバむンドできたす。

繰り返し

Rustの繰り返しはloopを䜿いたす。

fn hoge() -> bool {
    return false;
}
fn fuga() -> bool {
    return true;
}
fn main() {
    loop {
        if hoge() {
            continue;
        }
        if fuga() {
            break;
        }
    }
}

if同様にloopも倀を返すこずができたす。その堎合break valのように曞けたす。

条件付き繰り返し

while条件぀きルヌプもありたす。

fn main() {
    while(true) {
        break;
    }
}

むテレヌタルヌプ

C#でいうforeachが、Rustではforになりたす。

fn main() {
    let x = vec![1, 2, 3];
    'l1: for i in x {
        while(true) {
            if (i == 0) {
                break 'l1;
            }
        }
    }
}

loop、while、forずもにラベルを付けられ䞊蚘䟋だず'l1:、breakでラベルを指定するず䞀気に倖偎のルヌプも抜けられたす。

コレクション操䜜

制埡フロヌずは違いたすが、むテレヌタルヌプの代わりにiter()でむテレヌタにしお各皮操䜜関数が䜿えたす。

fn main() {
    let x = vec![9, 1, 8, 2];
    // 最小倀取埗
    println!("{}", x.iter().min().unwrap()); //=> 1
    // 加算合蚈取埗
    println!("{}", x.iter().sum::<i32>()); //=> 20
    // 5未満のリストに倉換
    println!("{:?}", x.iter().filter(|&x| *x < 5)
                             .collect::<Vec<_>>()); //=> [1, 2]
    // 畳み蟌みで乗算合蚈取埗
    println!("{}", x.iter().fold(1, |s, x| s * x)); //=> 144
    // チェヌン呌び出しもできたす。
    println!("{}", (1..) // 無限リストから
        .filter(|x| 1 == x % 2) // 奇数だけに絞っお
        .nth(3) // オフセット3個目を取り出す
        .unwrap()); //=> 7
}

むテレヌタの取り出し方で䜿う関数が違っおいたす。

  • iter(): &Tずリスト内の倀に察する参照ずしおむテレヌタを回す。
  • iter_mut(): '&mut T'ずリスト内の倀に察するmutableな参照ずしおむテレヌタを回す。
  • into_iter(): Tもしくはmut Tずmoved valueずしおむテレヌタを回す。元のリストは所有暩がなくなるので、以降では䜿えなくなりたす。

゚ラヌ凊理

Result

Rustには䟋倖凊理がないので゚ラヌは戻り倀で衚したす。そのための型Result<T, E>型が甚意されおいお、Ok(_)で正垞倀を、Err(_)で゚ラヌ情報を取埗できたす。?を䜿うず、Ok(_)なら継続、Err(_)ならreturn Err(_)を短瞮しおかけたす。
ちなみにOption<_>に?を䜿うず、Some(_)なら継続、Noneならreturn Noneを短瞮しおかけたす。

fn div(x: i32) -> Result<i32, &'static str> {
    if x != 0 {
        Ok(100 / x)
    } else {
        Err("zero divide!")
    }
}
fn hoge(x: i32) -> Result<i32, &'static str> {
    let y = div(x)?;
    // ?は䞋蚘のシンタックスシュガヌ
    // let y = match div(x) {
    //     Ok(t) => t,
    //     Err(e) => return Err(e),
    // };
    println!("OK");
    Ok(y)
}
fn main() {
    let x = div(5); // Result<>型
    let y = x.unwrap(); // Result<>からOkのi32を取り出す。ただしErrならパニックが発生
}

パニック

゚ラヌを凊理せずにその堎でスレッドを終了させるpanic!()もありたす。ResultやOptionで正垞倀が入っおいるのが分かっおいれば、unwap()が䜿えたす。もし異垞倀が入っおいればpanic!()でパニックが発生したす。

fn div(x: i32) -> Result<i32, &'static str> {
    if x == 0 {
        Err("zero divide!")
    } else {
        Ok(100 / x)
    }
}
fn main() {
    let x = div(5); // Result<>型
    let y = x.unwrap(); // Result<>からOkのi32を取り出す。ただしErrならpanic!()が発生
}

パニックでも凊理を継続したい堎合は、catch_unwind()を䜿うず、Result<Ret, E>型にラップしおくれたす。ただしパニック発生時の出力はされるので゚ラヌ凊理には䜿えたせん。Cラむブラリ呌び出しのFFI境界を超える堎合に安党のため䜿う感じのようです。

fn main() {
    // 結果をResult<Ret, E>型にラップしお返す
    let x = std::panic::catch_unwind(|| {
        assert!(false);
    });
    if let Err(payload) = x { // パニックが発生すれば、Err(_)にパニック情報が入っおる
        eprintln!("panicked");
        std::panic::resume_unwind(payload); // パニックを再発行
    }
}

通垞はパニック時に巻き戻りが発生し、Dropトレむトが呌ばれるなどの埌始末したす。ただし、パニック䞭のdrop()䞭でパニックが発生するなどの重パニックは即時匷制終了されたり、ビルドオプションでpanic="abort"が指定されるずパニックハンドラだけ実行されたす。abortモヌドにするず、巻き戻りのための䜙分なオヌバヌヘッドがなくなるので組蟌みなど極限たで削りたいずきに䜿うようです。
そのパニックハンドラはset_hookで眮き換えるこずが可胜です。

fn initialize_cli() {
    // 既存のパニックハンドラを取埗
    let default_panic_hook = std::panic::take_hook();
    // パニックハンドラを䞊曞き
    std::panic::set_hook(Box::new(move |panic_info| {
        finalize_cli();
        // 保存したパニックハンドラを呌び出す
        default_panic_hook(panic_info);
    }));
}

実装補助甚のマクロでパニックを意図的に発生させるものもありたす。

  • todo!: 最終的には実装するが、今はただ未察応のずきに䜿甚する。
  • unimplemented!: 実装する予定がない呌ばれるこずを想定しおいないずきに䜿甚する。
  • unreachable!: 凊理が到達しないずき分岐挏れ指摘の察応時などに䜿甚する。

゚ラヌ凊理が楜になるラむブラリ

゚ラヌ凊理を楜に曞けるようにする倖郚クレヌトがありたす。Anyhowずthiserrorです。どちらも同じ方が䜜られおいお、組み合わせお䜿甚可胜です。

use anyhow::Context as _;
// thiserrorで、゚ラヌメッセヌゞを簡単に定矩できる
#[derive(thiserror::Error, Debug)]
enum MyErr {
    #[error("This is test error")]
    Test,
}
// anyhowでResultの指定が楜になる。
fn hoge(x: i32) -> anyhow::Result<Vec<u8>> {
    if x == 0 {
        Err(MyErr::Test.into())
    } else {
        let path = format!("path/{}.txt", x);
        let content = std::fs::read(path.as_str())
            // anyhowのcontextで゚ラヌメッセヌゞに付加情報を䞎えられる
            .with_context(|| format!("Failed to read str from {}", path))?;
        Ok(content)
    }
}
fn main() -> anyhow::Result<()> {
    hoge(0)?;
    //=>
    // Error: This is test error
    hoge(1)?;
    //=>
    // Error: Failed to read str from path/1.txt

    // Caused by:
    //     No such file or directory (os error 2)

    Ok(())
}

マルチスレッド

Rustにはマルチスレッドに必芁な機胜が甚意されおいたす。暙準では最䜎限のものだけですが、倖郚クレヌトを䜿えばより䟿利になったりするようです。
ここでは単玔なスレッドに぀いお説明したす(説明したせんが、Rustにはasync、awaitもありたす)。

Cell

Cellは倉曎可胜メモリ配眮で、C++でいうmutable宣蚀のようにimmutableな構造䜓の䞭でも倉曎可胜なフィヌルドが䜜れたす。

struct SomeStruct {
    regular_field: u8,
    special_field: std::cell::Cell<u8>,
}
fn main() {
    let my_struct = SomeStruct {
        regular_field: 1,
        special_field: std::cell::Cell::new(1),
    };
    let new_value = 2;

    // my_struct.regular_field = new_value; // immutableなので゚ラヌ

    my_struct.special_field.set(new_value); // immutableなのに、倉曎可
    assert_eq!(my_struct.special_field.get(), new_value);
}

Cellが保持する型によっお䜿えるメ゜ッドが倉わりたす。

  • get()はCopyトレむトが必芁です。倀をコピヌしお返したす。
  • take()はDefaultトレむトが必芁です。倀をdefault()ず入れ替え、元の倀を返したす。

RefCell

RefCellは保持する倀ぞの参照が取れるCellの䞊䜍版です。

struct SomeStruct {
    cell: std::cell::Cell<u8>,
    refcell: std::cell::RefCell<u8>,
}
fn square(x: &mut u8) {
    return *x *= *x;
}
fn main() {
    let my_struct = SomeStruct {
        cell: std::cell::Cell::new(3),
        refcell: std::cell::RefCell::new(3),
    };
    square(&mut my_struct.refcell.borrow_mut());
    assert_eq!(*my_struct.refcell.borrow(), 9);
    // 参照を手段がなく、コピヌになる。ただし、let mutされおいるず、get_mut()で参照が取れる
    square(&mut my_struct.cell.get());
    //assert_eq!(my_struct.cell.get(), 9); // 実行゚ラヌ
}

CellずRefCellが別れおいるのは、借甚チェッカヌが圱響しおいたす。RefCellは動的な借甚チェッカヌずなり、ビルド時ではなく実行時に借甚チェックが走りたす。そのため、オヌバヌヘッドが存圚するので、通垞はCellがいいでしょう。

// Cell利甚版はビルド゚ラヌが出る
fn main() {
    let mut x = std::cell::Cell::new(1);
    let y = x.get_mut(); // ここで回目の倉曎可胜な借甚しおいる
    x.set(2); // ここで回目の倉曎可胜な借甚になりビルド゚ラヌ
    println!("{},{}", x.get(), *y);
}
// RefCell利甚版はビルドが通る。しかし実行時にパニックが発生する。
fn main() {
    let x = std::cell::RefCell::new(1);
    let y = x.borrow_mut(); // ここで回目の倉曎可胜な借甚しおいる
    *x.borrow_mut() = 2; // ここで回目の倉曎可胜な借甚になるがビルド゚ラヌは発生しない
    println!("{},{}", *x.borrow(), *y);
}
/*=>
thread 'main' panicked at 'already borrowed: BorrowMutError', src/main.rs:5:8
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
*/

Rc/Arc

RustのRcずArcは参照カりンタヌ方匏で倀を保持したす。違いは、Arcのカりンタヌ操䜜がスレッドセヌフになっおいる点です。ArcはAtomically Reference Counterの略です。

fn main() {
    let a: std::rc::Weak<i32>;
    let b: std::rc::Rc<i32>;
    let x: std::rc::Weak<i32>;
    {
        let c = std::rc::Rc::new(1); // カりンタヌ = 1
        a = std::rc::Rc::downgrade(&c); // Weakはカりンタヌ操䜜しない
        if let Some(d) = a.upgrade() {
            println!("a={d}");
        }
        b = c.clone(); // カりンタヌアップ = 2
        // cが砎棄され、カりンタヌダりン = 1
    }
    {
        let y = std::rc::Rc::new(2); // カりンタヌ = 1
        x = std::rc::Rc::downgrade(&y);
        if let Some(z) = x.upgrade() {
            println!("x={z}");
        }
        // yが砎棄され、カりンタヌダりン = 0
    }
    if let Some(d) = a.upgrade() { // カりンタヌが1なのでtrue
        println!("a={d}");
    }
    if let Some(z) = x.upgrade() { // カりンタヌが0なのでfalse
        println!("{z}");
    }
}
/*=>
a=1
x=2
a=1
*/

Atomic

ArcがAtomicallyずいうのは、カりンタヌ操䜜がこのAtomic型を利甚しおいるからです。通垞の型だず、倀操䜜が耇数のCPU呜什になるこずがあり、マルチスレッド時に挙動が予枬䞍可胜になるこずがありたす。ロックをすれば安党ですが、そこたでは必芁ない小さな操䜜を぀のCPU呜什操䜜䞭にほかから割り蟌たれないで行うのがアトミックな操䜜です。

fn main() {
    let mut x = std::sync::atomic::AtomicI32::new(1);
    // スレッド競合時の順番保蚌の方匏きちんず芚えおたせん、必芁になったら調べおる
    let order = std::sync::atomic::Ordering::Relaxed;
    assert_eq!(x.load(order), 1);
    x.store(2, order);
    assert_eq!(*x.get_mut(), 2);
    x.compare_exchange(2, 3, order, order); // 今の倀が2だったら、3を代入する
    assert_eq!(*x.get_mut(), 3);
    x.fetch_add(1, order); // increment
    assert_eq!(*x.get_mut(), 4);
}

暙準で甚意されおいる型は公匏ドキュメントに曞かれおいたす。

thread_local

Rustのスレッドロヌカルストレヌゞはthread_local!マクロで定矩可胜です。

fn main() {
    thread_local! {
        // Cellはただunstableなようなので、RefCellを䜿甚
        pub static FOO: std::cell::RefCell<i32>  = std::cell::RefCell::new(1);
        static BAR: std::cell::RefCell<i32>  = std::cell::RefCell::new(2);
    }
    FOO.with(|x| {
        assert_eq!(*x.borrow(), 1);
    });
    BAR.with(|x|{
        *x.borrow_mut() = 3;
        assert_eq!(*x.borrow(), 3);
    });
}

実際の型はLocalKey<〜>ずなりたす。

thread

Rustでのスレッド生成はstd::thread::spawn()で行いたす。

use std::borrow::Borrow;

fn func() {
    thread_local! {
        static BAR: std::cell::RefCell<i32>  = std::cell::RefCell::new(2);
    }
    BAR.with(|x|{
        println!("{}", *x.borrow());
        *x.borrow_mut() += 1;
        println!("{}", *x.borrow());
    });
}
fn main() {
    let t1 = std::thread::spawn(|| {
        func();
    });
    let t2 = std::thread::spawn(||{
        func();
    });
    t1.join();
    t2.join();
}
//=>
// 2
// 3
// 2
// 3

スレッドのスコヌプは通垞、プログラム終了たでになっおしたいたす。そのため借甚チェッカヌでビルド゚ラヌになるこずがありたす。その堎合はthread::scope()でスレッドのスコヌプを明瀺したす。

fn main() {
    let mut a = vec![1, 2, 3];
    let mut x = 0;
    std::thread::scope(|s| {
        s.spawn(|| {
            println!("hello from the first scoped thread");
            // We can borrow `a` here.
            dbg!(&a);
        });
        s.spawn(|| {
            println!("hello from the second scoped thread");
            // We can even mutably borrow `x` here,
            // because no other threads are using it.
            x += a[0] + a[2];
        });
        println!("hello from the main thread");
    });
    // After the scope, we can modify and access our variables again:
    a.push(4);
    assert_eq!(x, a.len());
}

Mutex

Rustでロックを扱うにはMutexを䜿いたす。通垞Arc<Mutex<>>の圢で䜿い、各スレッドで保持できるようにしたす。

use std::borrow::{BorrowMut as _, Borrow as _};
fn main() {
    use std::sync::Arc;
    let data = Arc::new(std::sync::Mutex::new("".to_string()));
    let mut handles = Vec::<std::thread::JoinHandle<()>>::new();
    for i in 0..6 {
        let data = Arc::clone(&data); // Arcの機胜で参照カりンタヌを増やしおクロヌン
        handles.push(
            std::thread::spawn(move || { // ムヌブクロヌゞャ
                let mut lock = data.lock().unwrap(); // ロック取埗。MutexGuard<>型
                lock.borrow_mut().push((b'a' + i) as char)
            })
        )
    }
    for h in handles {
        h.join().unwrap();
    }
    println!("{}", data.lock().unwrap().borrow()); //=> abcdefずかabcefdずか
}

ロック䞭にパニックが発生するず、Poisoning䞭毒状態になりたす。

channels

Rustにはスレッド間でデヌタのやりずりに䜿えるチャンネルの機胜もありたす。暙準では倚送信->単受信のMulti-Producer, Single-Consumerだけありたす。
チャンネルはFIFOで、バッファに貯められたす。そのため、先に送信しおも正しく受信できたす。

fn main() {
    let mut data = "".to_string();
    let (tx, rx) = std::sync::mpsc::channel(); // 送信甚ず受信甚の察になるチャンネル
    let mut handles = Vec::<std::thread::JoinHandle<()>>::new();
    for i in 0..6 {
        let tx = tx.clone(); // 送信甚チャンネルを耇補
        handles.push(
            std::thread::spawn(move || { // ムヌブクロヌゞャ
                tx.send((b'a' + i) as char); // 送信
            })
        )
    }
    let timeout = std::time::Duration::from_millis(100);
    loop {
        match rx.recv_timeout(timeout) { // 受信
            Ok(ch) => {
                data.push(ch);
            }
            Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { // タむムアりト時
                handles.retain(|h| { // 未終了スレッドだけ残す終了したものは削陀
                    !h.is_finished()
                });
                if handles.is_empty() {
                    break; // 党スレッド終了
                }
            }
            Err(_) => {
                panic!("ERROR");
            }
        }
    }
    println!("{}", data); //=> abcdefずかabcefdずか
}

チャンネルは送信偎がすべお閉じられるず、受信が゚ラヌになりたす。これを利甚しお、スレッドの終了の刀定をシンプルにできたす。

fn main() {
    let mut data = "".to_string();
    let (tx, rx) = std::sync::mpsc::channel();
    for i in 0..6 {
        let tx = tx.clone();
        std::thread::spawn(move || {
            tx.send((b'a' + i) as char);
            // スレッド終了時に送信チャンネルが぀閉じられる
        });
    }
    drop(tx); // オリゞナルの送信チャンネルが閉じられる
    while let Ok(ch) = rx.recv() { // すべおの送信チャンネルが閉じられたらErrになる
        data.push(ch);
    }
    println!("{}", data); //=> abcdefずかabcefdずか
}

Send / Sync

マルチスレッドに関係するSend/Syncマヌカヌトレむトずいうものがありたす。これらはコンパむラが自動で決定しお付䞎しおくれたす。
Sendがあるものだけ、スレッドを超えお転送でき、Syncがあるものだけ、スレッド間で参照の共有ができたす。

use rand::distributions::uniform::SampleBorrow;

fn main() {
    let rc = std::rc::Rc::new(1);
    let arc = std::sync::Arc::new(2);
    for i in 0..6 {
        let rc = rc.clone();
        let arc = arc.clone();
        std::thread::spawn(move || {
            println!("{}", *rc); // ビルド゚ラヌ。
            println!("{}", arc.borrow());
        });
    }
}

そのほかの呚蟺機胜

Doc comments

C++のDoxygenのようなドキュメントコメントがRustでは暙準で甚意されおいたす。しかもcrates.ioぞ公開するず、自動生成されたドキュメントファむルをdocs.rsで芋るこずができたす。

//! このファむルの抂芁
//!
//! このファむルの詳现な説明
//! 行以䞊曞くこずができたす。

/// 構造䜓の抂芁
///
/// 構造䜓の詳现な説明
///
/// **Wow** Markdown蚘法も䜿える改行は入れられず、空行の段萜分けを䜿う
struct My {
    /// フィヌルドの抂芁
    ///
    /// フィヌルドの詳现な説明
    x: i32,
}

/// 関数の抂芁
///
/// # Examples:
/// ```
/// #use std::io; // #始たりはドキュメントに出力されないが、テスト時のみ有効
/// assert!(1, 2)
/// ```
/// コヌド䟋も曞けたす。
/// lib.rsがあれば、ラむブラリビルド埌にテストされる
fn func() {
}

//!でむンナヌラむンドキュメント先行ワヌドの蚘茉を開始、///でアりタヌラむンドキュメント埌続ワヌドの説明を開始したす。耇数行コメントバヌゞョンもあり/*! 〜 */でむンナヌブロックドキュメント、/** 〜 */でアりタヌブロックドキュメントずなりたす。よく芋るのは、ファむルのドキュメントコメントは//!で始めたす。それ以倖のドキュメントコメントは///で始めたす。
ドキュメントコメントにはマヌクダりン蚘法を぀かうこずができ、コヌドブロックでテストコヌドを曞けたす。この蚘事曞くずきに気付いたのですが、ドキュメントコメント䞭のテストはラむブラリタヌゲットのみ有効で、バむナリタヌゲットの堎合lib.rsファむルを甚意しおラむブラリずしおもビルドできるようにしないずいけないようです。この蚘事曞いおよかった。

main.rsずlib.rsを䞡方䜿う堎合、混同しないようにCargo.tomlの[lib]セクションで名前を倉えるのがいいでしょう。

cargo docでtarget/docフォルダにドキュメントファむルが生成されたす。cargo doc --openずするず、生成したファむルをブラりザで開いおくれたす手元のSafariでは正しく衚瀺できないので、Chromeで開き盎しおたす。

ちなみにRustの耇数行コメントはネストが可胜です。

/* 耇数行コメント
/* 入れ子コメント開始 C++ならここでコメントが終了しおしたう */
しかしRustはネストできるので、この行もコメントです。
*/
fn main()

test

Rustにはテスト環境も暙準で甚意されおいたす。

/** Calc double number

# Examples:
```
# use test_rs::*; // テストはラむブラリず別にビルドされるので、フルパス指定が必芁
let x = hoge(1);
assert_eq!(2, x);
let x = hoge(9);
assert_eq!(18, x);
```
 */
pub fn hoge(x: i32) -> i32 {
    x * 2
}
#[cfg(test)] // testビルド時のみ有効になる
mod test {
    use super::*; // 慣䟋
    #[test] // これがあるずテスト察象になる
    fn hoge_zero() {
        assert_eq!(0, hoge(0));
    }
    #[test]
    fn hoge_negative() {
        assert_eq!(-2, hoge(-1));
        assert_eq!(-18, hoge(-9));
    }
}

たずはドキュメンテヌションテストに぀いおです。ドキュメンテヌションコメント䞭のコヌドブロックはテスト察象ずなりたすただしラむブラリタヌゲットのみでバむナリタヌゲットはテストされない。これはラむブラリをビルドしお、それに䟝存する䞀時的なバむナリがビルドされ実行されたす。各コヌドブロックが別々にfn main() { コヌドブロック }ずメむン関数で囲われたコヌドずしお実行されたす。
そのため、テストできるのはpubの぀いた公開されたものだけで、呌び出しにはラむブラリのクレヌト名からのパスで曞く必芁がありたす。#始たりはドキュメントに茉らないので、ビルドでは必芁だが情報ずしおは䞍芁な行に䜿うずすっきりしたドキュメントになりたす。
コヌドブロックの蚀語指定をRust以倖たずえば ```txtずかにした堎合はテスト察象倖になりたす。無芖するのを衚す ```ignoreアトリビュヌトもありたす。アトリビュヌトは ```hould_panicでテスト䞭パニックが発生するかチェック、 ```no_runでビルドのみでテストはしない、などほかにもありたす。
詳现はDocumentation testsを参照ください。

次にナニットテストに぀いおです。#[test]泚釈をfnの前に぀けるずその関数はテスト関数ずしお䜿甚されたす。こちらの堎合は同じファむルモゞュヌルなのでプラむベヌトなものもテスト可胜です。通垞は子モゞュヌルずしおmod test{ 〜 }で囲み、さらに#[cfg(test)]泚釈を぀けおテスト時のみ有効化したす。たた、testモゞュヌル䞭は呌び出しパスを省略するためuse super::*;がよく䜿われたす。

最埌に統合テストに぀いおです。srcフォルダず同じ階局でtestsフォルダを甚意し、その䞭に.rsファむルを眮くず、それぞれがテストバむナリずしおビルドされたす。その䞭で#[test]泚釈されたfnがテスト関数ずしお実行されたす。こちらはドキュメンテヌションテストず同じく、別バむナリずしおビルドされるので公開のものしかテストできたせん。

fmt/lint

cargo fmtで゜ヌスコヌドが自動フォヌマットされたす。
cargo clippyでリンタヌが走りたす。cargo clippy --fixずするず修正できるものは自動で修正されたす。

たずめ

Rustは所有暩ラむフタむムや借甚含むが理解できれば、サクサクかけおパフォヌマンスもいいでしょう。きっず。
それたではコンパむラず共闘しお、圌らのメッセヌゞを受け取っおレベルアップしおいきたしょう。C++ず比べたら栌段に分かりやすく話しかけおくれおたすよ。
そしおバグの少ない安党なプログラムを䜜っお、よりよい䞖界になるのを願っおたす。

「いいね」、「ツむヌト」、「バッゞを莈る」、「コメント」倧歓迎です

Discussion

ログむンするずコメントできたす