From 59e0429d1e834ff8665429534afeed82f139a144 Mon Sep 17 00:00:00 2001 From: "bn-e (CircleCI)" Date: Mon, 11 Jan 2021 11:01:13 +0000 Subject: [PATCH] ci: generate pages at 47f18a8 [ci skip] --- docs/second-edition/appendix-00.html | 2 +- docs/second-edition/appendix-01-keywords.html | 6 +- .../second-edition/appendix-02-operators.html | 8 +- .../appendix-03-derivable-traits.html | 14 +- docs/second-edition/appendix-04-macros.html | 10 +- .../appendix-05-translation.html | 2 +- .../appendix-06-newest-features.html | 12 +- .../appendix-07-nightly-rust.html | 12 +- docs/second-edition/book.css | 20 +- docs/second-edition/book.js | 48 +- docs/second-edition/ch00-00-introduction.html | 20 +- .../ch01-00-getting-started.html | 2 +- docs/second-edition/ch01-01-installation.html | 14 +- docs/second-edition/ch01-02-hello-world.html | 10 +- docs/second-edition/ch01-03-hello-cargo.html | 12 +- .../ch02-00-guessing-game-tutorial.html | 34 +- .../ch03-00-common-programming-concepts.html | 4 +- .../ch03-01-variables-and-mutability.html | 6 +- docs/second-edition/ch03-02-data-types.html | 24 +- .../ch03-03-how-functions-work.html | 8 +- docs/second-edition/ch03-04-comments.html | 2 +- docs/second-edition/ch03-05-control-flow.html | 18 +- .../ch04-00-understanding-ownership.html | 2 +- .../ch04-01-what-is-ownership.html | 22 +- .../ch04-02-references-and-borrowing.html | 8 +- docs/second-edition/ch04-03-slices.html | 12 +- docs/second-edition/ch05-00-structs.html | 2 +- .../ch05-01-defining-structs.html | 12 +- .../ch05-02-example-structs.html | 10 +- .../second-edition/ch05-03-method-syntax.html | 14 +- docs/second-edition/ch06-00-enums.html | 2 +- .../ch06-01-defining-an-enum.html | 6 +- docs/second-edition/ch06-02-match.html | 10 +- docs/second-edition/ch06-03-if-let.html | 4 +- docs/second-edition/ch07-00-modules.html | 2 +- .../ch07-01-mod-and-the-filesystem.html | 8 +- ...07-02-controlling-visibility-with-pub.html | 12 +- .../ch07-03-importing-names-with-use.html | 10 +- .../ch08-00-common-collections.html | 2 +- docs/second-edition/ch08-01-vectors.html | 14 +- docs/second-edition/ch08-02-strings.html | 24 +- docs/second-edition/ch08-03-hash-maps.html | 20 +- .../ch09-00-error-handling.html | 2 +- ...09-01-unrecoverable-errors-with-panic.html | 6 +- ...h09-02-recoverable-errors-with-result.html | 12 +- .../ch09-03-to-panic-or-not-to-panic.html | 12 +- docs/second-edition/ch10-00-generics.html | 4 +- docs/second-edition/ch10-01-syntax.html | 12 +- docs/second-edition/ch10-02-traits.html | 14 +- .../ch10-03-lifetime-syntax.html | 26 +- docs/second-edition/ch11-00-testing.html | 2 +- .../second-edition/ch11-01-writing-tests.html | 12 +- .../second-edition/ch11-02-running-tests.html | 14 +- .../ch11-03-test-organization.html | 18 +- .../second-edition/ch12-00-an-io-project.html | 2 +- ...2-01-accepting-command-line-arguments.html | 8 +- .../ch12-02-reading-a-file.html | 2 +- ...proving-error-handling-and-modularity.html | 28 +- ...04-testing-the-librarys-functionality.html | 14 +- ...05-working-with-environment-variables.html | 6 +- ...6-writing-to-stderr-instead-of-stdout.html | 8 +- .../ch13-00-functional-features.html | 2 +- docs/second-edition/ch13-01-closures.html | 16 +- docs/second-edition/ch13-02-iterators.html | 16 +- .../ch13-03-improving-our-io-project.html | 10 +- docs/second-edition/ch13-04-performance.html | 4 +- .../ch14-00-more-about-cargo.html | 2 +- .../ch14-01-release-profiles.html | 2 +- .../ch14-02-publishing-to-crates-io.html | 22 +- .../ch14-03-cargo-workspaces.html | 10 +- .../ch14-04-installing-binaries.html | 2 +- .../ch14-05-extending-cargo.html | 4 +- .../ch15-00-smart-pointers.html | 2 +- docs/second-edition/ch15-01-box.html | 12 +- docs/second-edition/ch15-02-deref.html | 14 +- docs/second-edition/ch15-03-drop.html | 4 +- docs/second-edition/ch15-04-rc.html | 6 +- .../ch15-05-interior-mutability.html | 12 +- .../ch15-06-reference-cycles.html | 14 +- docs/second-edition/ch16-00-concurrency.html | 2 +- docs/second-edition/ch16-01-threads.html | 8 +- .../ch16-02-message-passing.html | 8 +- docs/second-edition/ch16-03-shared-state.html | 14 +- ...-extensible-concurrency-sync-and-send.html | 10 +- docs/second-edition/ch17-00-oop.html | 2 +- docs/second-edition/ch17-01-what-is-oo.html | 10 +- .../second-edition/ch17-02-trait-objects.html | 12 +- .../ch17-03-oo-design-patterns.html | 20 +- docs/second-edition/ch18-00-patterns.html | 2 +- .../ch18-01-all-the-places-for-patterns.html | 14 +- docs/second-edition/ch18-02-refutability.html | 2 +- .../ch18-03-pattern-syntax.html | 38 +- .../ch19-00-advanced-features.html | 2 +- docs/second-edition/ch19-01-unsafe-rust.html | 20 +- .../ch19-02-advanced-lifetimes.html | 8 +- .../ch19-03-advanced-traits.html | 12 +- .../ch19-04-advanced-types.html | 10 +- ...19-05-advanced-functions-and-closures.html | 8 +- .../ch20-00-final-project-a-web-server.html | 2 +- .../ch20-01-single-threaded.html | 16 +- .../second-edition/ch20-02-multithreaded.html | 22 +- ...ch20-03-graceful-shutdown-and-cleanup.html | 8 +- docs/second-edition/foreword.html | 2 +- docs/second-edition/index.html | 2 +- docs/second-edition/print.html | 1024 ++++++++--------- docs/second-edition/searcher.js | 51 +- docs/second-edition/searchindex.js | 2 +- docs/second-edition/searchindex.json | 1 + 108 files changed, 1103 insertions(+), 1069 deletions(-) create mode 100644 docs/second-edition/searchindex.json diff --git a/docs/second-edition/appendix-00.html b/docs/second-edition/appendix-00.html index 95f2a72..33aefab 100644 --- a/docs/second-edition/appendix-00.html +++ b/docs/second-edition/appendix-00.html @@ -142,7 +142,7 @@

The Rust Programming Language

-

付録

+

付録

以下の節は、Rustの旅で役に立つと思えるかもしれない参考資料を含んでいます。

diff --git a/docs/second-edition/appendix-01-keywords.html b/docs/second-edition/appendix-01-keywords.html index d60f5bf..3d97f7c 100644 --- a/docs/second-edition/appendix-01-keywords.html +++ b/docs/second-edition/appendix-01-keywords.html @@ -142,7 +142,7 @@

The Rust Programming Language

-

付録A: キーワード

+

付録A: キーワード

@@ -151,7 +151,7 @@

The Rust Programming Language

そのため、識別子として使用することはできません。識別子の例は、関数名、変数名、引数名、構造体のフィールド名、 モジュール名、クレート名、定数名、マクロ名、静的な値の名前、属性名、型名、トレイト名、ライフタイム名です。

-

現在使用されているキーワード

+

現在使用されているキーワード

以下のキーワードは、解説された通りの機能が現状あります。

@@ -231,7 +231,7 @@

The Rust Programming Language

  • while - 式の結果に基づいて条件的にループする
  • -

    将来的な使用のために予約されているキーワード

    +

    将来的な使用のために予約されているキーワード

    以下のキーワードには機能が何もないものの、将来的に使用される可能性があるので、Rustにより予約されています。

    diff --git a/docs/second-edition/appendix-02-operators.html b/docs/second-edition/appendix-02-operators.html index 56955ea..c6bedbd 100644 --- a/docs/second-edition/appendix-02-operators.html +++ b/docs/second-edition/appendix-02-operators.html @@ -142,14 +142,14 @@

    The Rust Programming Language

    -

    付録B: 演算子と記号

    +

    付録B: 演算子と記号

    この付録は、演算子や、単独で現れたり、パス、ジェネリクス、トレイト境界、マクロ、属性、コメント、タプル、 かっこの文脈で現れる他の記号を含むRustの記法の用語集を含んでいます。

    -

    演算子

    +

    演算子

    @@ -296,7 +296,7 @@

    The Rust Programming Language

    -

    演算子以外のシンボル

    +

    演算子以外のシンボル

    以下のリストは、演算子として機能しない記号全部を含んでいます; つまり、関数やメソッド呼び出しのようには、 @@ -445,7 +445,7 @@

    The Rust Programming Language

    /**...*/ 外部ブロックdocコメント -

    タプル

    +

    タプル

    表B-8は、タプルの文脈で出現する記号を示しています。

    diff --git a/docs/second-edition/appendix-03-derivable-traits.html b/docs/second-edition/appendix-03-derivable-traits.html index 87295c2..24c710a 100644 --- a/docs/second-edition/appendix-03-derivable-traits.html +++ b/docs/second-edition/appendix-03-derivable-traits.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    付録C: 導出可能なトレイト

    +

    付録C: 導出可能なトレイト

    @@ -194,7 +194,7 @@

    The Rust Programming Language

    deriveと共に使用できるトレイトのリストが実に限りのないものになってしまうのです。deriveの実装には、 プロシージャルなマクロが関連します。マクロについては、付録Dで講義します。

    -

    プログラマ用の出力のDebug

    +

    プログラマ用の出力のDebug

    Debugトレイトにより、フォーマット文字列でのデバッグ整形が可能になり、 @@ -211,7 +211,7 @@

    The Rust Programming Language

    このマクロは、プログラマがどうして2つのインスタンスが等価でなかったのか確認できるように、 等価アサートが失敗したら、引数として与えられたインスタンスの値を出力します。

    -

    等価比較のためのPartialEqEq

    +

    等価比較のためのPartialEqEq

    PartialEqトレイトにより、型のインスタンスを比較して、等価性をチェックでき、==!=演算子の使用を可能にします。

    @@ -241,7 +241,7 @@

    The Rust Programming Language

    Eqが必要になる一例が、HashMap<K, V>のキーで、HashMap<K, V>が、2つのキーが同じであると判定できます。

    -

    順序付き比較のためのPartialOrdOrd

    +

    順序付き比較のためのPartialOrdOrd

    @@ -285,7 +285,7 @@

    The Rust Programming Language

    Ordが必要になる例は、BTreeSet<T>に値を格納する時です。 これは、値のソート順に基づいてデータを格納するデータ構造です。

    -

    値を複製するCloneCopy

    +

    値を複製するCloneCopy

    @@ -334,7 +334,7 @@

    The Rust Programming Language

    Copyで可能なこと全てがCloneでも達成可能ですが、コードがより遅い可能性や、 cloneを使用しなければならない箇所があったりします。

    -

    値を固定サイズの値にマップするHash

    +

    値を固定サイズの値にマップするHash

    @@ -348,7 +348,7 @@

    The Rust Programming Language

    Hashが必要になる例は、HashMap<K, V>にキーを格納し、データを効率的に格納することです。

    -

    既定値のためのDefault

    +

    既定値のためのDefault

    diff --git a/docs/second-edition/appendix-04-macros.html b/docs/second-edition/appendix-04-macros.html index 64efb4d..4fb284c 100644 --- a/docs/second-edition/appendix-04-macros.html +++ b/docs/second-edition/appendix-04-macros.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    付録D: マクロ

    +

    付録D: マクロ

    @@ -170,7 +170,7 @@

    The Rust Programming Language

    ここで示したコードは、将来のバージョンでも動き続けますが、この本の出版時点では利用可能ではないマクロを書くための追加の能力や、 より簡単な方法があるかもしれません。この付録から何かを実装しようとする場合には、そのことを肝に銘じておいてください。

    -

    マクロと関数の違い

    +

    マクロと関数の違い

    @@ -231,7 +231,7 @@

    The Rust Programming Language

    マクロと関数にはもう一つ、重要な違いがあります: ファイル内で呼び出すにマクロはスコープに導入しなければなりませんが、 一方で関数はどこにでも定義でき、どこでも呼び出せます。

    -

    一般的なメタプログラミングのためにmacro_rules!で宣言的なマクロ

    +

    一般的なメタプログラミングのためにmacro_rules!で宣言的なマクロ

    @@ -370,7 +370,7 @@

    The Rust Programming Language

    マクロの書き方をもっと学ぶには、オンラインドキュメンテーションか他のリソース、 “The Little Book of Rust Macros(訳注: Rustのマクロの小さな本)などを調べてください。

    -

    独自のderiveのためのプロシージャルマクロ

    +

    独自のderiveのためのプロシージャルマクロ

    @@ -728,7 +728,7 @@

    The Rust Programming Language

    プロシージャルマクロのHelloMacroトレイトの実装は、pancakesクレートが実装する必要なく、包含されました; #[derive(HelloMacro)]がトレイトの実装を追加したのです。

    -

    マクロの未来

    +

    マクロの未来

    diff --git a/docs/second-edition/appendix-05-translation.html b/docs/second-edition/appendix-05-translation.html index 6659c05..ed069fa 100644 --- a/docs/second-edition/appendix-05-translation.html +++ b/docs/second-edition/appendix-05-translation.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    付録E: 本の翻訳

    +

    付録E: 本の翻訳

    英語以外の言語のリソースです。ほとんどは翻訳中です; Translationsラベルを確認して、 diff --git a/docs/second-edition/appendix-06-newest-features.html b/docs/second-edition/appendix-06-newest-features.html index 6ca3ed6..6786a6f 100644 --- a/docs/second-edition/appendix-06-newest-features.html +++ b/docs/second-edition/appendix-06-newest-features.html @@ -142,12 +142,12 @@

    The Rust Programming Language

    -

    付録F: 最新の機能

    +

    付録F: 最新の機能

    この付録は、本の主な部分が完成してから安定版Rustに追加された機能をドキュメント化しています。

    -

    フィールド初期化省略

    +

    フィールド初期化省略

    @@ -180,7 +180,7 @@

    The Rust Programming Language

    } -

    ループから戻る

    +

    ループから戻る

    @@ -203,7 +203,7 @@

    The Rust Programming Language

    } -

    use宣言のネストされたグループ

    +

    use宣言のネストされたグループ

    @@ -236,7 +236,7 @@

    The Rust Programming Language

    # fn main() {} -

    境界を含む範囲

    +

    境界を含む範囲

    @@ -257,7 +257,7 @@

    The Rust Programming Language

    ...記法はそれでも、matchでは受け付けられますが、式では受け付けられません。..=を使用すべきです。

    -

    128ビット整数

    +

    128ビット整数

    Rust1.26.0で128ビットの整数基本型が追加されました:

    diff --git a/docs/second-edition/appendix-07-nightly-rust.html b/docs/second-edition/appendix-07-nightly-rust.html index e323156..2af03ce 100644 --- a/docs/second-edition/appendix-07-nightly-rust.html +++ b/docs/second-edition/appendix-07-nightly-rust.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    付録G: Rustの作られ方と“Nightly Rust”

    +

    付録G: Rustの作られ方と“Nightly Rust”

    @@ -153,7 +153,7 @@

    The Rust Programming Language

    それより新しいRustのどんな安定版でもコンパイルでき続けられるはずということに触れました。 この節は、これが本当のことであると保証する方法を説明します!

    -

    停滞なしの安定性

    +

    停滞なしの安定性

    @@ -170,7 +170,7 @@

    The Rust Programming Language

    安定版Rustの新しいバージョンにアップグレードするのを恐れる必要は何もないはずです。各アップグレートは痛みのないもののはずですが、 新しい機能、より少ないバグ、高速なコンパイル時間も齎すべきです。

    -

    シュポシュポ!リリースチャンネルと列車に乗ること

    +

    シュポシュポ!リリースチャンネルと列車に乗ること

    @@ -288,7 +288,7 @@

    The Rust Programming Language

    ベータリリースが予想した通りに動かなければ、チームに報告して、次の安定版のリリースが起きる前に直してもらうことができるのです! ベータリリースでの破損はどちらかといえば稀ですが、rustcもソフトウェアの一種であり、バグは確実に存在します。

    -

    安定しない機能

    +

    安定しない機能

    @@ -316,7 +316,7 @@

    The Rust Programming Language

    確実にこの本が執筆された時と安定版ビルドで有効化された時で異なるからです。ナイトリ限定の機能についてのドキュメンテーションは、 オンラインで発見できます。

    -

    RustupとRustナイトリの役目

    +

    RustupとRustナイトリの役目

    @@ -353,7 +353,7 @@

    The Rust Programming Language

    これで ~/projects/needs-nightly内でrustccargoを呼び出す度に、rustupは既定の安定版のRustではなく、 ナイトリRustを使用していることを確かめます。Rustプロジェクトが大量にある時には、重宝します。

    -

    RFCプロセスとチーム

    +

    RFCプロセスとチーム

    diff --git a/docs/second-edition/book.css b/docs/second-edition/book.css index 6013ab1..16129ae 100644 --- a/docs/second-edition/book.css +++ b/docs/second-edition/book.css @@ -1,6 +1,9 @@ html { font-family: "Open Sans", sans-serif; color: #333; + -webkit-text-size-adjust: none; + -ms-text-size-adjust: none; + text-size-adjust: none; } body { margin: 0; @@ -132,12 +135,6 @@ table thead td { padding-left: 20px; line-height: 1.9em; } -.section li { - -o-text-overflow: ellipsis; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; -} .page-wrapper { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; @@ -220,7 +217,7 @@ table thead td { #menu-bar i, #menu-bar .icon-button { position: relative; - margin: 0 10px; + margin: 0 8px; z-index: 10; line-height: 50px; cursor: pointer; @@ -230,6 +227,12 @@ table thead td { -ms-transition: color 0.5s; transition: color 0.5s; } +@media only screen and (max-width: 420px) { + #menu-bar i, + #menu-bar .icon-button { + margin: 0 5px; + } +} #menu-bar #print-button { margin: 0 15px; } @@ -240,6 +243,9 @@ html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-conta -ms-transform: translateY(-60px); transform: translateY(-60px); } +.left-buttons { + margin: 0 5px; +} .no-js .left-buttons { display: none; } diff --git a/docs/second-edition/book.js b/docs/second-edition/book.js index b0d89f3..aea7eda 100644 --- a/docs/second-edition/book.js +++ b/docs/second-edition/book.js @@ -18,13 +18,23 @@ function playpen_text(playpen) { (function codeSnippets() { // Hide Rust code lines prepended with a specific character var hiding_character = "#"; - var request = fetch("https://play.rust-lang.org/meta/crates", { - headers: { - 'Content-Type': "application/json", - }, - method: 'POST', - mode: 'cors', - }); + + var playpens = Array.from(document.querySelectorAll(".playpen")); + if (playpens.length > 0) { + fetch("https://play.rust-lang.org/meta/crates", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + }) + .then(response => response.json()) + .then(response => { + // get list of crates available in the rust playground + let playground_crates = response.crates.map(item => item["id"]); + playpens.forEach(block => handle_crate_list_update(block, playground_crates)); + }); + } function handle_crate_list_update(playpen_block, playground_crates) { // update the play buttons after receiving the response @@ -150,9 +160,11 @@ function playpen_text(playpen) { var lines = code_block.innerHTML.split("\n"); var first_non_hidden_line = false; var lines_hidden = false; + var trimmed_line = ""; for (var n = 0; n < lines.length; n++) { - if (lines[n].trim()[0] == hiding_character) { + trimmed_line = lines[n].trim(); + if (trimmed_line[0] == hiding_character && trimmed_line[1] != hiding_character) { if (first_non_hidden_line) { lines[n] = "" + "\n" + lines[n].replace(/(\s*)# ?/, "$1") + ""; } @@ -167,6 +179,9 @@ function playpen_text(playpen) { else { first_non_hidden_line = true; } + if (trimmed_line[0] == hiding_character && trimmed_line[1] == hiding_character) { + lines[n] = lines[n].replace("##", "#") + } } code_block.innerHTML = lines.join(""); @@ -274,17 +289,6 @@ function playpen_text(playpen) { }); } }); - - request - .then(function (response) { return response.json(); }) - .then(function (response) { - // get list of crates available in the rust playground - let playground_crates = response.crates.map(function (item) { return item["id"]; }); - Array.from(document.querySelectorAll(".playpen")).forEach(function (block) { - handle_crate_list_update(block, playground_crates); - }); - }); - })(); (function themes() { @@ -293,9 +297,9 @@ function playpen_text(playpen) { var themePopup = document.getElementById('theme-list'); var themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); var stylesheets = { - ayuHighlight: document.querySelector("[href='ayu-highlight.css']"), - tomorrowNight: document.querySelector("[href='tomorrow-night.css']"), - highlight: document.querySelector("[href='highlight.css']"), + ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"), + tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"), + highlight: document.querySelector("[href$='highlight.css']"), }; function showThemes() { diff --git a/docs/second-edition/ch00-00-introduction.html b/docs/second-edition/ch00-00-introduction.html index b6d3c5a..81697c1 100644 --- a/docs/second-edition/ch00-00-introduction.html +++ b/docs/second-edition/ch00-00-introduction.html @@ -143,7 +143,7 @@

    The Rust Programming Language

    -

    導入

    +

    導入

    @@ -165,12 +165,12 @@

    The Rust Programming Language

    Rustは、その衝突に挑戦しています。バランスのとれた強力な技術の許容量と素晴らしい開発者経験を通して、 Rustは伝統的にそれらの制御と紐付いていた困難全てなしに低レベルの詳細(メモリ使用など)を制御する選択肢を与えてくれます。

    -

    Rustは誰のためのものなの

    +

    Rustは誰のためのものなの

    Rustは、様々な理由により多くの人にとって理想的です。いくつか最も重要なグループを見ていきましょう。

    -

    開発者チーム

    +

    開発者チーム

    @@ -204,7 +204,7 @@

    The Rust Programming Language

    これらや他のツールをRustのエコシステムで使用することで、開発者はシステムレベルのコードを記述しつつ、 生産的になれます。

    -

    学生

    +

    学生

    @@ -215,7 +215,7 @@

    The Rust Programming Language

    多くの人がOS開発などの話題を学んできました。コミュニティはとても暖かく、喜んで学生の質問に答えてくれます。 この本のような努力を通じて、Rustチームはシステムの概念を多くの人、特にプログラミング初心者にとってアクセス可能にしたいと考えています。

    -

    企業

    +

    企業

    @@ -226,14 +226,14 @@

    The Rust Programming Language

    オーディオとビデオの解析および変換、暗号通貨、生物情報学、サーチエンジン、IoTアプリケーション、 機械学習、Firefoxウェブブラウザの主要部分さえ含まれます。

    -

    オープンソース開発者

    +

    オープンソース開発者

    Rustは、Rustプログラミング言語やコミュニティ、開発者ツール、ライブラリを開発したい方向けです。 あなたがRust言語に貢献されることを心よりお待ちしております。

    -

    スピードと安定性に価値を見出す方

    +

    スピードと安定性に価値を見出す方

    @@ -258,7 +258,7 @@

    The Rust Programming Language

    ただの最大の出資者の一部です。総合すると、Rustの最大の野望は、プログラマが数十年間受け入れてきた代償を排除することです: つまり、安全性生産性、スピードエルゴノミクスです。Rustを試してみて、その選択が自分に合っているか確かめてください。

    -

    この本は誰のためのものなの

    +

    この本は誰のためのものなの

    @@ -270,7 +270,7 @@

    The Rust Programming Language

    プログラミングとはなんなのかやそれについて考える方法について多くを語るつもりはありません。 もし、完全なプログラミング初心者であれば、プログラミング入門を特に行う本を読むことでよりよく役に立つでしょう。

    -

    この本の使い方

    +

    この本の使い方

    @@ -376,7 +376,7 @@

    The Rust Programming Language

    周りのテキストを読んで実行しようとしている例がエラーになることを意図しているのか確認することを確かめてください。 ほとんどの場合、コンパイルできないあらゆるコードの正しいバージョンへと導きます。

    -

    ソースコード

    +

    ソースコード

    この本が生成されるソースファイルは、GitHubで見つかります。

    diff --git a/docs/second-edition/ch01-00-getting-started.html b/docs/second-edition/ch01-00-getting-started.html index bdc9038..840e3ff 100644 --- a/docs/second-edition/ch01-00-getting-started.html +++ b/docs/second-edition/ch01-00-getting-started.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    事始め

    +

    事始め

    Rustの旅を始めましょう!学ぶべきことはたくさんありますが、いかなる旅もどこかから始まります。 diff --git a/docs/second-edition/ch01-01-installation.html b/docs/second-edition/ch01-01-installation.html index 4306a4e..a031b1d 100644 --- a/docs/second-edition/ch01-01-installation.html +++ b/docs/second-edition/ch01-01-installation.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    インストール

    +

    インストール

    @@ -175,14 +175,14 @@

    The Rust Programming Language

    -

    コマンドライン表記

    +

    コマンドライン表記

    この章及び、本を通して、端末で使用するなんらかのコマンドを示すことがあります。読者が入力するべき行は、 全て$で始まります。$文字を入れる必要はありません; 各コマンドの開始を示しているだけです。 $で始まらない行は、典型的には直前のコマンドの出力を示します。また、PowerShell限定の例は、 $ではなく、>を使用します。

    -

    LinuxとmacOSにrustupをインストールする

    +

    LinuxとmacOSにrustupをインストールする

    LinuxかmacOSを使用しているなら、端末を開き、以下のコマンドを入力してください:

    $ curl https://sh.rustup.rs -sSf | sh
    @@ -224,7 +224,7 @@ 

    The Rust Programming Language

    一般的なRustパッケージの中には、Cコードに依存し、Cコンパイラが必要になるものもあります。 故に今インストールする価値はあるかもしれません。

    -

    Windowsでrustupをインストールする

    +

    Windowsでrustupをインストールする

    @@ -240,7 +240,7 @@

    The Rust Programming Language

    これ以降、cmd.exeとPowerShellの両方で動くコマンドを使用します。 特定の違いがあったら、どちらを使用すべきか説明します。

    -

    更新及びアンインストール

    +

    更新及びアンインストール

    rustup経由でRustをインストールしたら、最新版への更新は、簡単になります。シェルから、 @@ -253,7 +253,7 @@

    The Rust Programming Language

    $ rustup self uninstall
     
    -

    トラブルシューティング

    +

    トラブルシューティング

    Rustが正常にインストールされているか確かめるには、シェルを開いて以下の行を入力してください:

    @@ -284,7 +284,7 @@

    The Rust Programming Language

    明日使えるかもしれないトリビアでした。

    -

    ローカルのドキュメンテーション

    +

    ローカルのドキュメンテーション

    diff --git a/docs/second-edition/ch01-02-hello-world.html b/docs/second-edition/ch01-02-hello-world.html index c85e6e3..fe92ab2 100644 --- a/docs/second-edition/ch01-02-hello-world.html +++ b/docs/second-edition/ch01-02-hello-world.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Hello, World!

    +

    Hello, World!

    @@ -164,7 +164,7 @@

    The Rust Programming Language

    その前線で急激に成果があがっています!

    -

    プロジェクトのディレクトリを作成する

    +

    プロジェクトのディレクトリを作成する

    @@ -197,7 +197,7 @@

    The Rust Programming Language

    > cd hello_world
    -

    Rustプログラムを書いて走らせる

    +

    Rustプログラムを書いて走らせる

    @@ -240,7 +240,7 @@

    The Rust Programming Language

    Hello, world!が確かに出力されたら、おめでとうございます!正式にRustプログラムを書きました。 Rustプログラマになったのです!ようこそ!

    -

    Rustプログラムの解剖

    +

    Rustプログラムの解剖

    Hello, world!プログラムでちょうど何が起こったのか詳しく確認しましょう。 @@ -300,7 +300,7 @@

    The Rust Programming Language

    4番目にこの行をセミコロン(;)で終え、この式が終わり、次の式の準備ができていると示唆していることです。 Rustコードのほとんどの行は、セミコロンで終わります。

    -

    コンパイルと実行は個別のステップ

    +

    コンパイルと実行は個別のステップ

    新しく作成したプログラムをちょうど実行したので、その途中の手順を調査しましょう。

    diff --git a/docs/second-edition/ch01-03-hello-cargo.html b/docs/second-edition/ch01-03-hello-cargo.html index 88d3a44..183279c 100644 --- a/docs/second-edition/ch01-03-hello-cargo.html +++ b/docs/second-edition/ch01-03-hello-cargo.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Hello, Cargo!

    +

    Hello, Cargo!

    @@ -174,7 +174,7 @@

    The Rust Programming Language

    バージョンナンバーが見えたら、インストールされています!command not foundなどのエラーが見えたら、 自分のインストール方法をドキュメンテーションで確認して、Cargoを個別にインストールする方法を決定してください。

    -

    Cargoでプロジェクトを作成する

    +

    Cargoでプロジェクトを作成する

    @@ -277,7 +277,7 @@

    The Rust Programming Language

    実際にCargoを使用するプロジェクトに変換することができます。プロジェクトのコードをsrcディレクトリに移動し、 適切なCargo.tomlファイルを作成してください。

    -

    Cargoプロジェクトをビルドし、実行する

    +

    Cargoプロジェクトをビルドし、実行する

    @@ -365,7 +365,7 @@

    The Rust Programming Language

    Cargoを使用する追加の利点は、使用しているOSに関わらず、同じコマンドが使用できることです。 故にこの時点で、WindowsとLinux及びmacOSで特定の手順を提供することは最早なくなります。

    -

    リリースビルドを行う

    +

    リリースビルドを行う

    @@ -383,7 +383,7 @@

    The Rust Programming Language

    頻繁に再ビルドをかけたい開発用と、繰り返し再ビルドすることはなく、できるだけ高速に動いてユーザにあげる最終的なプログラムをビルドする用です。 コードの実行時間をベンチマークするなら、cargo build --releaseを確実に実行し、target/releaseの実行可能ファイルでベンチマークしてください。

    -

    習慣としてのCargo

    +

    習慣としてのCargo

    @@ -404,7 +404,7 @@

    The Rust Programming Language

    Cargoについてより詳しく知るには、ドキュメンテーションを確認してください。

    -

    まとめ

    +

    まとめ

    既にRustの旅の素晴らしいスタートを切っています!この章では、以下の方法を学びました:

    diff --git a/docs/second-edition/ch02-00-guessing-game-tutorial.html b/docs/second-edition/ch02-00-guessing-game-tutorial.html index ebe17f4..7f45e51 100644 --- a/docs/second-edition/ch02-00-guessing-game-tutorial.html +++ b/docs/second-edition/ch02-00-guessing-game-tutorial.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    数当てゲームをプログラムする

    +

    数当てゲームをプログラムする

    @@ -163,7 +163,7 @@

    The Rust Programming Language

    その予想が小さすぎたか大きすぎたかを出力します。予想が当たっていれば、ゲームは祝福メッセージを表示し、 終了します。

    -

    新規プロジェクトの立ち上げ

    +

    新規プロジェクトの立ち上げ

    新規プロジェクトを立ち上げるには、第1章で作成したprojectsディレクトリに行き、 @@ -221,7 +221,7 @@

    The Rust Programming Language

    再度src/main.rsファイルを開きましょう。ここにすべてのコードを書いていきます。

    -

    予想を処理する

    +

    予想を処理する

    @@ -290,7 +290,7 @@

    The Rust Programming Language

    このコードは、このゲームが何かを出力し、ユーザに入力を求めています。

    -

    値を変数に保持する

    +

    値を変数に保持する

    次に、ユーザ入力を保持する場所を作りましょう。こんな感じに:

    let mut guess = String::new();
    @@ -395,7 +395,7 @@ 

    The Rust Programming Language

    故に、&guessと書くのではなく、&mut guessと書いて、可変にする必要があるのです。 (第4章で参照についてより詳細に説明します)

    -

    Result型で失敗の可能性を扱う

    +

    Result型で失敗の可能性を扱う

    @@ -480,7 +480,7 @@

    The Rust Programming Language

    問題が起きた時にプロラグムをクラッシュさせたいので、expectを使用できるわけです。 エラーから復旧する方法については、第9章で学ぶでしょう。

    -

    println!マクロのプレースホルダーで値を出力する

    +

    println!マクロのプレースホルダーで値を出力する

    閉じ波かっこを除けば、ここまでに追加されたコードのうち議論すべきものは、残り1行であり、それは以下の通りです:

    @@ -508,7 +508,7 @@

    The Rust Programming Language

    このコードは、x = 5 and y = 10と出力するでしょう.

    -

    最初の部分をテストする

    +

    最初の部分をテストする

    数当てゲームの最初の部分をテストしてみましょう。cargo runでプログラムを走らせてください:

    $ cargo run
    @@ -524,7 +524,7 @@ 

    The Rust Programming Language

    ここまでで、ゲームの最初の部分は完成になります: キーボードからの入力を受け付け、出力できています。

    -

    秘密の数字を生成する

    +

    秘密の数字を生成する

    @@ -535,7 +535,7 @@

    The Rust Programming Language

    Rustの標準ライブラリには、乱数機能はまだ含まれていません。ですが、実は、 Rustの開発チームがrandクレートを用意してくれています。

    -

    クレートを使用して機能を追加する

    +

    クレートを使用して機能を追加する

    @@ -630,7 +630,7 @@

    The Rust Programming Language

    依存は変更していないので、Cargoは、既にダウンロードしてコンパイルまで済ませてある依存を使用できると検知します。 自分で書いたコードのみ再ビルドをかけるわけです。

    -

    Cargo.lockファイルで再現可能なビルドを保証する

    +

    Cargo.lockファイルで再現可能なビルドを保証する

    @@ -657,7 +657,7 @@

    The Rust Programming Language

    このことにより、自動的に再現可能なビルドを構成できるのです。つまり、明示的にアップグレードしない限り、 プロジェクトが使用するバージョンは0.3.14に保たれるのです。Cargo.lockファイルのおかげでね。

    -

    クレートを更新して新バージョンを取得する

    +

    クレートを更新して新バージョンを取得する

    @@ -705,7 +705,7 @@

    The Rust Programming Language

    Cargoのおかげでライブラリはとても簡単に再利用ができるので、 Rustaceanは数多くのパッケージから構成された小規模のプロジェクトを書くことができるのです。

    -

    乱数を生成する

    +

    乱数を生成する

    Cargo.tomlrandクレートを追加したので、randクレートを使用開始しましょう。 @@ -804,7 +804,7 @@

    The Rust Programming Language

    毎回異なる乱数が出て、その数字はすべて1から100の範囲になるはずです。よくやりました!

    -

    予想と秘密の数字を比較する

    +

    予想と秘密の数字を比較する

    @@ -1053,7 +1053,7 @@

    The Rust Programming Language

    ここまでで大方ゲームはうまく動くようになりましたが、まだユーザは1回しか予想できません。 ループを追加して、その部分を変更しましょう!

    -

    ループで複数回の予想を可能にする

    +

    ループで複数回の予想を可能にする

    loopキーワードは、無限ループを作り出します。これを追加して、ユーザが何回も予想できるようにしましょう:

    @@ -1128,7 +1128,7 @@

    The Rust Programming Language

    quitと入力すれば、実際にゲームを終了できるわけですが、別に他の数字以外の入力でもそうなります。 しかしながら、これは最低限度と言えるでしょう。正しい数字が予想されたら、自動的にゲームが停止してほしいわけです。

    -

    正しい予想をした後に終了する

    +

    正しい予想をした後に終了する

    break文を追加して、ユーザが勝った時にゲームが終了するようにプログラムしましょう:

    @@ -1153,7 +1153,7 @@

    The Rust Programming Language

    プログラムはループを抜けるようになりました。ついでに、ループを抜けることは、プログラムを終了することを意味します。 ループがmain関数の最後の部分だからですね。

    -

    不正な入力を処理する

    +

    不正な入力を処理する

    @@ -1286,7 +1286,7 @@

    The Rust Programming Language

    リスト2-6: 数当てゲームの完全なコード

    -

    まとめ

    +

    まとめ

    ここまでで、数当てゲームの構築に成功しました。おめでとうございます!

    diff --git a/docs/second-edition/ch03-00-common-programming-concepts.html b/docs/second-edition/ch03-00-common-programming-concepts.html index 845768f..286e4f7 100644 --- a/docs/second-edition/ch03-00-common-programming-concepts.html +++ b/docs/second-edition/ch03-00-common-programming-concepts.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    一般的なプログラミングの概念

    +

    一般的なプログラミングの概念

    @@ -167,7 +167,7 @@

    The Rust Programming Language

    -

    キーワード

    +

    キーワード

    Rust言語にも他の言語同様、キーワードが存在し、これらは言語だけが使用できるようになっています。 これらの単語は、変数や関数名には使えないことを弁えておいてください。ほとんどのキーワードは、特別な意味を持っており、 自らのRustプログラムにおいて、様々な作業をこなすために使用することができます; diff --git a/docs/second-edition/ch03-01-variables-and-mutability.html b/docs/second-edition/ch03-01-variables-and-mutability.html index e82bebb..32f6e88 100644 --- a/docs/second-edition/ch03-01-variables-and-mutability.html +++ b/docs/second-edition/ch03-01-variables-and-mutability.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    変数と可変性

    +

    変数と可変性

    @@ -262,7 +262,7 @@

    The Rust Programming Language

    小規模なデータ構造なら、新規インスタンスを生成して、もっと関数型っぽいコードを書く方が通して考えやすくなるため、 低パフォーマンスは、その簡潔性を得るのに足りうるペナルティになるかもしれません。

    -

    変数と定数(constants)の違い

    +

    変数と定数(constants)の違い

    @@ -315,7 +315,7 @@

    The Rust Programming Language

    コードの将来的な管理者にとって値の意味を汲むのに役に立ちます。将来、ハードコードされた値を変える必要が出た時に、 たった1箇所を変更するだけで済むようにもしてくれます。

    -

    シャドーイング

    +

    シャドーイング

    diff --git a/docs/second-edition/ch03-02-data-types.html b/docs/second-edition/ch03-02-data-types.html index 76fdd9b..3f1a1c7 100644 --- a/docs/second-edition/ch03-02-data-types.html +++ b/docs/second-edition/ch03-02-data-types.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    データ型

    +

    データ型

    @@ -183,7 +183,7 @@

    The Rust Programming Language

    他のデータ型についても、様々な型注釈を目にすることになるでしょう。

    -

    スカラー型

    +

    スカラー型

    @@ -191,7 +191,7 @@

    The Rust Programming Language

    整数、浮動小数点数、論理値、最後に文字です。他のプログラミング言語でも、これらの型を見かけたことはあるでしょう。 Rustでの動作方法に飛び込みましょう。

    -

    整数型

    +

    整数型

    @@ -280,7 +280,7 @@

    The Rust Programming Language

    Rustの基準型は一般的にいい選択肢になります。整数型の基準はi32型です: 64ビットシステム上でも、 この型が普通最速になります。isizeusizeを使う主な状況は、何らかのコレクションにアクセスすることです。

    -

    浮動小数点型

    +

    浮動小数点型

    @@ -304,7 +304,7 @@

    The Rust Programming Language

    浮動小数点数は、IEEE-754規格に従って表現されています。f32が単精度浮動小数点数、 f64が倍精度浮動小数点数です。

    -

    数値演算

    +

    数値演算

    @@ -353,7 +353,7 @@

    The Rust Programming Language

    これらの文の各式は、数学演算子を使用しており、一つの値に評価され、そして、変数に束縛されます。 付録BにRustで使える演算子の一覧が載っています。

    -

    論理値型

    +

    論理値型

    @@ -380,7 +380,7 @@

    The Rust Programming Language

    論理値を使う主な手段は、条件式です。例えば、if式などですね。if式のRustでの動作方法については、 「制御フロー」節で講義します。

    -

    文字型

    +

    文字型

    @@ -410,13 +410,13 @@

    The Rust Programming Language

    ところが、「文字」は実はユニコードの概念ではないので、文字とは何かという人間としての直観は、 Rustにおけるchar値が何かとは合致しない可能性があります。この話題については、第8章の「文字列」で詳しく議論しましょう。

    -

    複合型

    +

    複合型

    複合型により、複数の値を一つの型にまとめることができます。Rustには、 2種類の基本的な複合型があります: タプルと配列です。

    -

    タプル型

    +

    タプル型

    タプルは、複数の型の何らかの値を一つの複合型にまとめ上げる一般的な手段です。

    @@ -479,7 +479,7 @@

    The Rust Programming Language

    このプログラムは、新しいタプルxを作成し、添え字アクセスで各要素に対して新しい変数も作成しています。 多くのプログラミング言語同様、タプルの最初の添え字は0です。

    -

    配列型

    +

    配列型

    @@ -521,7 +521,7 @@

    The Rust Programming Language

    "August", "September", "October", "November", "December"]; #}
    -
    配列の要素にアクセスする
    +
    配列の要素にアクセスする

    配列は、スタック上に確保される一塊のメモリです。添え字によって、 @@ -541,7 +541,7 @@

    The Rust Programming Language

    この例では、firstという名前の変数には1という値が格納されます。配列の[0]番目にある値が、 それだからですね。secondという名前の変数には、配列の[1]番目の値2が格納されます。

    -
    配列要素への無効なアクセス
    +
    配列要素への無効なアクセス
    diff --git a/docs/second-edition/ch03-03-how-functions-work.html b/docs/second-edition/ch03-03-how-functions-work.html index 07acbb2..786fb1d 100644 --- a/docs/second-edition/ch03-03-how-functions-work.html +++ b/docs/second-edition/ch03-03-how-functions-work.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    関数

    +

    関数

    @@ -203,7 +203,7 @@

    The Rust Programming Language

    行出力は、main関数内に書かれた順序で実行されています。最初に"Hello, world"メッセージが出て、 それからanother_functionが呼ばれて、こちらのメッセージが出力されています。

    -

    関数の引数

    +

    関数の引数

    @@ -290,7 +290,7 @@

    The Rust Programming Language

    この値で出力されました。

    -

    関数本体は、文と式を含む

    +

    関数本体は、文と式を含む

    @@ -398,7 +398,7 @@

    The Rust Programming Language

    式は終端にセミコロンを含みません。式の終端にセミコロンを付けたら、文に変えてしまいます。そして、文は値を返しません。 次に関数の戻り値や式を見ていく際にこのことを肝に銘じておいてください。

    -

    戻り値のある関数

    +

    戻り値のある関数

    diff --git a/docs/second-edition/ch03-04-comments.html b/docs/second-edition/ch03-04-comments.html index 7832e48..dd2914f 100644 --- a/docs/second-edition/ch03-04-comments.html +++ b/docs/second-edition/ch03-04-comments.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    コメント

    +

    コメント

    diff --git a/docs/second-edition/ch03-05-control-flow.html b/docs/second-edition/ch03-05-control-flow.html index 1500a87..227bc0e 100644 --- a/docs/second-edition/ch03-05-control-flow.html +++ b/docs/second-edition/ch03-05-control-flow.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    フロー制御

    +

    フロー制御

    @@ -152,7 +152,7 @@

    The Rust Programming Language

    条件が真の間繰り返しコードを走らせるか決定したりすることは、多くのプログラミング言語において、基本的な構成ブロックです。 Rustコードの実行フローを制御する最も一般的な文法要素は、if式とループです。

    -

    if

    +

    if

    @@ -269,7 +269,7 @@

    The Rust Programming Language

    このコードを実行したら、number was something other than zeroと表示されるでしょう。

    -

    else ifで複数の条件を扱う

    +

    else ifで複数の条件を扱う

    ifelseを組み合わせてelse if式にすることで複数の条件を持たせることもできます。例です:

    @@ -320,7 +320,7 @@

    The Rust Programming Language

    コードをリファクタリングしたくなるかもしれません。これらのケースに有用なmatchと呼ばれる、 強力なRustの枝分かれ文法要素については第6章で解説します。

    -

    let文内でif式を使う

    +

    let文内でif式を使う

    ifは式なので、let文の右辺に持ってくることができます。リスト3-2のようにですね。

    @@ -412,7 +412,7 @@

    The Rust Programming Language

    どの変数に対しても、架空の複数の型があることを追いかけなければならないのであれば、コンパイラはより複雑になり、 コードに対して行える保証が少なくなってしまうでしょう。

    -

    ループでの繰り返し

    +

    ループでの繰り返し

    @@ -424,7 +424,7 @@

    The Rust Programming Language

    Rustには3種類のループが存在します: loopwhileforです。それぞれ試してみましょう。

    -

    loopでコードを繰り返す

    +

    loopでコードを繰り返す

    loopキーワードを使用すると、同じコードを何回も何回も永遠に、明示的にやめさせるまで実行します。

    @@ -471,7 +471,7 @@

    The Rust Programming Language

    第2章の「正しい予想をした後に終了する」節の数当てゲーム内でこれをして、ユーザが予想を的中させ、 ゲームに勝った時にプログラムを終了させたことを思い出してください。

    -

    whileで条件付きループ

    +

    whileで条件付きループ

    @@ -512,7 +512,7 @@

    The Rust Programming Language

    この文法要素により、loopifelsebreakを使った時に必要になるネストがなくなり、 より明確になります。条件が真の間、コードは実行されます; そうでなければ、ループを抜けます.

    -

    forでコレクションを覗き見る

    +

    forでコレクションを覗き見る

    while要素を使って配列などのコレクションの要素を覗き見ることができます。例えば、リスト3-4を見ましょう。

    @@ -621,7 +621,7 @@

    The Rust Programming Language

    こちらのコードの方が少しいいでしょう?

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch04-00-understanding-ownership.html b/docs/second-edition/ch04-00-understanding-ownership.html index b02dd6b..791446c 100644 --- a/docs/second-edition/ch04-00-understanding-ownership.html +++ b/docs/second-edition/ch04-00-understanding-ownership.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    所有権を理解する

    +

    所有権を理解する

    diff --git a/docs/second-edition/ch04-01-what-is-ownership.html b/docs/second-edition/ch04-01-what-is-ownership.html index d2cd3d1..b77dea3 100644 --- a/docs/second-edition/ch04-01-what-is-ownership.html +++ b/docs/second-edition/ch04-01-what-is-ownership.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    所有権とは?

    +

    所有権とは?

    Rustの中心的な機能は、所有権です。この機能は、説明するのは簡単なのですが、言語の残りの機能全てにかかるほど @@ -227,7 +227,7 @@

    The Rust Programming Language

    -

    スタックとヒープ

    +

    スタックとヒープ

    多くのプログラミング言語において、スタックとヒープについて考える機会はそう多くないでしょう。 しかし、Rustのようなシステムプログラミング言語においては、値がスタックに積まれるかヒープに置かれるかは、 言語の振る舞い方や、特定の決断を下す理由などに影響以上のものを与えるのです。 @@ -270,7 +270,7 @@

    The Rust Programming Language

    -

    所有権規則

    +

    所有権規則

    まず、所有権のルールについて見ていきましょう。 @@ -284,7 +284,7 @@

    The Rust Programming Language

  • 所有者がスコープから外れたら、値は破棄される。
  • -

    変数スコープ

    +

    変数スコープ

    @@ -344,7 +344,7 @@

    The Rust Programming Language

    ここで、スコープと変数が有効になる期間の関係は、他の言語に類似しています。さて、この理解のもとに、 String型を導入して構築していきましょう。

    -

    String

    +

    String

    @@ -411,7 +411,7 @@

    The Rust Programming Language

    では、ここでの違いは何でしょうか?なぜ、String型は可変化できるのに、リテラルはできないのでしょうか? 違いは、これら二つの型がメモリを扱う方法にあります。

    -

    メモリと確保

    +

    メモリと確保

    @@ -503,7 +503,7 @@

    The Rust Programming Language

    ヒープ上に確保されたデータを複数の変数に使用させるようなもっと複雑な場面では、コードの振る舞いは、 予期しないものになる可能性もあります。これから、そのような場面を掘り下げてみましょう。

    -

    変数とデータの相互作用法: ムーブ

    +

    変数とデータの相互作用法: ムーブ

    Rustにおいては、複数の変数が同じデータに対して異なる手段で相互作用することができます。 @@ -660,7 +660,7 @@

    The Rust Programming Language

    自動的にデータの"deep copy"が行われることは絶対にないわけです。それ故に、あらゆる自動コピーは、実行時性能の観点で言うと、 悪くないと考えてよいことになります。

    -

    変数とデータの相互作用法: クローン

    +

    変数とデータの相互作用法: クローン

    @@ -688,7 +688,7 @@

    The Rust Programming Language

    cloneメソッドの呼び出しを見かけたら、何らかの任意のコードが実行され、その実行コストは高いと把握できます。 何か違うことが起こっているなと見た目でわかるわけです。

    -

    スタックのみのデータ: コピー

    +

    スタックのみのデータ: コピー

    まだ話題にしていない別の問題があります。 @@ -753,7 +753,7 @@

    The Rust Programming Language

    (i32, String)は違う。 -

    所有権と関数

    +

    所有権と関数

    @@ -817,7 +817,7 @@

    The Rust Programming Language

    これらの静的チェックにより、ミスを犯さないでいられます。sxを使用するコードをmainに追加してみて、 どこで使えて、そして、所有権規則により、どこで使えないかを確認してください。

    -

    戻り値とスコープ

    +

    戻り値とスコープ

    値を返すことでも、所有権は移動します。リスト4-4は、リスト4-3と似た注釈のついた例です。

    diff --git a/docs/second-edition/ch04-02-references-and-borrowing.html b/docs/second-edition/ch04-02-references-and-borrowing.html index 73e8541..2d686e8 100644 --- a/docs/second-edition/ch04-02-references-and-borrowing.html +++ b/docs/second-edition/ch04-02-references-and-borrowing.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    参照と借用

    +

    参照と借用

    @@ -273,7 +273,7 @@

    The Rust Programming Language

    変数が標準で不変なのと全く同様に、参照も不変なのです。参照している何かを変更することは叶わないわけです。

    -

    可変な参照

    +

    可変な参照

    一捻り加えるだけでリスト4-6のコードのエラーは解決します:

    @@ -411,7 +411,7 @@

    The Rust Programming Language

    これらのエラーは、時としてイライラするものではありますが、Rustコンパイラがバグの可能性を早期に指摘してくれ(それも実行時ではなくコンパイル時に)、 問題の発生箇所をズバリ示してくれるのだと覚えておいてください。そうして想定通りにデータが変わらない理由を追いかける必要がなくなります。

    -

    宙に浮いた参照

    +

    宙に浮いた参照

    @@ -505,7 +505,7 @@

    The Rust Programming Language

    これは何の問題もなく動きます。所有権はムーブされ、何も解放されることはありません。

    -

    参照の規則

    +

    参照の規則

    参照について議論したことを再確認しましょう:

    diff --git a/docs/second-edition/ch04-03-slices.html b/docs/second-edition/ch04-03-slices.html index ea5b7c3..01a1748 100644 --- a/docs/second-edition/ch04-03-slices.html +++ b/docs/second-edition/ch04-03-slices.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    スライス型

    +

    スライス型

    @@ -311,7 +311,7 @@

    The Rust Programming Language

    運のいいことに、Rustにはこの問題への解決策が用意されています: 文字列スライスです。

    -

    文字列スライス

    +

    文字列スライス

    文字列スライスとは、Stringの一部への参照で、こんな見た目をしています:

    
    @@ -497,7 +497,7 @@ 

    The Rust Programming Language

    clearStringを切り詰める必要があるので、可変な参照を得ようとして失敗しているわけです。 RustのおかげでAPIが使いやすくなるだけでなく、ある種のエラー全てを完全にコンパイル時に排除してくれるのです!

    -

    文字列リテラルはスライスである

    +

    文字列リテラルはスライスである

    文字列は、バイナリに埋め込まれると話したことを思い出してください。今やスライスのことを知ったので、 @@ -513,7 +513,7 @@

    The Rust Programming Language

    ここでのsの型は、&strです: バイナリのその特定の位置を指すスライスです。 これは、文字列が不変である理由にもなっています。要するに、&strは不変な参照なのです。

    -

    引数としての文字列スライス

    +

    引数としての文字列スライス

    リテラルやString値のスライスを得ることができると知ると、first_wordに対して、もう一つ改善点を見出すことができます。 @@ -590,7 +590,7 @@

    The Rust Programming Language

    }
    -

    他のスライス

    +

    他のスライス

    文字列リテラルは、ご想像通り、文字列に特化したものです。ですが、もっと一般的なスライス型も存在します。 @@ -620,7 +620,7 @@

    The Rust Programming Language

    この種のスライスは使用するでしょう。これらのコレクションについて詳しくは、 第8章でベクタについて話すときに議論します。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch05-00-structs.html b/docs/second-edition/ch05-00-structs.html index 3f9ae31..4f12f74 100644 --- a/docs/second-edition/ch05-00-structs.html +++ b/docs/second-edition/ch05-00-structs.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    構造体を使用して関係のあるデータを構造化する

    +

    構造体を使用して関係のあるデータを構造化する

    diff --git a/docs/second-edition/ch05-01-defining-structs.html b/docs/second-edition/ch05-01-defining-structs.html index 1d0b490..856be2b 100644 --- a/docs/second-edition/ch05-01-defining-structs.html +++ b/docs/second-edition/ch05-01-defining-structs.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    構造体を定義し、インスタンス化する

    +

    構造体を定義し、インスタンス化する

    @@ -282,7 +282,7 @@

    The Rust Programming Language

    構造体にもっとフィールドがあれば、名前を繰り返すことはさらに煩わしくなるでしょう。 幸運なことに、便利な省略記法があります!

    -

    フィールドと変数が同名の時にフィールド初期化省略記法を使う

    +

    フィールドと変数が同名の時にフィールド初期化省略記法を使う

    @@ -323,7 +323,7 @@

    The Rust Programming Language

    emailフィールドとemail引数は同じ名前なので、email: emailと書くのではなく、 emailと書くだけで済むのです。

    -

    構造体更新記法で他のインスタンスからインスタンスを生成する

    +

    構造体更新記法で他のインスタンスからインスタンスを生成する

    多くは前のインスタンスの値を使用しつつ、変更する箇所もある形で新しいインスタンスを生成できるとしばしば有用です。 @@ -399,7 +399,7 @@

    The Rust Programming Language

    リスト5-7のコードも、emailusernameについては異なる値、activesign_in_countフィールドについては、 user1と同じ値になるインスタンスをuser2に生成します。

    -

    異なる型を生成する名前付きフィールドのないタプル構造体を使用する

    +

    異なる型を生成する名前付きフィールドのないタプル構造体を使用する

    @@ -439,7 +439,7 @@

    The Rust Programming Language

    3つのi32値からできていてもです。それ以外については、タプル構造体のインスタンスは、 タプルと同じように振る舞います: 分配して個々の部品にしたり、.と添え字を使用して個々の値にアクセスするなどです。

    -

    フィールドのないユニット(よう)構造体

    +

    フィールドのないユニット(よう)構造体

    @@ -501,7 +501,7 @@

    The Rust Programming Language

    -

    構造体データの所有権

    +

    構造体データの所有権

    リスト5-1のUser構造体定義において、&str文字列スライス型ではなく、所有権のあるString型を使用しました。 これは意図的な選択です。というのも、この構造体のインスタンスには全データを所有してもらう必要があり、 このデータは、構造体全体が有効な間はずっと有効である必要があるのです。

    diff --git a/docs/second-edition/ch05-02-example-structs.html b/docs/second-edition/ch05-02-example-structs.html index 23d5ddd..3f9de2c 100644 --- a/docs/second-edition/ch05-02-example-structs.html +++ b/docs/second-edition/ch05-02-example-structs.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    構造体を使ったプログラム例

    +

    構造体を使ったプログラム例

    @@ -181,7 +181,7 @@

    The Rust Programming Language

    (四角形の面積は、1500平方ピクセルです) -

    タプルでリファクタリングする

    +

    タプルでリファクタリングする

    @@ -202,7 +202,7 @@

    The Rust Programming Language

    幅と高さを一緒にグループ化する方が、より読みやすく、扱いやすくなるでしょう。 それをする一つの方法については、第3章の「タプル型」節ですでに議論しました: タプルを使うのです。

    -

    タプルでリファクタリングする

    +

    タプルでリファクタリングする

    リスト5-9は、タプルを使う別バージョンのプログラムを示しています。

    @@ -243,7 +243,7 @@

    The Rust Programming Language

    容易く、このことを忘れたり、これらの値を混ぜこぜにしたりしてエラーを発生させてしまうでしょう。 データの意味をコードに載せていないからです。

    -

    構造体でリファクタリングする: より意味付けする

    +

    構造体でリファクタリングする: より意味付けする

    @@ -298,7 +298,7 @@

    The Rust Programming Language

    Rectangleの面積を計算します。これにより、幅と高さが相互に関係していることが伝わり、 タプルの01という添え字を使うよりも、これらの値に説明的な名前を与えられるのです。プログラムの意図が明瞭になりました。

    -

    トレイトの導出で有用な機能を追加する

    +

    トレイトの導出で有用な機能を追加する

    diff --git a/docs/second-edition/ch05-03-method-syntax.html b/docs/second-edition/ch05-03-method-syntax.html index 9e25de8..c9338fd 100644 --- a/docs/second-edition/ch05-03-method-syntax.html +++ b/docs/second-edition/ch05-03-method-syntax.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    メソッド記法

    +

    メソッド記法

    @@ -155,7 +155,7 @@

    The Rust Programming Language

    メソッドは構造体の文脈(あるいはenumかトレイトオブジェクトの。これらについては各々第6章と17章で解説します)で定義されるという点で、 関数とは異なり、最初の引数は必ずselfになり、これはメソッドが呼び出されている構造体インスタンスを表します。

    -

    メソッドを定義する

    +

    メソッドを定義する

    @@ -279,7 +279,7 @@

    The Rust Programming Language

    -

    ->演算子はどこに行ったの?

    +

    ->演算子はどこに行ったの?

    CとC++では、メソッド呼び出しには2種類の異なる演算子が使用されます: オブジェクトに対して直接メソッドを呼び出すのなら、.を使用するし、オブジェクトのポインタに対してメソッドを呼び出し、 先にポインタを参照外しする必要があるなら、->を使用するわけです。 @@ -318,7 +318,7 @@

    The Rust Programming Language

    所有権を実際に使うのがRustにおいて簡単である大きな理由です。

    -

    より引数の多いメソッド

    +

    より引数の多いメソッド

    @@ -407,7 +407,7 @@

    The Rust Programming Language

    メソッドは、self引数の後にシグニチャに追加した引数を複数取ることができ、 その引数は、関数の引数と同様に動作するのです。

    -

    関連関数

    +

    関連関数

    @@ -450,7 +450,7 @@

    The Rust Programming Language

    この関数は、構造体によって名前空間分けされています: ::という記法は、関連関数とモジュールによって作り出される名前空間両方に使用されます。 モジュールについては第7章で議論します。

    -

    複数のimplブロック

    +

    複数のimplブロック

    @@ -486,7 +486,7 @@

    The Rust Programming Language

    ここでこれらのメソッドを個々のimplブロックに分ける理由はないのですが、合法な書き方です。 複数のimplブロックが有用になるケースは第10章で見ますが、そこではジェネリック型と、トレイトについて議論します。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch06-00-enums.html b/docs/second-edition/ch06-00-enums.html index 26d1331..670e7f8 100644 --- a/docs/second-edition/ch06-00-enums.html +++ b/docs/second-edition/ch06-00-enums.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Enumとパターンマッチング

    +

    Enumとパターンマッチング

    diff --git a/docs/second-edition/ch06-01-defining-an-enum.html b/docs/second-edition/ch06-01-defining-an-enum.html index ccc2f3f..ed6e04c 100644 --- a/docs/second-edition/ch06-01-defining-an-enum.html +++ b/docs/second-edition/ch06-01-defining-an-enum.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Enumを定義する

    +

    Enumを定義する

    @@ -180,7 +180,7 @@

    The Rust Programming Language

    これで、IpAddrKindはコードの他の場所で使用できる独自のデータ型になります。

    -

    Enumの値

    +

    Enumの値

    以下のようにして、IpAddrKindの各列挙子のインスタンスは生成できます:

    
    @@ -472,7 +472,7 @@ 

    The Rust Programming Language

    非常に一般的で有用な別の標準ライブラリのenumを見てみましょう: Optionです。

    -

    Option enumとNull値に勝る利点

    +

    Option enumとNull値に勝る利点

    diff --git a/docs/second-edition/ch06-02-match.html b/docs/second-edition/ch06-02-match.html index a0acbb4..8a9c468 100644 --- a/docs/second-edition/ch06-02-match.html +++ b/docs/second-edition/ch06-02-match.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    matchフロー制御演算子

    +

    matchフロー制御演算子

    @@ -251,7 +251,7 @@

    The Rust Programming Language

    } #}
    -

    値に束縛されるパターン

    +

    値に束縛されるパターン

    @@ -355,7 +355,7 @@

    The Rust Programming Language

    UsState::Alaskaという値です。そして、println!式でその束縛を使用することができ、 そのため、Coin enumの列挙子からQuarterに対する中身のstateの値を取得できたわけです。

    -

    Option<T>とのマッチ

    +

    Option<T>とのマッチ

    @@ -431,7 +431,7 @@

    The Rust Programming Language

    一旦慣れてしまえば、全ての言語にあってほしいと願うことになるでしょう。一貫してユーザのお気に入りなのです。

    -

    マッチは包括的

    +

    マッチは包括的

    もう一つ議論する必要のあるmatchの観点があります。一点バグがありコンパイルできないこんなバージョンのplus_one関数を考えてください:

    @@ -465,7 +465,7 @@

    The Rust Programming Language

    nullになるかもしれないのに値があると思い込まないよう、すなわち前に議論した10億ドルの失敗を犯さないよう、 コンパイラが保護してくれるわけです。

    -

    _というプレースホルダー

    +

    _というプレースホルダー

    diff --git a/docs/second-edition/ch06-03-if-let.html b/docs/second-edition/ch06-03-if-let.html index fb4b54c..179a3eb 100644 --- a/docs/second-edition/ch06-03-if-let.html +++ b/docs/second-edition/ch06-03-if-let.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    if letで簡潔なフロー制御

    +

    if letで簡潔なフロー制御

    @@ -262,7 +262,7 @@

    The Rust Programming Language

    matchを使って表現するには冗長的すぎるロジックがプログラムにあるようなシチュエーションに遭遇したら、 if letもRust道具箱にあることを思い出してください。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch07-00-modules.html b/docs/second-edition/ch07-00-modules.html index 89019b1..d3d6171 100644 --- a/docs/second-edition/ch07-00-modules.html +++ b/docs/second-edition/ch07-00-modules.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    モジュールを使用してコードを体系化し、再利用する

    +

    モジュールを使用してコードを体系化し、再利用する

    diff --git a/docs/second-edition/ch07-01-mod-and-the-filesystem.html b/docs/second-edition/ch07-01-mod-and-the-filesystem.html index b370632..12c37a5 100644 --- a/docs/second-edition/ch07-01-mod-and-the-filesystem.html +++ b/docs/second-edition/ch07-01-mod-and-the-filesystem.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    modとファイルシステム

    +

    modとファイルシステム

    @@ -197,7 +197,7 @@

    The Rust Programming Language

    コードの意図によって、いろんなシチュエーションで最適になるライブラリコードを体系化する別のオプションをお目にかけます。

    -

    モジュール定義

    +

    モジュール定義

    @@ -329,7 +329,7 @@

    The Rust Programming Language

    こちらで示したテクニックを使用して、並列したモジュールや、ネストしたモジュールなど、どんな構造のモジュールでも、 作成してください。

    -

    モジュールを別ファイルに移す

    +

    モジュールを別ファイルに移す

    @@ -664,7 +664,7 @@

    The Rust Programming Language

    network::clientというサブモジュールは専用のsrc/network/client.rsファイルを持てるわけです。 これで、頂点にあるsrc/client.rsは間違いなく、clientモジュールに属するコードになるわけです。

    -

    モジュールファイルシステムの規則

    +

    モジュールファイルシステムの規則

    ファイルに関するモジュール規則をまとめましょう:

    diff --git a/docs/second-edition/ch07-02-controlling-visibility-with-pub.html b/docs/second-edition/ch07-02-controlling-visibility-with-pub.html index 97fee1c..b3c5205 100644 --- a/docs/second-edition/ch07-02-controlling-visibility-with-pub.html +++ b/docs/second-edition/ch07-02-controlling-visibility-with-pub.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    pubで公開するか制御する

    +

    pubで公開するか制御する

    @@ -263,7 +263,7 @@

    The Rust Programming Language

    関数が「使用されている」という架空の外部使用の可能性を考慮してくれます。それ故に、関数が公開とマークされれば、 コンパイラはそれが自分のプログラムで使用されるべきという要求をなくし、その関数が未使用という警告も止めるのです。

    -

    関数を公開にする

    +

    関数を公開にする

    @@ -392,7 +392,7 @@

    The Rust Programming Language

    残る警告は1つなので、自分で解消してみてください!

    -

    プライバシー規則

    +

    プライバシー規則

    まとめると、要素の公開性は以下のようなルールになります:

    @@ -403,7 +403,7 @@

    The Rust Programming Language

  • 要素が非公開なら、直接の親モジュールとその親の子モジュールのみアクセスできます。
  • -

    プライバシー例

    +

    プライバシー例

    @@ -439,7 +439,7 @@

    The Rust Programming Language

    このコードをコンパイルする前に、try_me関数のどの行がエラーになるか当ててみてください。 それからコンパイルを試して、合ってたかどうか確かめ、エラーの議論を求めて読み進めてください!

    -

    エラーを確かめる

    +

    エラーを確かめる

    @@ -468,7 +468,7 @@

    The Rust Programming Language

    insideという名前のモジュールは非公開で子モジュールを持たないので、現在のモジュールであるoutermostからのみアクセスできます。 つまり、try_me関数は、outermost::inside::inner_functionoutermost::inside::secret_functionも呼び出すことを許されないのです。

    -

    エラーを修正する

    +

    エラーを修正する

    diff --git a/docs/second-edition/ch07-03-importing-names-with-use.html b/docs/second-edition/ch07-03-importing-names-with-use.html index 7a9ca43..ea7555b 100644 --- a/docs/second-edition/ch07-03-importing-names-with-use.html +++ b/docs/second-edition/ch07-03-importing-names-with-use.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    異なるモジュールの名前を参照する

    +

    異なるモジュールの名前を参照する

    @@ -170,7 +170,7 @@

    The Rust Programming Language

    見てお分かりの通り、フルパス指定した名前を参照すると非常に長ったらしくなります。 幸い、Rustには、これらの呼び出しをもっと簡潔にするキーワードが用意されています。

    -

    useキーワードで名前をスコープに導入する

    +

    useキーワードで名前をスコープに導入する

    @@ -247,7 +247,7 @@

    The Rust Programming Language

    Greenuse文に含んでいないので、まだGreenバリアント用にTrafficLight名前空間を指定しています。

    -

    Globで全ての名前をスコープに導入する

    +

    Globで全ての名前をスコープに導入する

    @@ -275,7 +275,7 @@

    The Rust Programming Language

    あまりglobは使用するべきではありません: 便利ではありますが、globは予想以上の要素を引き込んで、 名前衝突を引き起こす可能性があるのです。

    -

    superを使用して親モジュールにアクセスする

    +

    superを使用して親モジュールにアクセスする

    @@ -415,7 +415,7 @@

    The Rust Programming Language

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch08-00-common-collections.html b/docs/second-edition/ch08-00-common-collections.html index 7f08aad..463f8af 100644 --- a/docs/second-edition/ch08-00-common-collections.html +++ b/docs/second-edition/ch08-00-common-collections.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    一般的なコレクション

    +

    一般的なコレクション

    diff --git a/docs/second-edition/ch08-01-vectors.html b/docs/second-edition/ch08-01-vectors.html index e96a415..79ef842 100644 --- a/docs/second-edition/ch08-01-vectors.html +++ b/docs/second-edition/ch08-01-vectors.html @@ -143,7 +143,7 @@

    The Rust Programming Language

    -

    ベクタで一連の値を保持する

    +

    ベクタで一連の値を保持する

    @@ -154,7 +154,7 @@

    The Rust Programming Language

    ベクタには、同じ型の値しか保持できません。要素のリストがある場合に有用です。 例えば、テキストファイルの各行とか、ショッピングカートのアイテムの価格などです。

    -

    新しいベクタを生成する

    +

    新しいベクタを生成する

    新しい空のベクタを作るには、リスト8-1に示されたように、Vec::new関数を呼べばよいです。

    @@ -204,7 +204,7 @@

    The Rust Programming Language

    初期値のi32値を与えたので、コンパイラは、vの型がVec<i32>であると推論でき、型注釈は必要なくなりました。 次は、ベクタを変更する方法を見ましょう。

    -

    ベクタを更新する

    +

    ベクタを更新する

    ベクタを生成し、それから要素を追加するには、リスト8-3に示したように、pushメソッドを使用できます。

    @@ -229,7 +229,7 @@

    The Rust Programming Language

    中に配置する数値は全てi32型であり、コンパイラはこのことをデータから推論するので、 Vec<i32>という注釈は必要なくなります。

    -

    ベクタをドロップすれば、要素もドロップする

    +

    ベクタをドロップすれば、要素もドロップする

    他のあらゆる構造体同様、ベクタもスコープを抜ければ、解放されます。リスト8-4に注釈したようにですね。

    @@ -260,7 +260,7 @@

    The Rust Programming Language

    片付けられるということです。これは一見単純な点に見えるかもしれませんが、ベクタの要素への参照を導入した途端、 もうちょっと複雑になる可能性を秘めています。次は、それに挑んでいきましょう!

    -

    ベクタの要素を読む

    +

    ベクタの要素を読む

    @@ -395,7 +395,7 @@

    The Rust Programming Language

    訳注: 日本語版のThe Rustonomiconはこちらです。

    -

    ベクタの値を走査する

    +

    ベクタの値を走査する

    @@ -435,7 +435,7 @@

    The Rust Programming Language

    可変参照が参照している値を変更するには、+=演算子を使用する前に、 参照外し演算子(*)を使用してiの値に辿り着かないといけません。

    -

    Enumを使って複数の型を保持する

    +

    Enumを使って複数の型を保持する

    diff --git a/docs/second-edition/ch08-02-strings.html b/docs/second-edition/ch08-02-strings.html index 48ab237..4bc338f 100644 --- a/docs/second-edition/ch08-02-strings.html +++ b/docs/second-edition/ch08-02-strings.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    文字列でUTF-8でエンコードされたテキストを保持する

    +

    文字列でUTF-8でエンコードされたテキストを保持する

    @@ -166,7 +166,7 @@

    The Rust Programming Language

    また、Stringが他のコレクションと異なる点についても議論します。具体的には、人間とコンピュータがStringデータを解釈する方法の差異により、 Stringに添え字アクセスする方法がどう複雑なのかということです。

    -

    文字列とは?

    +

    文字列とは?

    @@ -203,7 +203,7 @@

    The Rust Programming Language

    メモリ上の表現が異なったりします。この章では、これらの他の種類の文字列については議論しません; 使用方法やどれが最適かについては、APIドキュメントを参照してください。

    -

    新規文字列を生成する

    +

    新規文字列を生成する

    @@ -285,14 +285,14 @@

    The Rust Programming Language

    これらは全て、有効なStringの値です。

    -

    文字列を更新する

    +

    文字列を更新する

    Stringは、サイズを伸ばすことができ、Vec<T>の中身のように、追加のデータをプッシュすれば、中身も変化します。 付け加えると、String値を連結する+演算子や、format!マクロを便利に使用することができます。

    -

    push_strpushで文字列に追加する

    +

    push_strpushで文字列に追加する

    push_strメソッドで文字列スライスを追記することで、Stringを伸ばすことができます。 @@ -349,7 +349,7 @@

    The Rust Programming Language

    日本語のwwwみたいなものですね。

    -

    +演算子、またはformat!マクロで連結

    +

    +演算子、またはformat!マクロで連結

    2つのすでにある文字列を組み合わせたくなることがよくあります。リスト8-18に示したように、 @@ -456,7 +456,7 @@

    The Rust Programming Language

    出力をスクリーンに行う代わりに、中身をStringで返すのです。format!を使用したコードの方がはるかに読みやすく、 引数の所有権を奪いません。

    -

    文字列に添え字アクセスする

    +

    文字列に添え字アクセスする

    @@ -487,7 +487,7 @@

    The Rust Programming Language

    エラーと注釈が全てを物語っています: Rustの文字列は、添え字アクセスをサポートしていないのです。 でも、なぜでしょうか?その疑問に答えるには、Rustがメモリにどのように文字列を保持しているかについて議論する必要があります。

    -

    内部表現

    +

    内部表現

    StringVec<u8>のラッパです。リスト8-14から適切にUTF-8でエンコードされた文字列の例をご覧ください。 @@ -541,7 +541,7 @@

    The Rust Programming Language

    予期しない値を返し、すぐには判明しないバグを引き起こさないために、Rustはこのコードを全くコンパイルせず、 開発過程の早い段階で誤解を防いでくれるのです。

    -

    バイトとスカラー値と書記素クラスタ!なんてこった!

    +

    バイトとスカラー値と書記素クラスタ!なんてこった!

    @@ -584,7 +584,7 @@

    The Rust Programming Language

    しかし、Stringでそのパフォーマンスを保証することはできません。というのも、 合法な文字がいくつあるか決定するのに、最初から添え字まで中身を走査する必要があるからです。

    -

    文字列をスライスする

    +

    文字列をスライスする

    @@ -620,7 +620,7 @@

    The Rust Programming Language

    範囲を使用して文字列スライスを作る際にはプログラムをクラッシュさせることがあるので、気をつけるべきです。

    -

    文字列を走査するメソッド群

    +

    文字列を走査するメソッド群

    幸いなことに、他の方法でも文字列の要素にアクセスすることができます。

    @@ -673,7 +673,7 @@

    The Rust Programming Language

    書記素クラスタを文字列から得る方法は複雑なので、この機能は標準ライブラリでは提供されていません。 この機能が必要なら、crates.ioでクレートを入手可能です。

    -

    文字列はそう単純じゃない

    +

    文字列はそう単純じゃない

    diff --git a/docs/second-edition/ch08-03-hash-maps.html b/docs/second-edition/ch08-03-hash-maps.html index 6ed84ad..0b1c792 100644 --- a/docs/second-edition/ch08-03-hash-maps.html +++ b/docs/second-edition/ch08-03-hash-maps.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    ハッシュマップに値に紐づいたキーを格納する

    +

    ハッシュマップに値に紐づいたキーを格納する

    @@ -168,7 +168,7 @@

    The Rust Programming Language

    HashMap<K, V>上に定義された関数に隠されています。いつものように、 もっと情報が欲しければ、標準ライブラリのドキュメントをチェックしてください。

    -

    新規ハッシュマップを生成する

    +

    新規ハッシュマップを生成する

    @@ -237,7 +237,7 @@

    The Rust Programming Language

    コンパイラは指定しない限り、どれを所望なのかわからないからです。ところが、キーと値の型引数については、 アンダースコアを使用しており、コンパイラはベクタのデータ型に基づいてハッシュマップが含む型を推論することができるのです。

    -

    ハッシュマップと所有権

    +

    ハッシュマップと所有権

    @@ -274,7 +274,7 @@

    The Rust Programming Language

    最低でもハッシュマップが有効な間は、有効でなければなりません。これらの問題について詳細には、 第10章の「ライフタイムで参照を有効化する」節で語ります。

    -

    ハッシュマップの値にアクセスする

    +

    ハッシュマップの値にアクセスする

    リスト8-23に示したように、キーをgetメソッドに提供することで、ハッシュマップから値を取り出すことができます。

    @@ -325,7 +325,7 @@

    The Rust Programming Language

    Blue: 10
    -

    ハッシュマップを更新する

    +

    ハッシュマップを更新する

    @@ -340,7 +340,7 @@

    The Rust Programming Language

    新しい値を無視し、キーにまだ値がない場合に新しい値を追加するだけにすることもできます。 あるいは、古い値と新しい値を組み合わせることもできます。各方法について見ていきましょう!

    -

    値を上書きする

    +

    値を上書きする

    @@ -368,7 +368,7 @@

    The Rust Programming Language

    このコードは、{"Blue": 25}と出力するでしょう。10という元の値は上書きされたのです。

    -

    キーに値がなかった時のみ値を挿入する

    +

    キーに値がなかった時のみ値を挿入する

    @@ -415,7 +415,7 @@

    The Rust Programming Language

    最初のentry呼び出しは、まだイエローチームに対する値がないので、値50でイエローチームのキーを挿入します。 entryの2回目の呼び出しはハッシュマップを変更しません。なぜなら、ブルーチームには既に10という値があるからです。

    -

    古い値に基づいて値を更新する

    +

    古い値に基づいて値を更新する

    @@ -457,7 +457,7 @@

    The Rust Programming Language

    まずアスタリスク(*)でcountを参照外ししなければならないのです。この可変参照は、 forループの終端でスコープを抜けるので、これらの変更は全て安全であり、借用規則により許可されるのです。

    -

    ハッシュ関数

    +

    ハッシュ関数

    @@ -477,7 +477,7 @@

    The Rust Programming Language

    必ずしも独自のhasherを1から作り上げる必要はありません; crates.ioには、 他のRustユーザによって共有された多くの一般的なハッシュアルゴリズムを実装したhasherを提供するライブラリがあります。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch09-00-error-handling.html b/docs/second-edition/ch09-00-error-handling.html index 4734b85..36be930 100644 --- a/docs/second-edition/ch09-00-error-handling.html +++ b/docs/second-edition/ch09-00-error-handling.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    エラー処理

    +

    エラー処理

    diff --git a/docs/second-edition/ch09-01-unrecoverable-errors-with-panic.html b/docs/second-edition/ch09-01-unrecoverable-errors-with-panic.html index 355f4bd..2d1e86a 100644 --- a/docs/second-edition/ch09-01-unrecoverable-errors-with-panic.html +++ b/docs/second-edition/ch09-01-unrecoverable-errors-with-panic.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    panic!で回復不能なエラー

    +

    panic!で回復不能なエラー

    @@ -170,7 +170,7 @@

    The Rust Programming Language

    -

    パニックに対してスタックを巻き戻すか異常終了するか

    +

    パニックに対してスタックを巻き戻すか異常終了するか

    標準では、パニックが発生すると、プログラムは巻き戻しを始めます。つまり、言語がスタックを遡り、 遭遇した各関数のデータを片付けるということです。しかし、この遡りと片付けはすべきことが多くなります。 対立案は、即座に異常終了し、片付けをせずにプログラムを終了させることです。そうなると、プログラムが使用していたメモリは、 @@ -221,7 +221,7 @@

    The Rust Programming Language

    panic!マクロが呼び出されている他人のコードになるでしょう。panic!呼び出しの発生元である関数のバックトレースを使用して、 問題を起こしている自分のコードの箇所を割り出すことができます。バックトレースがどんなものか、次に議論しましょう。

    -

    panic!バックトレースを使用する

    +

    panic!バックトレースを使用する

    diff --git a/docs/second-edition/ch09-02-recoverable-errors-with-result.html b/docs/second-edition/ch09-02-recoverable-errors-with-result.html index 8970dda..4fb7dd2 100644 --- a/docs/second-edition/ch09-02-recoverable-errors-with-result.html +++ b/docs/second-edition/ch09-02-recoverable-errors-with-result.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Resultで回復可能なエラー

    +

    Resultで回復可能なエラー

    @@ -295,7 +295,7 @@

    The Rust Programming Language

    通常通り、この出力は、一体何がおかしくなったのかを物語っています。

    -

    色々なエラーにマッチする

    +

    色々なエラーにマッチする

    @@ -384,7 +384,7 @@

    The Rust Programming Language

    内部にもmatch式を追加する必要があるのです。ファイルが開けないなら、異なるエラーメッセージが出力されるでしょう。 外側のmatchの最後のアームは同じままなので、ファイルが存在しないエラー以外ならプログラムはパニックします。

    -

    エラー時にパニックするショートカット: unwrapexpect

    +

    エラー時にパニックするショートカット: unwrapexpect

    @@ -451,7 +451,7 @@

    The Rust Programming Language

    ズバリどのunwrapがパニックを引き起こしているのか理解するのは、より時間がかかる可能性があります。 パニックするunwrap呼び出しは全て、同じメッセージを出力するからです。

    -

    エラーを委譲する

    +

    エラーを委譲する

    @@ -559,7 +559,7 @@

    The Rust Programming Language

    Rustにおいて、この種のエラー委譲は非常に一般的なので、Rustにはこれをしやすくする?演算子が用意されています。

    -

    エラー委譲のショートカット: ?演算子

    +

    エラー委譲のショートカット: ?演算子

    @@ -658,7 +658,7 @@

    The Rust Programming Language

    エラーを返すというよりもそれでも、sにユーザ名を含むOk値を返します。機能もまたリスト9-6及び、9-7と同じです; ただ単に異なるバージョンのよりエルゴノミックな書き方なのです。

    -

    ?演算子は、Resultを返す関数でしか使用できない

    +

    ?演算子は、Resultを返す関数でしか使用できない

    diff --git a/docs/second-edition/ch09-03-to-panic-or-not-to-panic.html b/docs/second-edition/ch09-03-to-panic-or-not-to-panic.html index 91f4601..23c834d 100644 --- a/docs/second-edition/ch09-03-to-panic-or-not-to-panic.html +++ b/docs/second-edition/ch09-03-to-panic-or-not-to-panic.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    panic!すべきかするまいか

    +

    panic!すべきかするまいか

    @@ -172,7 +172,7 @@

    The Rust Programming Language

    それからコンパイラではありえない失敗だと気づけなくとも、人間なら気づける場面を議論しましょう。 そして、ライブラリコードでパニックするか決定する方法についての一般的なガイドラインで結論づけましょう。

    -

    例、プロトタイプコード、テスト

    +

    例、プロトタイプコード、テスト

    @@ -195,7 +195,7 @@

    The Rust Programming Language

    テスト全体が失敗してほしいでしょう。panic!が、テストが失敗と印づけられる手段なので、 unwrapexpectの呼び出しはスバリ起こるべきことです。

    -

    コンパイラよりもプログラマがより情報を持っている場合

    +

    コンパイラよりもプログラマがより情報を持っている場合

    @@ -234,7 +234,7 @@

    The Rust Programming Language

    プログラムにハードコードされるのではなく、IPアドレス文字列がユーザ起源でそれ故に確かに失敗する可能性がある場合、 Resultをもっと頑健な方法で処理したほうが絶対にいいでしょう。

    -

    エラー処理のガイドライン

    +

    エラー処理のガイドライン

    @@ -318,7 +318,7 @@

    The Rust Programming Language

    コンパイルが通りもしませんので、その場合を実行時に検査する必要はないわけです。 別の例は、u32のような符号なし整数を使うことであり、この場合、引数は負には絶対にならないことが確認されます。

    -

    検証のために独自の型を作る

    +

    検証のために独自の型を作る

    @@ -458,7 +458,7 @@

    The Rust Programming Language

    そうしたら、引数を一つ持つか、1から100の範囲の数値のみを返す関数は、シグニチャでu32ではなく、 Guessを取るか返し、本体内で追加の確認を行う必要はなくなると宣言できるでしょう。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch10-00-generics.html b/docs/second-edition/ch10-00-generics.html index fbcc4c2..754b33f 100644 --- a/docs/second-edition/ch10-00-generics.html +++ b/docs/second-edition/ch10-00-generics.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    ジェネリック型、トレイト、ライフタイム

    +

    ジェネリック型、トレイト、ライフタイム

    @@ -180,7 +180,7 @@

    The Rust Programming Language

    最後に、ライフタイムを議論します。ライフタイムとは、コンパイラに参照がお互いにどう関係しているかの情報を与える一種のジェネリクスです。 ライフタイムのおかげでコンパイラに参照が有効であることを確認してもらうことを可能にしつつ、多くの場面で値を借用できます。

    -

    関数を抽出することで重複を取り除く

    +

    関数を抽出することで重複を取り除く

    diff --git a/docs/second-edition/ch10-01-syntax.html b/docs/second-edition/ch10-01-syntax.html index 6116e70..ad9b8b0 100644 --- a/docs/second-edition/ch10-01-syntax.html +++ b/docs/second-edition/ch10-01-syntax.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    ジェネリックなデータ型

    +

    ジェネリックなデータ型

    @@ -152,7 +152,7 @@

    The Rust Programming Language

    ジェネリクスで関数、構造体、enum、メソッドを定義する方法を見ましょう。それから、 ジェネリクスがコードのパフォーマンスに与える影響を議論します。

    -

    関数定義では

    +

    関数定義では

    @@ -304,7 +304,7 @@

    The Rust Programming Language

    ジェネリックな型が特定のトレイトを持つと指定する方法は「トレイト境界」節で習うでしょうが、 先にジェネリックな型引数を使用する他の方法を探究しましょう。

    -

    構造体定義では

    +

    構造体定義では

    @@ -401,7 +401,7 @@

    The Rust Programming Language

    数個以上使用すると、コードが読みづらくなります。コードで多くのジェネリックな型が必要な時は、 コードの小分けが必要なサインかもしれません。

    -

    enum定義では

    +

    enum定義では

    @@ -455,7 +455,7 @@

    The Rust Programming Language

    自分のコード内で、保持している値の型のみが異なる構造体やenum定義の場面を認識したら、 代わりにジェネリックな型を使用することで重複を避けることができます。

    -

    メソッド定義では

    +

    メソッド定義では

    @@ -585,7 +585,7 @@

    The Rust Programming Language

    ここで、ジェネリックな引数TUimplの後に宣言されています。構造体定義にはまるからです。 ジェネリックな引数VWfn mixupの後に宣言されています。何故なら、このメソッドにしか関係ないからです。

    -

    ジェネリクスを使用したコードのパフォーマンス

    +

    ジェネリクスを使用したコードのパフォーマンス

    diff --git a/docs/second-edition/ch10-02-traits.html b/docs/second-edition/ch10-02-traits.html index 26cf579..80c9bb0 100644 --- a/docs/second-edition/ch10-02-traits.html +++ b/docs/second-edition/ch10-02-traits.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    トレイト: 共通の振る舞いを定義する

    +

    トレイト: 共通の振る舞いを定義する

    @@ -156,7 +156,7 @@

    The Rust Programming Language

    注釈: 違いはあるものの、トレイトは他の言語でよくインターフェイスと呼ばれる機能に類似しています。

    -

    トレイトを定義する

    +

    トレイトを定義する

    @@ -211,7 +211,7 @@

    The Rust Programming Language

    トレイトには、本体に複数のメソッドを含むことができます: メソッドシグニチャは行ごとに列挙され、 各行はセミコロンで終止します。

    -

    トレイトを型に実装する

    +

    トレイトを型に実装する

    @@ -335,7 +335,7 @@

    The Rust Programming Language

    その逆が起きないことを保証してくれます。この規則がなければ、2つのクレートが同じ型に対して同じトレイトを実装できてしまい、 コンパイラはどちらの実装を使うべきかわからなくなってしまうでしょう。

    -

    デフォルト実装

    +

    デフォルト実装

    @@ -455,7 +455,7 @@

    The Rust Programming Language

    同じメソッドのオーバーライドした実装からは、デフォルト実装を呼び出すことができないことに注意してください。

    -

    トレイト境界

    +

    トレイト境界

    @@ -517,7 +517,7 @@

    The Rust Programming Language

    この関数シグニチャは、多くのトレイト境界のない関数のように、関数名、引数リスト、戻り値の型が一緒になって近いという点でごちゃごちゃしていません。

    -

    トレイト境界でlargest関数を修正する

    +

    トレイト境界でlargest関数を修正する

    @@ -647,7 +647,7 @@

    The Rust Programming Language

    CloneCopyトレイト境界は必要なくなり、ヒープ確保も避けられるでしょう。 試しにこれらの対立的な解決策もご自身で実装してみてください!

    -

    トレイト境界を使用して、メソッド実装を条件分けする

    +

    トレイト境界を使用して、メソッド実装を条件分けする

    diff --git a/docs/second-edition/ch10-03-lifetime-syntax.html b/docs/second-edition/ch10-03-lifetime-syntax.html index 2c8eb88..43fb538 100644 --- a/docs/second-edition/ch10-03-lifetime-syntax.html +++ b/docs/second-edition/ch10-03-lifetime-syntax.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    ライフタイムで参照を検証する

    +

    ライフタイムで参照を検証する

    @@ -168,7 +168,7 @@

    The Rust Programming Language

    ライフタイム記法と遭遇する可能性のある一般的な手段を議論するので、その概念に馴染めます。 もっと詳しく知るには、第19章の「高度なライフタイム」節を参照されたし。

    -

    ライフタイムでダングリング参照を回避する

    +

    ライフタイムでダングリング参照を回避する

    @@ -240,7 +240,7 @@

    The Rust Programming Language

    rで行おうとするいかなることもちゃんと動作しないでしょう。では、どうやってコンパイラはこのコードが無効であると決定しているのでしょうか? 借用チェッカーを使用しています。

    -

    借用精査機

    +

    借用精査機

    @@ -300,7 +300,7 @@

    The Rust Programming Language

    今や、参照のライフタイムがどれだけあり、コンパイラがライフタイムを解析して参照が常に有効であることを保証する仕組みがわかったので、 関数の文脈でジェネリックな引数と戻り値のライフタイムを探究しましょう。

    -

    関数のジェネリックなライフタイム

    +

    関数のジェネリックなライフタイム

    @@ -392,7 +392,7 @@

    The Rust Programming Language

    借用チェッカーもこれを決定することはできません。xyのライフタイムがどう戻り値のライフタイムと関係するかわからないからです。 このエラーを修正するには、借用チェッカーが解析を実行できるように、参照間の関係を定義するジェネリックなライフタイム引数を追加します。

    -

    ライフタイム注釈記法

    +

    ライフタイム注釈記法

    @@ -437,7 +437,7 @@

    The Rust Programming Language

    この関数にはさらに、'aのライフタイム付きのi32への別の参照となるsecondという別の引数もあります。 ライフタイム注釈は、firstsecondの参照がどちらもジェネリックなライフタイムと同じだけ生きることを示唆します。

    -

    関数シグニチャにおけるライフタイム注釈

    +

    関数シグニチャにおけるライフタイム注釈

    @@ -613,7 +613,7 @@

    The Rust Programming Language

    試しに値や、longest関数に渡される参照のライフタイムや、返される参照の使用法が異なる実験をもっとしてみてください。 自分の実験がコンパイル前に借用チェッカーを通るかどうか仮説を立ててください; そして、正しいか確かめてください!

    -

    ライフタイムの観点で思考する

    +

    ライフタイムの観点で思考する

    @@ -699,7 +699,7 @@

    The Rust Programming Language

    一旦、繋がりができたら、メモリ安全な処理を許可するのに十分な情報がコンパイラにはあり、 ダングリングポインタを生成するであろう処理を不認可し、さもなくばメモリ安全性を侵害するのです。

    -

    構造体定義のライフタイム注釈

    +

    構造体定義のライフタイム注釈

    @@ -746,7 +746,7 @@

    The Rust Programming Language

    加えて、ImportantExcerptがスコープを抜けるまでnovelはスコープを抜けないので、 ImportantExcerptインスタンスの参照は有効なのです。

    -

    ライフタイム省略

    +

    ライフタイム省略

    @@ -906,7 +906,7 @@

    The Rust Programming Language

    3番目の規則は本当にメソッドシグニチャでしか適用されないので、次にその文脈でライフタイムを観察し、 3番目の規則が、メソッドシグニチャであまり頻繁にライフタイムを注釈しなくても済むことを意味する理由を確認します。

    -

    メソッド定義におけるライフタイム注釈

    +

    メソッド定義におけるライフタイム注釈

    @@ -973,7 +973,7 @@

    The Rust Programming Language

    引数の1つが&selfなので、戻り値型は&selfのライフタイムを得て、 全てのライフタイムが説明されました。

    -

    静的ライフタイム

    +

    静的ライフタイム

    @@ -1003,7 +1003,7 @@

    The Rust Programming Language

    ほとんどの場合、問題は、ダングリング参照を生成しようとしているか、利用可能なライフタイムの不一致が原因です。 そのような場合、解決策はその問題を修正することであり、'staticライフタイムを指定することではありません。

    -

    ジェネリックな型引数、トレイト境界、ライフタイムを一度に

    +

    ジェネリックな型引数、トレイト境界、ライフタイムを一度に

    ジェネリックな型引数、トレイト境界、ライフタイムを指定する記法を全て1関数でちょっと眺めましょう!

    @@ -1039,7 +1039,7 @@

    The Rust Programming Language

    Displayトレイト境界が必要なのです。ライフタイムは一種のジェネリックなので、 ライフタイム引数'aとジェネリックな型引数Tが関数名の後、山カッコ内の同じリストに収まっています。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch11-00-testing.html b/docs/second-edition/ch11-00-testing.html index 65d8f3e..7740f0d 100644 --- a/docs/second-edition/ch11-00-testing.html +++ b/docs/second-edition/ch11-00-testing.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    自動テストを書く

    +

    自動テストを書く

    diff --git a/docs/second-edition/ch11-01-writing-tests.html b/docs/second-edition/ch11-01-writing-tests.html index 7456ad6..b60907a 100644 --- a/docs/second-edition/ch11-01-writing-tests.html +++ b/docs/second-edition/ch11-01-writing-tests.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    テストの記述法

    +

    テストの記述法

    @@ -162,7 +162,7 @@

    The Rust Programming Language

    Rustが、特にこれらの動作を行うテストを書くために用意している機能を見ていきましょう。 これには、test属性、いくつかのマクロ、should_panic属性が含まれます。

    -

    テスト関数の解剖

    +

    テスト関数の解剖

    @@ -387,7 +387,7 @@

    The Rust Programming Language

    異なる筋書きでのテスト結果がどんな風になるか見てきたので、テストを行う際に有用になるpanic!以外のマクロに目を向けましょう。

    -

    assert!マクロで結果を確認する

    +

    assert!マクロで結果を確認する

    @@ -552,7 +552,7 @@

    The Rust Programming Language

    テストによりバグが捕捉されました!larger.lengthが8、smaller.lengthが5なので、 can_hold内の長さの比較が今はfalseを返すようになったのです: 8は5より小さくないですからね。

    -

    assert_eq!assert_ne!マクロで等値性をテストする

    +

    assert_eq!assert_ne!マクロで等値性をテストする

    @@ -691,7 +691,7 @@

    The Rust Programming Language

    これは通常、構造体やenum定義に#[derive(PartialEq, Debug)]という注釈を追加するくらい単純になります。 これらや他の導出可能なトレイトに関する詳細については、付録Cをご覧ください。

    -

    カスタムの失敗メッセージを追加する

    +

    カスタムの失敗メッセージを追加する

    @@ -793,7 +793,7 @@

    The Rust Programming Language

    実際に得られた値がテスト出力に見られ、起こると想定していたものではなく、 起こったものをデバッグするのに役に立ちます。

    -

    should_panicでパニックを確認する

    +

    should_panicでパニックを確認する

    diff --git a/docs/second-edition/ch11-02-running-tests.html b/docs/second-edition/ch11-02-running-tests.html index 2930ae2..cdf8f36 100644 --- a/docs/second-edition/ch11-02-running-tests.html +++ b/docs/second-edition/ch11-02-running-tests.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    テストの実行され方を制御する

    +

    テストの実行され方を制御する

    @@ -167,7 +167,7 @@

    The Rust Programming Language

    それからテストバイナリにかかる引数を列挙します。cargo test --helpを走らせると、cargo testで使用できるオプションが表示され、 cargo test -- --helpを走らせると、--という区分記号の後に使えるオプションが表示されます。

    -

    テストを並行または連続して実行する

    +

    テストを並行または連続して実行する

    @@ -212,7 +212,7 @@

    The Rust Programming Language

    1スレッドのみを使用してテストを実行すると、並行に実行するより時間がかかりますが、 状態を共有していても、お互いに邪魔をすることはありません。

    -

    関数の出力を表示する

    +

    関数の出力を表示する

    @@ -318,7 +318,7 @@

    The Rust Programming Language

    -test-threads=1オプションと--nocaptureフラグを使ってみて、 その時、出力がどうなるか確かめてください!

    -

    名前でテストの一部を実行する

    +

    名前でテストの一部を実行する

    @@ -373,7 +373,7 @@

    The Rust Programming Language

    test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out -

    単独のテストを走らせる

    +

    単独のテストを走らせる

    あらゆるテスト関数の名前をcargo testに渡して、そのテストのみを実行することができます:

    $ cargo test one_hundred
    @@ -395,7 +395,7 @@ 

    The Rust Programming Language

    この方法では、複数のテストの名前を指定することはできません; cargo testに与えられた最初の値のみが使われるのです。 ですが、複数のテストを走らせる方法もあります。

    -

    複数のテストを実行するようフィルターをかける

    +

    複数のテストを実行するようフィルターをかける

    @@ -419,7 +419,7 @@

    The Rust Programming Language

    また、テストが出現するモジュールがテスト名の一部になっていて、 モジュール名でフィルターをかけることで、あるモジュール内のテスト全てを実行できることに注目してください。

    -

    特に要望のない限りテストを無視する

    +

    特に要望のない限りテストを無視する

    diff --git a/docs/second-edition/ch11-03-test-organization.html b/docs/second-edition/ch11-03-test-organization.html index 6077f62..74b7f1c 100644 --- a/docs/second-edition/ch11-03-test-organization.html +++ b/docs/second-edition/ch11-03-test-organization.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    テストの体系化

    +

    テストの体系化

    @@ -160,7 +160,7 @@

    The Rust Programming Language

    どちらのテストを書くのも、ライブラリの一部が個別かつ共同でしてほしいことをしていることを確認するのに重要なのです。

    -

    単体テスト

    +

    単体テスト

    @@ -173,7 +173,7 @@

    The Rust Programming Language

    慣習は、各ファイルにtestsという名前のモジュールを作り、テスト関数を含ませ、 そのモジュールをcfg(test)で注釈することです。

    -

    テストモジュールと#[cfg(test)]

    +

    テストモジュールと#[cfg(test)]

    @@ -218,7 +218,7 @@

    The Rust Programming Language

    Cargoがテストコードをコンパイルします。これには、このモジュールに含まれるかもしれないヘルパー関数全ても含まれ、 #[test]で注釈された関数だけにはなりません。

    -

    非公開関数をテストする

    +

    非公開関数をテストする

    @@ -262,7 +262,7 @@

    The Rust Programming Language

    testsモジュールもただのモジュールでしかないので、テスト内でinternal_adderを普通にインポートし呼び出すことができます。 非公開関数はテストするべきではないとお考えなら、Rustにはそれを強制するものは何もありません。

    -

    結合テスト

    +

    結合テスト

    @@ -277,7 +277,7 @@

    The Rust Programming Language

    結合したコードのテストの範囲も同様に重要になるのです。結合テストを作成するには、 まずtestsディレクトリが必要になります。

    -

    testsディレクトリ

    +

    testsディレクトリ

    @@ -383,7 +383,7 @@

    The Rust Programming Language

    このコマンドは、tests/integration_test.rsファイルにあるテストのみを実行します。

    -

    結合テスト内のサブモジュール

    +

    結合テスト内のサブモジュール

    @@ -496,7 +496,7 @@

    The Rust Programming Language

    mod common;という宣言は、リスト7-4で模擬したモジュール宣言と同じであることに注意してください。 それから、テスト関数内でcommon::setup()関数を呼び出すことができます。

    -

    バイナリクレート用の結合テスト

    +

    バイナリクレート用の結合テスト

    @@ -519,7 +519,7 @@

    The Rust Programming Language

    この構造を使用して結合テストは、extern crateを使用して重要な機能を用いることでライブラリクレートをテストすることができます。 この重要な機能が動作すれば、src/main.rsファイルの少量のコードも動作し、その少量のコードはテストする必要がないわけです。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch12-00-an-io-project.html b/docs/second-edition/ch12-00-an-io-project.html index 9a40a30..029eb6d 100644 --- a/docs/second-edition/ch12-00-an-io-project.html +++ b/docs/second-edition/ch12-00-an-io-project.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    入出力プロジェクト: コマンドラインプログラムを構築する

    +

    入出力プロジェクト: コマンドラインプログラムを構築する

    diff --git a/docs/second-edition/ch12-01-accepting-command-line-arguments.html b/docs/second-edition/ch12-01-accepting-command-line-arguments.html index dc93db4..f65c950 100644 --- a/docs/second-edition/ch12-01-accepting-command-line-arguments.html +++ b/docs/second-edition/ch12-01-accepting-command-line-arguments.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    コマンドライン引数を受け付ける

    +

    コマンドライン引数を受け付ける

    @@ -166,7 +166,7 @@

    The Rust Programming Language

    コマンドライン引数を受け付けるプログラムを書く手助けをしてくれるものもありますが、ちょうどこの概念を学んでいる最中なので、 この能力を自分で実装しましょう。

    -

    引数の値を読み取る

    +

    引数の値を読み取る

    @@ -221,7 +221,7 @@

    The Rust Programming Language

    -

    args関数と不正なユニコード

    +

    args関数と不正なユニコード

    引数のどれかが不正なユニコードを含んでいたら、std::env::argsはパニックすることに注意してください。 プログラムが不正なユニコードを含む引数を受け付ける必要があるなら、代わりにstd::env::args_osを使用してください。 この関数は、String値ではなく、OsString値を生成するイテレータを返します。ここでは、 @@ -265,7 +265,7 @@

    The Rust Programming Language

    メッセージで出力したり、プログラムを起動するのに使用されたコマンドラインエイリアスによってプログラムの振る舞いを変えたい場合に、 プログラム名にアクセスするのにしばしば便利です。ですが、この章の目的には、これを無視し、必要な二つの引数のみを保存します。

    -

    引数の値を変数に保存する

    +

    引数の値を変数に保存する

    diff --git a/docs/second-edition/ch12-02-reading-a-file.html b/docs/second-edition/ch12-02-reading-a-file.html index 8f2b887..5b15ac6 100644 --- a/docs/second-edition/ch12-02-reading-a-file.html +++ b/docs/second-edition/ch12-02-reading-a-file.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    ファイルを読み込む

    +

    ファイルを読み込む

    diff --git a/docs/second-edition/ch12-03-improving-error-handling-and-modularity.html b/docs/second-edition/ch12-03-improving-error-handling-and-modularity.html index 0794b0c..ee96961 100644 --- a/docs/second-edition/ch12-03-improving-error-handling-and-modularity.html +++ b/docs/second-edition/ch12-03-improving-error-handling-and-modularity.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    リファクタリングしてモジュール性とエラー処理を向上させる

    +

    リファクタリングしてモジュール性とエラー処理を向上させる

    プログラムを改善するために、プログラムの構造と起こりうるエラーに対処する方法に関連する4つの問題を修正していきましょう。

    @@ -196,7 +196,7 @@

    The Rust Programming Language

    プロジェクトをリファクタリングして、これら4つの問題を扱いましょう。

    -

    バイナリプロジェクトの責任の分離

    +

    バイナリプロジェクトの責任の分離

    @@ -241,7 +241,7 @@

    The Rust Programming Language

    main.rsに残る唯一のコードは、読めばその正当性が評価できるだけ小規模になるでしょう。 この工程に従って、プログラムのやり直しをしましょう。

    -

    引数解析器を抽出する

    +

    引数解析器を抽出する

    @@ -291,7 +291,7 @@

    The Rust Programming Language

    少しずつ段階的にリファクタリングしているのです。この変更後、プログラムを再度実行して、 引数解析がまだ動作していることを実証してください。問題が発生した時に原因を特定する助けにするために頻繁に進捗を確認するのはいいことです。

    -

    設定値をまとめる

    +

    設定値をまとめる

    @@ -391,7 +391,7 @@

    The Rust Programming Language

    -

    cloneを使用する代償

    +

    cloneを使用する代償

    実行時コストのためにcloneを使用して所有権問題を解消するのを避ける傾向が多くのRustaceanにあります。 第13章で、この種の状況においてより効率的なメソッドの使用法を学ぶでしょう。ですがとりあえずは、 これらのコピーをするのは1回だけですし、ファイル名とクエリ文字列は非常に小さなものなので、 @@ -412,7 +412,7 @@

    The Rust Programming Language

    これでコードはqueryfilenameが関連していることと、その目的がプログラムの振る舞い方を設定するということをより明確に伝えます。 これらの値を使用するあらゆるコードは、configインスタンスの目的の名前を冠したフィールドにそれらを発見することを把握しています。

    -

    Configのコンストラクタを作成する

    +

    Configのコンストラクタを作成する

    @@ -476,7 +476,7 @@

    The Rust Programming Language

    parse_configの名前をnewに変え、implブロックに入れ込んだので、new関数とConfigが紐づくようになりました。 再度このコードをコンパイルしてみて、動作することを確かめてください。

    -

    エラー処理を修正する

    +

    エラー処理を修正する

    @@ -498,7 +498,7 @@

    The Rust Programming Language

    境界外アクセス: 長さは1なのに添え字も1ですという行は、プログラマ向けのエラーメッセージです。 エンドユーザが起きたことと代わりにすべきことを理解する手助けにはならないでしょう。これを今修正しましょう。

    -

    エラーメッセージを改善する

    +

    エラーメッセージを改善する

    @@ -553,7 +553,7 @@

    The Rust Programming Language

    panic!の呼び出しは、第9章で議論したように、使用の問題よりもプログラミング上の問題により適しています。 代わりに、第9章で学んだもう一つのテクニックを使用することができます。成功か失敗かを示唆するResultを返すことです。

    -

    panic!を呼び出す代わりにnewからResultを返す

    +

    panic!を呼び出す代わりにnewからResultを返す

    @@ -607,7 +607,7 @@

    The Rust Programming Language

    Config::newからErr値を返すことにより、main関数は、new関数から返ってくるResult値を処理し、 エラー時により綺麗にプロセスから抜け出すことができます。

    -

    Config::newを呼び出し、エラーを処理する

    +

    Config::newを呼び出し、エラーを処理する

    @@ -678,7 +678,7 @@

    The Rust Programming Language

    素晴らしい!この出力の方が遥かにユーザに優しいです。

    -

    mainからロジックを抽出する

    +

    mainからロジックを抽出する

    @@ -727,7 +727,7 @@

    The Rust Programming Language

    これでrun関数は、ファイル読み込みから始まるmain関数の残りのロジック全てを含むようになりました。 このrun関数は、引数にConfigインスタンスを取ります。

    -

    run関数からエラーを返す

    +

    run関数からエラーを返す

    @@ -816,7 +816,7 @@

    The Rust Programming Language

    コンパイラは、ここにエラー処理コードを書くつもりだったんじゃないかと思い出させてくれています! 今、その問題を改修しましょう。

    -

    mainrunから返ってきたエラーを処理する

    +

    mainrunから返ってきたエラーを処理する

    リスト12-10のConfig::newに対して行った方法に似たテクニックを使用してエラーを確認し、扱いますが、 @@ -850,7 +850,7 @@

    The Rust Programming Language

    if letunwrap_or_else関数の中身はどちらも同じです: エラーを出力して終了します。

    -

    コードをライブラリクレートに分割する

    +

    コードをライブラリクレートに分割する

    diff --git a/docs/second-edition/ch12-04-testing-the-librarys-functionality.html b/docs/second-edition/ch12-04-testing-the-librarys-functionality.html index 5e10deb..9eabf5e 100644 --- a/docs/second-edition/ch12-04-testing-the-librarys-functionality.html +++ b/docs/second-edition/ch12-04-testing-the-librarys-functionality.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    テスト駆動開発でライブラリの機能を開発する

    +

    テスト駆動開発でライブラリの機能を開発する

    @@ -182,7 +182,7 @@

    The Rust Programming Language

    実際にクエリ文字列の検索を行う機能の実装をテスト駆動し、クエリに合致する行のリストを生成します。 この機能をsearchという関数に追加しましょう。

    -

    失敗するテストを記述する

    +

    失敗するテストを記述する

    @@ -338,7 +338,7 @@

    The Rust Programming Language

    素晴らしい。テストは全く想定通りに失敗しています。テストが通るようにしましょう!

    -

    テストを通過させるコードを書く

    +

    テストを通過させるコードを書く

    空のベクタを常に返しているために、現状テストは失敗しています。それを修正し、searchを実装するには、 @@ -358,7 +358,7 @@

    The Rust Programming Language

    各行を繰り返す作業から、この手順に順に取り掛かりましょう。

    -

    linesメソッドで各行を繰り返す

    +

    linesメソッドで各行を繰り返す

    @@ -384,7 +384,7 @@

    The Rust Programming Language

    リスト3-5でこのようなイテレータの使用法は見かけたことを思い出してください。 そこでは、イテレータにforループを使用してコレクションの各要素に対して何らかのコードを走らせていました。

    -

    クエリを求めて各行を検索する

    +

    クエリを求めて各行を検索する

    @@ -407,7 +407,7 @@

    The Rust Programming Language

    リスト12-18: 行がqueryの文字列を含むか確認する機能を追加する

    -

    合致した行を保存する

    +

    合致した行を保存する

    @@ -455,7 +455,7 @@

    The Rust Programming Language

    検索関数のコードは悪すぎるわけではありませんが、イテレータの有用な機能の一部を活用していません。 この例には第13章で再度触れ、そこでは、イテレータをより深く探究し、さらに改善する方法に目を向けます。

    -

    run関数内でsearch関数を使用する

    +

    run関数内でsearch関数を使用する

    diff --git a/docs/second-edition/ch12-05-working-with-environment-variables.html b/docs/second-edition/ch12-05-working-with-environment-variables.html index 055e9c5..3466c55 100644 --- a/docs/second-edition/ch12-05-working-with-environment-variables.html +++ b/docs/second-edition/ch12-05-working-with-environment-variables.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    環境変数を取り扱う

    +

    環境変数を取り扱う

    @@ -154,7 +154,7 @@

    The Rust Programming Language

    代わりに環境変数を使用します。そうすることでユーザは1回環境変数をセットすれば、そのターミナルセッションの間は、 大文字小文字無視の検索を行うことができるようになるわけです。

    -

    大文字小文字を区別しないsearch関数用に失敗するテストを書く

    +

    大文字小文字を区別しないsearch関数用に失敗するテストを書く

    @@ -238,7 +238,7 @@

    The Rust Programming Language

    “Trust me.”という行にもマッチするはずです。これが失敗するテストであり、まだsearch_case_insensitive関数を定義していないので、 コンパイルは失敗するでしょう。リスト12-16のsearch関数で行ったのと同様に空のベクタを常に返すような仮実装を追加し、テストがコンパイルされるものの、失敗する様をご自由に確認してください。

    -

    search_case_insensitive関数を実装する

    +

    search_case_insensitive関数を実装する

    diff --git a/docs/second-edition/ch12-06-writing-to-stderr-instead-of-stdout.html b/docs/second-edition/ch12-06-writing-to-stderr-instead-of-stdout.html index 413c21f..80fe9b1 100644 --- a/docs/second-edition/ch12-06-writing-to-stderr-instead-of-stdout.html +++ b/docs/second-edition/ch12-06-writing-to-stderr-instead-of-stdout.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    標準出力ではなく標準エラーにエラーメッセージを書き込む

    +

    標準出力ではなく標準エラーにエラーメッセージを書き込む

    @@ -157,7 +157,7 @@

    The Rust Programming Language

    println!関数は、標準出力に出力する能力しかないので、標準エラーに出力するには他のものを使用しなければなりません。

    -

    エラーが書き込まれる場所を確認する

    +

    エラーが書き込まれる場所を確認する

    @@ -198,7 +198,7 @@

    The Rust Programming Language

    そうです。エラーメッセージは標準出力に出力されているのです。このようなエラーメッセージは標準エラーに出力され、 成功した状態のデータのみがファイルに残ると遥かに有用です。それを変更します。

    -

    エラーを標準エラーに出力する

    +

    エラーを標準エラーに出力する

    @@ -256,7 +256,7 @@

    The Rust Programming Language

    これは、もう成功した出力には標準出力を、エラー出力には標準エラーを適切に使用していることをデモしています。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch13-00-functional-features.html b/docs/second-edition/ch13-00-functional-features.html index a867ff3..086b9c3 100644 --- a/docs/second-edition/ch13-00-functional-features.html +++ b/docs/second-edition/ch13-00-functional-features.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    関数型言語の機能: イテレータとクロージャ

    +

    関数型言語の機能: イテレータとクロージャ

    diff --git a/docs/second-edition/ch13-01-closures.html b/docs/second-edition/ch13-01-closures.html index 5e2023b..6127afb 100644 --- a/docs/second-edition/ch13-01-closures.html +++ b/docs/second-edition/ch13-01-closures.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    クロージャ: 環境をキャプチャできる匿名関数

    +

    クロージャ: 環境をキャプチャできる匿名関数

    @@ -154,7 +154,7 @@

    The Rust Programming Language

    関数と異なり、呼び出されたスコープの値をクロージャは、キャプチャすることができます。 これらのクロージャの機能がコードの再利用や、動作のカスタマイズを行わせてくれる方法を模擬しましょう。

    -

    クロージャで動作の抽象化を行う

    +

    クロージャで動作の抽象化を行う

    @@ -337,7 +337,7 @@

    The Rust Programming Language

    この関数を現時点で呼んでいるところを切り捨てたくもあります。要するに、結果が必要なければ関数を呼び出したくなく、 それでも1回だけ呼び出したいのです。

    -

    関数でリファクタリング

    +

    関数でリファクタリング

    @@ -400,7 +400,7 @@

    The Rust Programming Language

    プログラムの1箇所でコードを定義したいですが、結果が本当に必要なところでだけコードを実行します。 これは、クロージャのユースケースです!

    -

    クロージャでリファクタリングして、コードを保存する

    +

    クロージャでリファクタリングして、コードを保存する

    @@ -522,7 +522,7 @@

    The Rust Programming Language

    クロージャは他の解決法も用意してくれます。その解決策については、もう少し先で語りましょう。でもまずは、 クロージャ定義に型注釈がない理由とクロージャに関わるトレイトについて話しましょう。

    -

    クロージャの型推論と注釈

    +

    クロージャの型推論と注釈

    @@ -635,7 +635,7 @@

    The Rust Programming Language

    そして、その型がexample_closureのクロージャに閉じ込められ、同じクロージャを異なる型で使用しようとすると、 型エラーが出るのです。

    -

    ジェネリック引数とFnトレイトを使用してクロージャを保存する

    +

    ジェネリック引数とFnトレイトを使用してクロージャを保存する

    @@ -882,7 +882,7 @@

    The Rust Programming Language

    必要以上に重い計算を呼び出さないことを保証するのに必要なロジックの面倒をCacherは見るので、 generate_workoutはビジネスロジックに集中できるのです。

    -

    Cacher実装の限界

    +

    Cacher実装の限界

    @@ -945,7 +945,7 @@

    The Rust Programming Language

    例えば、文字列スライスを取り、usizeを返すクロージャの結果をキャッシュしたくなるかもしれません。 この問題を修正するには、Cacher機能の柔軟性を向上させるためによりジェネリックな引数を導入してみてください。

    -

    クロージャで環境をキャプチャする

    +

    クロージャで環境をキャプチャする

    diff --git a/docs/second-edition/ch13-02-iterators.html b/docs/second-edition/ch13-02-iterators.html index d3b0d04..47486ac 100644 --- a/docs/second-edition/ch13-02-iterators.html +++ b/docs/second-edition/ch13-02-iterators.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    一連の要素をイテレータで処理する

    +

    一連の要素をイテレータで処理する

    @@ -211,7 +211,7 @@

    The Rust Programming Language

    イテレータにより、添え字を使えるデータ構造、ベクタなどだけではなく、多くの異なるシーケンスに対して同じロジックを使う柔軟性も得られます。 イテレータがそれをする方法を調査しましょう。

    -

    Iteratorトレイトとnextメソッド

    +

    Iteratorトレイトとnextメソッド

    全てのイテレータは、標準ライブラリで定義されているIteratorというトレイトを実装しています。 @@ -293,7 +293,7 @@

    The Rust Programming Language

    iterではなくinto_iterを呼び出すことができます。同様に、可変参照を繰り返したいなら、 iterではなくiter_mutを呼び出せます。

    -

    イテレータを消費するメソッド

    +

    イテレータを消費するメソッド

    @@ -337,7 +337,7 @@

    The Rust Programming Language

    sumは呼び出し対象のイテレータの所有権を奪うので、sum呼び出し後にv1_iterを使用することはできません。

    -

    他のイテレータを生成するメソッド

    +

    他のイテレータを生成するメソッド

    @@ -418,7 +418,7 @@

    The Rust Programming Language

    これは、Iteratorトレイトが提供する繰り返し動作を再利用しつつ、 クロージャにより一部の動作をカスタマイズできる好例になっています。

    -

    環境をキャプチャするクロージャを使用する

    +

    環境をキャプチャするクロージャを使用する

    @@ -494,7 +494,7 @@

    The Rust Programming Language

    shoes_in_my_sizeを呼び出した時に、指定した値と同じサイズの靴だけが得られることをテストは示しています。

    -

    Iteratorトレイトで独自のイテレータを作成する

    +

    Iteratorトレイトで独自のイテレータを作成する

    @@ -588,7 +588,7 @@

    The Rust Programming Language

    countの値が5以下なら、nextSomeに包まれた現在の値を返しますが、 countが6以上なら、イテレータはNoneを返します。

    -

    Counterイテレータのnextメソッドを使用する

    +

    Counterイテレータのnextメソッドを使用する

    @@ -641,7 +641,7 @@

    The Rust Programming Language

    それからイテレータにほしい動作が実装し終わっていることを実証しながら、nextを繰り返し呼び出しています: 1から5の値を返すことです。

    -

    他のIteratorトレイトメソッドを使用する

    +

    他のIteratorトレイトメソッドを使用する

    diff --git a/docs/second-edition/ch13-03-improving-our-io-project.html b/docs/second-edition/ch13-03-improving-our-io-project.html index 8531427..131954f 100644 --- a/docs/second-edition/ch13-03-improving-our-io-project.html +++ b/docs/second-edition/ch13-03-improving-our-io-project.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    入出力プロジェクトを改善する

    +

    入出力プロジェクトを改善する

    @@ -152,7 +152,7 @@

    The Rust Programming Language

    このイテレータに関する新しい知識があれば、イテレータを使用してコードのいろんな場所をより明確で簡潔にすることで、 第12章の入出力プロジェクトを改善することができます。イテレータがConfig::new関数とsearch関数の実装を改善する方法に目を向けましょう。

    -

    イテレータを使用してcloneを取り除く

    +

    イテレータを使用してcloneを取り除く

    @@ -208,7 +208,7 @@

    The Rust Programming Language

    ひとたび、Config::newがイテレータの所有権を奪い、借用する添え字アクセス処理をやめたら、 cloneを呼び出して新しくメモリ確保するのではなく、イテレータからのString値をConfigにムーブできます。

    -

    返却されるイテレータを直接使う

    +

    返却されるイテレータを直接使う

    入出力プロジェクトのsrc/main.rsファイルを開いてください。こんな見た目のはずです:

    @@ -276,7 +276,7 @@

    The Rust Programming Language

    std::env::Argsになりました。argsの所有権を奪い、繰り返しを行うことでargsを可変化する予定なので、 args引数の仕様にmutキーワードを追記でき、可変にします。

    -

    添え字の代わりにIteratorトレイトのメソッドを使用する

    +

    添え字の代わりにIteratorトレイトのメソッドを使用する

    @@ -335,7 +335,7 @@

    The Rust Programming Language

    matchを使用してその値を抜き出します。Noneを返したら、十分な引数が与えられなかったということなので、 Err値で早期リターンします。filename値に対しても同じことをします。

    -

    イテレータアダプタでコードをより明確にする

    +

    イテレータアダプタでコードをより明確にする

    入出力プロジェクトのsearch関数でも、イテレータを活用することができます。その関数はリスト12-19に示していますが、以下のリスト13-28に再掲します。

    diff --git a/docs/second-edition/ch13-04-performance.html b/docs/second-edition/ch13-04-performance.html index 10e8d6d..e6cfb10 100644 --- a/docs/second-edition/ch13-04-performance.html +++ b/docs/second-edition/ch13-04-performance.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    パフォーマンス比較: ループVSイテレータ

    +

    パフォーマンス比較: ループVSイテレータ

    @@ -250,7 +250,7 @@

    The Rust Programming Language

    もうイテレータとクロージャを恐れなしに使用することができますね!それらのおかげでコードは、高レベルだけれども、 そうすることに対して実行時のパフォーマンスを犠牲にしないようになります。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch14-00-more-about-cargo.html b/docs/second-edition/ch14-00-more-about-cargo.html index eb963ef..e599d97 100644 --- a/docs/second-edition/ch14-00-more-about-cargo.html +++ b/docs/second-edition/ch14-00-more-about-cargo.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    CargoとCrates.ioについてより詳しく

    +

    CargoとCrates.ioについてより詳しく

    diff --git a/docs/second-edition/ch14-01-release-profiles.html b/docs/second-edition/ch14-01-release-profiles.html index 90a4671..cd790a5 100644 --- a/docs/second-edition/ch14-01-release-profiles.html +++ b/docs/second-edition/ch14-01-release-profiles.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    リリースプロファイルでビルドをカスタマイズする

    +

    リリースプロファイルでビルドをカスタマイズする

    diff --git a/docs/second-edition/ch14-02-publishing-to-crates-io.html b/docs/second-edition/ch14-02-publishing-to-crates-io.html index 874579a..b96df89 100644 --- a/docs/second-edition/ch14-02-publishing-to-crates-io.html +++ b/docs/second-edition/ch14-02-publishing-to-crates-io.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Crates.ioにクレートを公開する

    +

    Crates.ioにクレートを公開する

    @@ -158,7 +158,7 @@

    The Rust Programming Language

    RustとCargoは、公開したパッケージを人が使用し、そもそも見つけやすくしてくれる機能を有しています。 これらの機能の一部を次に語り、そして、パッケージの公開方法を説明します。

    -

    役に立つドキュメンテーションコメントを行う

    +

    役に立つドキュメンテーションコメントを行う

    @@ -222,7 +222,7 @@

    The Rust Programming Language

    図14-1: add_one関数のHTMLドキュメント

    -

    よく使われるセクション

    +

    よく使われるセクション

    @@ -252,7 +252,7 @@

    The Rust Programming Language

    多くのドキュメンテーションコメントでは、これら全てのセクションが必要になることはありませんが、 これは自分のコードを呼び出している人が知りたいと思うコードの方向性を思い出させてくれるいいチェックリストになります。

    -

    テストとしてのドキュメンテーションコメント

    +

    テストとしてのドキュメンテーションコメント

    @@ -279,7 +279,7 @@

    The Rust Programming Language

    さて、例のassert_eq!がパニックするように、関数か例を変更し、再度cargo testを実行したら、 docテストが、例とコードがお互いに同期されていないことを捕捉するところを目撃するでしょう!

    -

    含まれている要素にコメントする

    +

    含まれている要素にコメントする

    @@ -337,7 +337,7 @@

    The Rust Programming Language

    要素内のドキュメンテーションコメントは、特にクレートやモジュールを解説するのに有用です。 コンテナの全体の目的を説明し、クレートの使用者がクレートの体系を理解する手助けをするのに使用してください。

    -

    pub useで便利な公開APIをエクスポートする

    +

    pub useで便利な公開APIをエクスポートする

    @@ -541,7 +541,7 @@

    The Rust Programming Language

    その内部構造をユーザに提示する構造から切り離してくれます。インストールしてある他のクレートを見て、 内部構造が公開APIと異なっているか確認してみてください。

    -

    Crates.ioのアカウントをセットアップする

    +

    Crates.ioのアカウントをセットアップする

    @@ -567,7 +567,7 @@

    The Rust Programming Language

    このトークンは、秘密です: 他人とは共有しないでください。なんらかの理由で他人と実際に共有してしまったら、 古いものを破棄してcrates.ioで新しいトークンを生成するべきです。

    -

    新しいクレートにメタデータを追加する

    +

    新しいクレートにメタデータを追加する

    @@ -667,7 +667,7 @@

    The Rust Programming Language

    Cargoのドキュメンテーションには、 指定して他人が発見し、より容易くクレートを使用できることを保証する他のメタデータが解説されています。

    -

    Crates.ioに公開する

    +

    Crates.ioに公開する

    @@ -702,7 +702,7 @@

    The Rust Programming Language

    おめでとうございます!Rustコミュニティとコードを共有し、誰でもあなたのクレートを依存として簡単に追加できます。

    -

    既存のクレートの新バージョンを公開する

    +

    既存のクレートの新バージョンを公開する

    @@ -713,7 +713,7 @@

    The Rust Programming Language

    セマンティックバージョンルールを使用して加えた変更の種類に基づいて次の適切なバージョン番号を決定してください。 そして、cargo publishを実行し、新バージョンをアップロードします。

    -

    cargo yankでCrates.ioからバージョンを削除する

    +

    cargo yankでCrates.ioからバージョンを削除する

    diff --git a/docs/second-edition/ch14-03-cargo-workspaces.html b/docs/second-edition/ch14-03-cargo-workspaces.html index 480eb2f..a8ae656 100644 --- a/docs/second-edition/ch14-03-cargo-workspaces.html +++ b/docs/second-edition/ch14-03-cargo-workspaces.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Cargoのワークスペース

    +

    Cargoのワークスペース

    @@ -153,7 +153,7 @@

    The Rust Programming Language

    ライブラリクレートの肥大化が続き、その上で複数のライブラリクレートにパッケージを分割したくなることでしょう。 この場面において、Cargoはワークスペースという協調して開発された関連のある複数のパッケージを管理するのに役立つ機能を提供しています。

    -

    ワークスペースを生成する

    +

    ワークスペースを生成する

    @@ -226,7 +226,7 @@

    The Rust Programming Language

    targetディレクトリに生成物がある状態にしなければならないでしょう。一つのtargetディレクトリを共有することで、 クレートは不必要な再ビルドを回避できるのです。

    -

    ワークスペース内に2番目のクレートを作成する

    +

    ワークスペース内に2番目のクレートを作成する

    @@ -327,7 +327,7 @@

    The Rust Programming Language

    これにより、adder/src/main.rsのコードが実行され、これはadd_oneクレートに依存しています。

    -

    ワークスペースの外部クレートに依存する

    +

    ワークスペースの外部クレートに依存する

    @@ -402,7 +402,7 @@

    The Rust Programming Language

    同じバージョンを使っていることを確かめてくれるのです。ワークスペース全体でrandの同じバージョンを使用することにより、 複数のコピーが存在しないのでスペースを節約し、ワークスペースのクレートが相互に互換性を維持することを確かめます。

    -

    ワークスペースにテストを追加する

    +

    ワークスペースにテストを追加する

    さらなる改善として、add_oneクレート内にadd_one::add_one関数のテストを追加しましょう:

    diff --git a/docs/second-edition/ch14-04-installing-binaries.html b/docs/second-edition/ch14-04-installing-binaries.html index a838680..367da50 100644 --- a/docs/second-edition/ch14-04-installing-binaries.html +++ b/docs/second-edition/ch14-04-installing-binaries.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    cargo installでCrates.ioからバイナリをインストールする

    +

    cargo installでCrates.ioからバイナリをインストールする

    diff --git a/docs/second-edition/ch14-05-extending-cargo.html b/docs/second-edition/ch14-05-extending-cargo.html index 1ce4d48..a6fc31e 100644 --- a/docs/second-edition/ch14-05-extending-cargo.html +++ b/docs/second-edition/ch14-05-extending-cargo.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    独自のコマンドでCargoを拡張する

    +

    独自のコマンドでCargoを拡張する

    @@ -155,7 +155,7 @@

    The Rust Programming Language

    cargo --listを実行すると、列挙もされます。cargo installを使用して拡張をインストールし、 それから組み込みのCargoツール同様に実行できることは、Cargoの設計上の非常に便利な恩恵です!

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch15-00-smart-pointers.html b/docs/second-edition/ch15-00-smart-pointers.html index cf4e6e6..c171795 100644 --- a/docs/second-edition/ch15-00-smart-pointers.html +++ b/docs/second-edition/ch15-00-smart-pointers.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    スマートポインタ

    +

    スマートポインタ

    diff --git a/docs/second-edition/ch15-01-box.html b/docs/second-edition/ch15-01-box.html index fb3da75..0eec771 100644 --- a/docs/second-edition/ch15-01-box.html +++ b/docs/second-edition/ch15-01-box.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    ヒープのデータを指すBox<T>を使用する

    +

    ヒープのデータを指すBox<T>を使用する

    @@ -185,7 +185,7 @@

    The Rust Programming Language

    すべてその話題を説明するためだけのものです。 従って、ここで学ぶのと同じことが第17章においても適用するでしょう!

    -

    Box<T>を使ってヒープにデータを格納する

    +

    Box<T>を使ってヒープにデータを格納する

    Box<T>のこのユースケースを議論する前に、Box<T>の記法と、Box<T>内に格納された値を読み書きする方法について講義しましょう。

    @@ -221,7 +221,7 @@

    The Rust Programming Language

    単独のi32のような値は、既定で格納される場所であるスタックに置くことが、大多数の場合にはより適切です。 ボックスがなかったら定義することの叶わない型をボックスが定義させてくれる場合を見ましょう。

    -

    ボックスで再帰的な型を可能にする

    +

    ボックスで再帰的な型を可能にする

    @@ -241,7 +241,7 @@

    The Rust Programming Language

    我々が定義するコンスリストは、再帰を除いて素直です; 故に、これから取り掛かる例の概念は、 再帰的な型が関わるもっと複雑な場面に遭遇したら必ず役に立つでしょう。

    -

    コンスリストについてもっと詳しく

    +

    コンスリストについてもっと詳しく

    @@ -350,7 +350,7 @@

    The Rust Programming Language

    自身の別の値を直接保持しているのです。結果として、コンパイラは、List値を格納するのに必要な領域が計算できないのです。 このエラーが得られた理由を少し噛み砕きましょう。まず、非再帰的な型の値を格納するのに必要な領域をどうコンパイラが決定しているかを見ましょう。

    -

    非再帰的な型のサイズを計算する

    +

    非再帰的な型のサイズを計算する

    第6章でenum定義を議論した時にリスト6-2で定義したMessage enumを思い出してください:

    @@ -394,7 +394,7 @@

    The Rust Programming Language

    図15-1: 無限のCons列挙子からなる無限のList

    -

    Box<T>で既知のサイズの再帰的な型を得る

    +

    Box<T>で既知のサイズの再帰的な型を得る

    diff --git a/docs/second-edition/ch15-02-deref.html b/docs/second-edition/ch15-02-deref.html index 2921711..ec23f54 100644 --- a/docs/second-edition/ch15-02-deref.html +++ b/docs/second-edition/ch15-02-deref.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Derefトレイトでスマートポインタを普通の参照のように扱う

    +

    Derefトレイトでスマートポインタを普通の参照のように扱う

    @@ -163,7 +163,7 @@

    The Rust Programming Language

    Derefトレイトを実装することでスマートポインタが参照と似た方法で動作するようにできる方法を探求します。 そして、Rustの参照外し型強制機能と、それにより参照やスマートポインタと協調できる方法を見ます。

    -

    参照外し演算子で値までポインタを追いかける

    +

    参照外し演算子で値までポインタを追いかける

    @@ -213,7 +213,7 @@

    The Rust Programming Language

    参照と数値は異なる型なので、比較することは許容されていません。参照外し演算子を使用して、 参照を指している値まで追いかけなければならないのです。

    -

    Box<T>を参照のように使う

    +

    Box<T>を参照のように使う

    リスト15-6のコードを参照の代わりにBox<T>を使うように書き直すことができます; @@ -242,7 +242,7 @@

    The Rust Programming Language

    最後のアサートで参照外し演算子を使用してyが参照だった時のようにボックスのポインタを追いかけることができます。 次に、独自のボックス型を定義することで参照外し演算子を使用させてくれるBox<T>について何が特別なのかを探究します。

    -

    独自のスマートポインタを定義する

    +

    独自のスマートポインタを定義する

    @@ -310,7 +310,7 @@

    The Rust Programming Language

    MyBox<T>に参照外しの能力を実装していないので、参照外しできません。*演算子で参照外しできるようにするには、 Derefトレイトを実装します。

    -

    Derefトレイトを実装して型を参照のように扱う

    +

    Derefトレイトを実装して型を参照のように扱う

    @@ -388,7 +388,7 @@

    The Rust Programming Language

    *演算子は、コードで*を打つたびに、ただ1回、derefメソッドの呼び出し、そして*演算子の呼び出しに置き換えられることに注意してください。 *演算子の置き換えは、無限に繰り返されないので、型i32に行き着き、リスト15-9でassert_eq!5と合致します。

    -

    関数やメソッドで暗黙的な参照外し型強制

    +

    関数やメソッドで暗黙的な参照外し型強制

    @@ -531,7 +531,7 @@

    The Rust Programming Language

    参照を得、引数の型と一致させます。Deref::derefが挿入される必要のある回数は、コンパイル時に解決されるので、 参照外し型強制を活用するための実行時の代償は何もありません。

    -

    参照外し型強制が可変性と相互作用する方法

    +

    参照外し型強制が可変性と相互作用する方法

    diff --git a/docs/second-edition/ch15-03-drop.html b/docs/second-edition/ch15-03-drop.html index 234beb3..fef12cb 100644 --- a/docs/second-edition/ch15-03-drop.html +++ b/docs/second-edition/ch15-03-drop.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Dropトレイトで片付け時にコードを走らせる

    +

    Dropトレイトで片付け時にコードを走らせる

    @@ -238,7 +238,7 @@

    The Rust Programming Language

    この例は、dropメソッドの動き方を見た目で案内するだけですが、通常は、メッセージ出力ではなく、 自分の型が走らせる必要のあるクリーンアップコードを指定するでしょう。

    -

    std::mem::dropで早期に値をドロップする

    +

    std::mem::dropで早期に値をドロップする

    diff --git a/docs/second-edition/ch15-04-rc.html b/docs/second-edition/ch15-04-rc.html index 2d25ffe..763c34a 100644 --- a/docs/second-edition/ch15-04-rc.html +++ b/docs/second-edition/ch15-04-rc.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Rc<T>は、参照カウント方式のスマートポインタ

    +

    Rc<T>は、参照カウント方式のスマートポインタ

    @@ -185,7 +185,7 @@

    The Rust Programming Language

    Rc<T>は、シングルスレッドの筋書きで使用するためだけのものであることに注意してください。 第16章で並行性について議論する時に、マルチスレッドプログラムで参照カウントをする方法を講義します。

    -

    Rc<T>でデータを共有する

    +

    Rc<T>でデータを共有する

    @@ -318,7 +318,7 @@

    The Rust Programming Language

    視覚的にディープコピーをする類のクローンと参照カウントを増やす種類のクローンを区別することができます。 コード内でパフォーマンスの問題を探す際、ディープコピーのクローンだけを考慮し、Rc::cloneの呼び出しを無視できるのです。

    -

    Rc<T>をクローンすると、参照カウントが増える

    +

    Rc<T>をクローンすると、参照カウントが増える

    aRc<List>への参照を作ったりドロップする毎に参照カウントが変化するのが確かめられるように、 diff --git a/docs/second-edition/ch15-05-interior-mutability.html b/docs/second-edition/ch15-05-interior-mutability.html index 0862e08..3efb8ef 100644 --- a/docs/second-edition/ch15-05-interior-mutability.html +++ b/docs/second-edition/ch15-05-interior-mutability.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    RefCell<T>と内部可変性パターン

    +

    RefCell<T>と内部可変性パターン

    @@ -162,7 +162,7 @@

    The Rust Programming Language

    内部可変性パターンに従うRefCell<T>型を眺めてこの概念を探究しましょう。

    -

    RefCell<T>で実行時に借用規則を強制する

    +

    RefCell<T>で実行時に借用規則を強制する

    @@ -244,7 +244,7 @@

    The Rust Programming Language

    不変な値の中の値を可変化することは、内部可変性パターンです。内部可変性が有用になる場面を見て、 それが可能になる方法を調査しましょう。

    -

    内部可変性: 不変値への可変借用

    +

    内部可変性: 不変値への可変借用

    借用規則の結果は、不変値がある時、可変で借用することはできないということです。 @@ -282,7 +282,7 @@

    The Rust Programming Language

    RefCell<T>を使用して不変値を可変化する実践的な例に取り組み、それが役に立つ理由を確認しましょう。

    -

    内部可変性のユースケース: モックオブジェクト

    +

    内部可変性のユースケース: モックオブジェクト

    @@ -541,7 +541,7 @@

    The Rust Programming Language

    RefCell<T>の使用法を見かけたので、動作の仕方を深掘りしましょう!

    -

    RefCell<T>で実行時に借用を追いかける

    +

    RefCell<T>で実行時に借用を追いかける

    @@ -623,7 +623,7 @@

    The Rust Programming Language

    自身を変更して見かけたメッセージを追跡するモックオブジェクトを書くことが可能になります。 代償はありますが、RefCell<T>を使用すれば、普通の参照よりも多くの機能を得ることができるわけです。

    -

    Rc<T>RefCell<T>を組み合わせることで可変なデータに複数の所有者を持たせる

    +

    Rc<T>RefCell<T>を組み合わせることで可変なデータに複数の所有者を持たせる

    diff --git a/docs/second-edition/ch15-06-reference-cycles.html b/docs/second-edition/ch15-06-reference-cycles.html index ef297f9..fd8224d 100644 --- a/docs/second-edition/ch15-06-reference-cycles.html +++ b/docs/second-edition/ch15-06-reference-cycles.html @@ -143,7 +143,7 @@

    The Rust Programming Language

    -

    循環参照は、メモリをリークすることもある

    +

    循環参照は、メモリをリークすることもある

    @@ -159,7 +159,7 @@

    The Rust Programming Language

    要素がお互いに循環して参照する参照を生成することも可能ということです。循環の各要素の参照カウントが絶対に0にならないので、 これはメモリリークを起こし、値は絶対にドロップされません。

    -

    循環参照させる

    +

    循環参照させる

    @@ -347,7 +347,7 @@

    The Rust Programming Language

    リスト15-25では、常にCons列挙子にリストを所有してほしいので、データ構造を再構成することはできません。 親ノードと子ノードからなるグラフを使った例に目を向けて、どんな時に所有権のない関係が循環参照を回避するのに適切な方法になるか確認しましょう。

    -

    循環参照を回避する: Rc<T>Weak<T>に変換する

    +

    循環参照を回避する: Rc<T>Weak<T>に変換する

    @@ -390,7 +390,7 @@

    The Rust Programming Language

    例として、要素が次の要素を知っているだけのリストを使うのではなく、要素が子要素親要素を知っている木を作りましょう。

    -

    木データ構造を作る: 子ノードのあるNode

    +

    木データ構造を作る: 子ノードのあるNode

    @@ -460,7 +460,7 @@

    The Rust Programming Language

    理由は、leafにはbranchへの参照がなく、関係していることを知らないからです。leafbranchが親であることを知ってほしいです。 次はそれを行います。

    -

    子供から親に参照を追加する

    +

    子供から親に参照を追加する

    @@ -581,7 +581,7 @@

    The Rust Programming Language

    無限の出力が欠けているということは、このコードは循環参照しないことを示唆します。 このことは、Rc::strong_countRc::weak_countを呼び出すことで得られる値を見てもわかります。

    -

    strong_countweak_countへの変更を可視化する

    +

    strong_countweak_countへの変更を可視化する

    @@ -691,7 +691,7 @@

    The Rust Programming Language

    Nodeの定義で子供から親への関係はWeak<T>参照になるべきと指定することで、 循環参照やメモリリークを引き起こさずに親ノードに子ノードを参照させたり、その逆を行うことができます。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch16-00-concurrency.html b/docs/second-edition/ch16-00-concurrency.html index da4e956..16975fa 100644 --- a/docs/second-edition/ch16-00-concurrency.html +++ b/docs/second-edition/ch16-00-concurrency.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    恐れるな!並行性

    +

    恐れるな!並行性

    diff --git a/docs/second-edition/ch16-01-threads.html b/docs/second-edition/ch16-01-threads.html index 1df5759..03660e4 100644 --- a/docs/second-edition/ch16-01-threads.html +++ b/docs/second-edition/ch16-01-threads.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    スレッドを使用してコードを同時に走らせる

    +

    スレッドを使用してコードを同時に走らせる

    @@ -227,7 +227,7 @@

    The Rust Programming Language

    今やRustにおけるスレッドを定義したので、標準ライブラリで提供されているスレッド関連のAPIの使用法を探究しましょう。

    -

    spawnで新規スレッドを生成する

    +

    spawnで新規スレッドを生成する

    @@ -292,7 +292,7 @@

    The Rust Programming Language

    このコードを実行してメインスレッドの出力しか目の当たりにできなかったり、オーバーラップがなければ、 範囲の値を増やしてOSがスレッド切り替えを行う機会を増やしてみてください。

    -

    joinハンドルで全スレッドの終了を待つ

    +

    joinハンドルで全スレッドの終了を待つ

    @@ -408,7 +408,7 @@

    The Rust Programming Language

    どこでjoinを呼ぶかといったほんの些細なことが、スレッドが同時に走るかどうかに影響することもあります。

    -

    スレッドでmoveクロージャを使用する

    +

    スレッドでmoveクロージャを使用する

    moveクロージャは、thread::spawnとともによく使用されます。 diff --git a/docs/second-edition/ch16-02-message-passing.html b/docs/second-edition/ch16-02-message-passing.html index ab97255..1739e16 100644 --- a/docs/second-edition/ch16-02-message-passing.html +++ b/docs/second-edition/ch16-02-message-passing.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    メッセージ受け渡しを使ってスレッド間でデータを転送する

    +

    メッセージ受け渡しを使ってスレッド間でデータを転送する

    @@ -334,7 +334,7 @@

    The Rust Programming Language

    完璧です!

    -

    チャンネルと所有権の転送

    +

    チャンネルと所有権の転送

    @@ -398,7 +398,7 @@

    The Rust Programming Language

    値がムーブされると、受信側が所有権を得るのです。これにより、送信後に誤って再度値を使用するのを防いでくれます; 所有権システムが、万事問題ないことを確認してくれます。

    -

    複数の値を送信し、受信側が待機するのを確かめる

    +

    複数の値を送信し、受信側が待機するのを確かめる

    @@ -466,7 +466,7 @@

    The Rust Programming Language

    メインスレッドのforループには停止したり、遅れせたりするコードは何もないので、 メインスレッドが立ち上げたスレッドから値を受け取るのを待機していることがわかります。

    -

    転送機をクローンして複数の生成器を作成する

    +

    転送機をクローンして複数の生成器を作成する

    diff --git a/docs/second-edition/ch16-03-shared-state.html b/docs/second-edition/ch16-03-shared-state.html index e170a71..9b47552 100644 --- a/docs/second-edition/ch16-03-shared-state.html +++ b/docs/second-edition/ch16-03-shared-state.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    状態共有並行性

    +

    状態共有並行性

    @@ -170,7 +170,7 @@

    The Rust Programming Language

    Rustの型システムと所有権規則は、この管理を正しく行う大きな助けになります。 例として、メモリ共有を行うより一般的な並行性の基本型の一つであるミューテックスを見てみましょう。

    -

    ミューテックスを使用して一度に1つのスレッドからデータにアクセスすることを許可する

    +

    ミューテックスを使用して一度に1つのスレッドからデータにアクセスすることを許可する

    @@ -213,7 +213,7 @@

    The Rust Programming Language

    ミューテックスの管理は、正しく行うのに著しく技工を要することがあるので、多くの人がチャンネルに熱狂的になるわけです。 しかしながら、Rustの型システムと所有権規則のおかげで、ロックとアンロックをおかしくすることはありません。

    -

    Mutex<T>のAPI

    +

    Mutex<T>のAPI

    ミューテックスの使用方法の例として、ミューテックスをシングルスレッドの文脈で使うことから始めましょう。 @@ -276,7 +276,7 @@

    The Rust Programming Language

    ロックをドロップした後、ミューテックスの値を出力し、内部のi32の値を6に変更できたことが確かめられるのです。

    -

    複数のスレッド間でMutex<T>を共有する

    +

    複数のスレッド間でMutex<T>を共有する

    @@ -448,7 +448,7 @@

    The Rust Programming Language

    ループの違う繰り返しにある違うスレッドをコンパイラは指し示せないからです。 第15章で議論した複数所有権メソッドによりコンパイルエラーを修正しましょう。

    -

    複数のスレッドで複数の所有権

    +

    複数のスレッドで複数の所有権

    @@ -544,7 +544,7 @@

    The Rust Programming Language

    これは間違ったカウントにつながる可能性があり、今度はメモリリークや、使用し終わる前に値がドロップされることにつながる可能性のある潜在的なバグです。 必要なのは、いかにもRc<T>のようだけれども、参照カウントへの変更をスレッドセーフに行うものです。

    -

    Arc<T>で原子的な参照カウント

    +

    Arc<T>で原子的な参照カウント

    @@ -616,7 +616,7 @@

    The Rust Programming Language

    計算を独立した部分に小分けにし、その部分をスレッドに分割し、それからMutex<T>を使用して、 各スレッドに最終結果を更新させることができます。

    -

    RefCell<T>/Rc<T>Mutex<T>/Arc<T>の類似性

    +

    RefCell<T>/Rc<T>Mutex<T>/Arc<T>の類似性

    diff --git a/docs/second-edition/ch16-04-extensible-concurrency-sync-and-send.html b/docs/second-edition/ch16-04-extensible-concurrency-sync-and-send.html index 9a23571..91b2606 100644 --- a/docs/second-edition/ch16-04-extensible-concurrency-sync-and-send.html +++ b/docs/second-edition/ch16-04-extensible-concurrency-sync-and-send.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    SyncSendトレイトで拡張可能な並行性

    +

    SyncSendトレイトで拡張可能な並行性

    @@ -155,7 +155,7 @@

    The Rust Programming Language

    ですが、2つの並行性概念が言語に埋め込まれています: std::markerトレイトのSyncSendです。

    -

    Sendでスレッド間の所有権の転送を許可する

    +

    Sendでスレッド間の所有権の転送を許可する

    @@ -183,7 +183,7 @@

    The Rust Programming Language

    完全にSendの型からなる型も全て自動的にSendと印付けされます。生ポインタを除くほとんどの基本型もSendで、 生ポインタについては第19章で議論します。

    -

    Syncで複数のスレッドからのアクセスを許可する

    +

    Syncで複数のスレッドからのアクセスを許可する

    @@ -205,7 +205,7 @@

    The Rust Programming Language

    スマートポインタのMutex<T>Syncで、「複数のスレッド間でMutex<T>を共有する」節で見たように、 複数のスレッドでアクセスを共有するのに使用することができます。

    -

    SendSyncを手動で実装するのは非安全である

    +

    SendSyncを手動で実装するのは非安全である

    @@ -228,7 +228,7 @@

    The Rust Programming Language

    訳注: 日本語版のThe Rustonomiconはこちらです。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch17-00-oop.html b/docs/second-edition/ch17-00-oop.html index 48dcd3f..33db412 100644 --- a/docs/second-edition/ch17-00-oop.html +++ b/docs/second-edition/ch17-00-oop.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Rustのオブジェクト指向プログラミング機能

    +

    Rustのオブジェクト指向プログラミング機能

    diff --git a/docs/second-edition/ch17-01-what-is-oo.html b/docs/second-edition/ch17-01-what-is-oo.html index afc7548..cf91492 100644 --- a/docs/second-edition/ch17-01-what-is-oo.html +++ b/docs/second-edition/ch17-01-what-is-oo.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    オブジェクト指向言語の特徴

    +

    オブジェクト指向言語の特徴

    @@ -156,7 +156,7 @@

    The Rust Programming Language

    OOP言語は特定の一般的な特徴を共有しています。具体的には、オブジェクトやカプセル化、 継承などです。それらの個々の特徴が意味するものとRustがサポートしているかを見ましょう。

    -

    オブジェクトは、データと振る舞いを含む

    +

    オブジェクトは、データと振る舞いを含む

    @@ -182,7 +182,7 @@

    The Rust Programming Language

    implブロックが構造体とenumにメソッドを提供します。メソッドのある構造体とenumは、 オブジェクトとは呼ばれないものの、GoFのオブジェクト定義によると、同じ機能を提供します。

    -

    カプセル化は、実装詳細を隠蔽する

    +

    カプセル化は、実装詳細を隠蔽する

    @@ -311,7 +311,7 @@

    The Rust Programming Language

    カプセル化が、言語がオブジェクト指向と考えられるのに必要な側面ならば、Rustはその条件を満たしています。 コードの異なる部分でpubを使用するかしないかという選択肢のおかげで、実装詳細をカプセル化することが可能になります。

    -

    型システム、およびコード共有としての継承

    +

    型システム、およびコード共有としての継承

    @@ -362,7 +362,7 @@

    The Rust Programming Language

    -

    多相性

    +

    多相性

    多くの人にとって、多相性は、継承の同義語です。ですが、実際には複数の型のデータを取り扱えるコードを指すより一般的な概念です。 継承について言えば、それらの型は一般的にはサブクラスです。

    Rustは代わりにジェネリクスを使用して様々な可能性のある型を抽象化し、トレイト境界を使用してそれらの型が提供するものに制約を課します。 diff --git a/docs/second-edition/ch17-02-trait-objects.html b/docs/second-edition/ch17-02-trait-objects.html index 6467306..9ef5f2e 100644 --- a/docs/second-edition/ch17-02-trait-objects.html +++ b/docs/second-edition/ch17-02-trait-objects.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    トレイトオブジェクトで異なる型の値を許容する

    +

    トレイトオブジェクトで異なる型の値を許容する

    @@ -198,7 +198,7 @@

    The Rust Programming Language

    Componentインスタンスであるかのようにその型全部を扱い、この型に対してdrawを呼び出します。 ですが、Rustに継承は存在しないので、使用者に新しい型で拡張してもらうためにguiライブラリを構成する他の方法が必要です。

    -

    一般的な振る舞いにトレイトを定義する

    +

    一般的な振る舞いにトレイトを定義する

    @@ -353,7 +353,7 @@

    The Rust Programming Language

    Box<Button>Box<TextField>を含むVec<T>を保持できます。 この動作方法を見、それから実行時性能の裏の意味について語りましょう。

    -

    トレイトを実装する

    +

    トレイトを実装する

    @@ -496,7 +496,7 @@

    The Rust Programming Language

    componentsベクタでBox<Draw>を値の型として指定することで、Screenを、 drawメソッドを呼び出せる値を必要とするように定義できたのです。

    -

    注釈: ダックタイピングについて

    +

    注釈: ダックタイピングについて

    ご存知かもしれませんが、ダックタイピングについて補足です。ダックタイピングとは、動的型付け言語やC++のテンプレートで使用される、 特定のフィールドやメソッドがあることを想定してコンパイルを行い、実行時に実際にあることを確かめるというプログラミング手法です。 ダック・テストという思考法に由来するそうです。

    @@ -551,7 +551,7 @@

    The Rust Programming Language

    このエラーは、渡すことを意図していないものをScreenに渡しているので、異なる型を渡すべきか、 Screendrawを呼び出せるようにStringDrawを実装するべきのどちらかであることを知らせてくれています。

    -

    トレイトオブジェクトは、ダイナミックディスパッチを行う

    +

    トレイトオブジェクトは、ダイナミックディスパッチを行う

    @@ -586,7 +586,7 @@

    The Rust Programming Language

    そのため、ある種の最適化が不可能になります。ですが、リスト17-5で記述し、 リスト17-9ではサポートできたコードで追加の柔軟性を確かに得られたので、考慮すべき代償です。

    -

    トレイトオブジェクトには、オブジェクト安全性が必要

    +

    トレイトオブジェクトには、オブジェクト安全性が必要

    diff --git a/docs/second-edition/ch17-03-oo-design-patterns.html b/docs/second-edition/ch17-03-oo-design-patterns.html index 7c3001d..e921093 100644 --- a/docs/second-edition/ch17-03-oo-design-patterns.html +++ b/docs/second-edition/ch17-03-oo-design-patterns.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    オブジェクト指向デザインパターンを実装する

    +

    オブジェクト指向デザインパターンを実装する

    @@ -246,7 +246,7 @@

    The Rust Programming Language

    状態の変化を直接管理する必要はありません。また、ユーザは、 査読前に記事を公開するなど状態を誤ることはありません。

    -

    Postを定義し、草稿状態で新しいインスタンスを生成する

    +

    Postを定義し、草稿状態で新しいインスタンスを生成する

    @@ -306,7 +306,7 @@

    The Rust Programming Language

    新しいPostを作る度に、草稿から始まることが保証されます。Poststateフィールドは非公開なので、 Postを他の状態で作成する方法はないのです!Post::new関数では、contentフィールドを新しい空のStringにセットしています。

    -

    記事の内容のテキストを格納する

    +

    記事の内容のテキストを格納する

    @@ -349,7 +349,7 @@

    The Rust Programming Language

    この振る舞いは、記事の状態によらないので、ステートパターンの一部ではありません。add_textメソッドは、 stateフィールドと全く相互作用しませんが、サポートしたい振る舞いの一部ではあります。

    -

    草稿の記事の内容は空であることを保証する

    +

    草稿の記事の内容は空であることを保証する

    @@ -389,7 +389,7 @@

    The Rust Programming Language

    -

    記事の査読を要求すると、状態が変化する

    +

    記事の査読を要求すると、状態が変化する

    次に、記事の査読を要求する機能を追加する必要があり、これをすると、状態がDraftからPendingReviewに変わるはずです。 @@ -497,7 +497,7 @@

    The Rust Programming Language

    これでPostPendingReviewDraft状態になり得ますが、PendingReview状態でも、 同じ振る舞いが欲しいです。もうリスト17-11は11行目まで動くようになりました!

    -

    contentの振る舞いを変化させるapproveメソッドを追加する

    +

    contentの振る舞いを変化させるapproveメソッドを追加する

    @@ -694,7 +694,7 @@

    The Rust Programming Language

    出来上がりました。要するに、リスト17-11はもう動くようになったのです!ブログ記事ワークフローの規則でステートパターンを実装しました。 その規則に関連するロジックは、Post中に散乱するのではなく、ステートオブジェクトに息づいています。

    -

    ステートパターンの代償

    +

    ステートパターンの代償

    @@ -780,7 +780,7 @@

    The Rust Programming Language

    オブジェクト指向言語で定義されている通り忠実にステートパターンを実装することで、 Rustの強みをできるだけ発揮していません。blogクレートに対して行える無効な状態と遷移をコンパイルエラーにできる変更に目を向けましょう。

    -

    状態と振る舞いを型としてコード化する

    +

    状態と振る舞いを型としてコード化する

    @@ -874,7 +874,7 @@

    The Rust Programming Language

    従って、これでプログラムは、全ての記事が草稿記事から始まり、草稿記事は表示できる内容がないことを保証します。 この制限をかいくぐる試みは、全てコンパイルエラーに落ち着くでしょう。

    -

    遷移を異なる型への変形として実装する

    +

    遷移を異なる型への変形として実装する

    @@ -1013,7 +1013,7 @@

    The Rust Programming Language

    コンパイル時に一部のバグを回避できるなどの利益が得られることもあります。オブジェクト指向のパターンは、 オブジェクト指向言語にはない所有権などの特定の機能によりRustでは、必ずしも最善の解決策ではないでしょう。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch18-00-patterns.html b/docs/second-edition/ch18-00-patterns.html index c2c81e1..7d483ca 100644 --- a/docs/second-edition/ch18-00-patterns.html +++ b/docs/second-edition/ch18-00-patterns.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    パターンとマッチング

    +

    パターンとマッチング

    diff --git a/docs/second-edition/ch18-01-all-the-places-for-patterns.html b/docs/second-edition/ch18-01-all-the-places-for-patterns.html index b5a4f6c..98ce699 100644 --- a/docs/second-edition/ch18-01-all-the-places-for-patterns.html +++ b/docs/second-edition/ch18-01-all-the-places-for-patterns.html @@ -142,14 +142,14 @@

    The Rust Programming Language

    -

    パターンが使用されることのある箇所全部

    +

    パターンが使用されることのある箇所全部

    Rustにおいて、パターンはいろんな箇所に出現し、そうと気づかないうちにたくさん使用してきました! この節は、パターンが合法な箇所全部を議論します。

    -

    matchアーム

    +

    matchアーム

    @@ -180,7 +180,7 @@

    The Rust Programming Language

    例えば、_パターンは、指定されていないあらゆる値を無視したい時に有用です。 _パターンについて詳しくは、この章の後ほど、「パターンで値を無視する」節で講義します。

    -

    条件分岐if let

    +

    条件分岐if let

    @@ -263,7 +263,7 @@

    The Rust Programming Language

    if let式を使うことの欠点は、コンパイラが網羅性を確認してくれないことです。一方でmatch式ではしてくれます。 最後のelseブロックを省略して故に、扱い忘れたケースがあっても、コンパイラは、ロジックバグの可能性を指摘してくれないでしょう。

    -

    while let条件分岐ループ

    +

    while let条件分岐ループ

    @@ -296,7 +296,7 @@

    The Rust Programming Language

    ベクタが空なら、popNoneを返します。whileループはpopSomeを返す限り、ブロックのコードを実行し続けます。 popNoneを返すと、ループは停止します。while letを使用してスタックから全ての要素を取り出せるのです。

    -

    forループ

    +

    forループ

    @@ -334,7 +334,7 @@

    The Rust Programming Language

    enumerateの最初の呼び出しは、タプル(0, 'a')を生成します。この値がパターン(index, value)とマッチさせられると、 index0value'a'になり、出力の最初の行を出力するのです。

    -

    let

    +

    let

    @@ -414,7 +414,7 @@

    The Rust Programming Language

    _..を使用できるでしょう。パターンに変数が多すぎるというのが問題なら、変数の数がタプルの要素数と一致するように変数を減らすことで、 型を一致させることが解決策です。

    -

    関数の引数

    +

    関数の引数

    diff --git a/docs/second-edition/ch18-02-refutability.html b/docs/second-edition/ch18-02-refutability.html index 7f01119..2d60bef 100644 --- a/docs/second-edition/ch18-02-refutability.html +++ b/docs/second-edition/ch18-02-refutability.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    論駁可能性: パターンが合致しないかどうか

    +

    論駁可能性: パターンが合致しないかどうか

    diff --git a/docs/second-edition/ch18-03-pattern-syntax.html b/docs/second-edition/ch18-03-pattern-syntax.html index aa639a4..9e2ccc5 100644 --- a/docs/second-edition/ch18-03-pattern-syntax.html +++ b/docs/second-edition/ch18-03-pattern-syntax.html @@ -142,14 +142,14 @@

    The Rust Programming Language

    -

    パターン記法

    +

    パターン記法

    本全体で、多くの種類のパターンの例を見かけてきました。この節では、パターンで合法な記法全てを集め、 それぞれを使用したくなる可能性がある理由について議論します。

    -

    リテラルにマッチする

    +

    リテラルにマッチする

    第6章で目撃したように、パターンを直接リテラルに合致させられます。以下のコードが例を挙げています:

    @@ -170,7 +170,7 @@

    The Rust Programming Language

    このコードは、xの値が1なので、oneを出力します。この記法は、コードが特定の具体的な値を得た時に行動を起こしてほしい時に有用です。

    -

    名前付き変数にマッチする

    +

    名前付き変数にマッチする

    @@ -246,7 +246,7 @@

    The Rust Programming Language

    代わりにマッチガード条件式を使用する必要があるでしょう。マッチガードについては、後ほど、 「マッチガードで追加の条件式」節で語ります。

    -

    複数のパターン

    +

    複数のパターン

    @@ -271,7 +271,7 @@

    The Rust Programming Language

    このコードは、one or twoを出力します。

    -

    ...で値の範囲に合致させる

    +

    ...で値の範囲に合致させる

    @@ -322,13 +322,13 @@

    The Rust Programming Language

    コンパイラにはcが最初のパターンの範囲にあることがわかり、early ASCII letterと出力されます。

    -

    分配して値を分解する

    +

    分配して値を分解する

    また、パターンを使用して構造体、enum、タプル、参照を分配し、これらの値の異なる部分を使用することもできます。 各値を見ていきましょう。

    -

    構造体を分配する

    +

    構造体を分配する

    リスト18-12は、let文でパターンを使用して分解できる2つのフィールドxyのあるPoint構造体を示しています。

    @@ -442,7 +442,7 @@

    The Rust Programming Language

    この例で、値pは0を含むxの力で2番目のアームに一致するので、このコードはOn the y axis at 7と出力します。

    -

    enumを分配する

    +

    enumを分配する

    @@ -520,7 +520,7 @@

    The Rust Programming Language

    パターンは、タプルと一致させるために指定するパターンと類似しています。パターンの変数の数は、 マッチ対象の列挙子の要素数と一致しなければなりません。

    -

    参照を分配する

    +

    参照を分配する

    @@ -584,7 +584,7 @@

    The Rust Programming Language

    このエラーは、コンパイラがクロージャに&Pointと一致することを期待しているのに、 Pointへの参照ではなく、Point値に直接一致させようとしたことを示唆しています。

    -

    構造体とタプルを分配する

    +

    構造体とタプルを分配する

    @@ -607,7 +607,7 @@

    The Rust Programming Language

    パターンで分配することは、構造体の各フィールドからの値のように、複数の値をお互いに区別して使用する便利な方法です。

    -

    パターンの値を無視する

    +

    パターンの値を無視する

    @@ -621,7 +621,7 @@

    The Rust Programming Language

    他のパターン内で_パターンを使用すること、アンダースコアで始まる名前を使用すること、..を使用して値の残りの部分を無視することです。 これらのパターンそれぞれを使用する方法と理由を探究しましょう。

    -

    _で値全体を無視する

    +

    _で値全体を無視する

    @@ -657,7 +657,7 @@

    The Rust Programming Language

    特定の型シグニチャが必要だけれども、自分の実装の関数本体では引数の1つが必要ない時などです。 そうすれば、代わりに名前を使った場合のようには、未使用関数引数についてコンパイラが警告することはないでしょう。

    -

    ネストされた_で値の一部を無視する

    +

    ネストされた_で値の一部を無視する

    @@ -731,7 +731,7 @@

    The Rust Programming Language

    このコードは、Some numbers: 2, 8, 32と出力し、値4と16は無視されます。

    -

    名前を_で始めて未使用の変数を無視する

    +

    名前を_で始めて未使用の変数を無視する

    @@ -802,7 +802,7 @@

    The Rust Programming Language

    このコードは、sを何にも束縛しないので、ただ単に上手く動きます。つまり、ムーブされないのです。

    -

    ..で値の残りの部分を無視する

    +

    ..で値の残りの部分を無視する

    @@ -904,7 +904,7 @@

    The Rust Programming Language

    変数名のsecondは、コンパイラにとってなんの特別な意味もなく、このように2箇所で..を使うのは曖昧なので、 コンパイルエラーになります。

    -

    refref mutでパターンに参照を生成する

    +

    refref mutでパターンに参照を生成する

    @@ -1006,7 +1006,7 @@

    The Rust Programming Language

    この例はコンパイルが通り、robot_name is: Some("Another name")と出力するでしょう。 nameは可変参照なので、値を可変化するためにマッチアーム内で*演算子を使用して参照外しする必要があります。

    -

    マッチガードで追加の条件式

    +

    マッチガードで追加の条件式

    @@ -1148,7 +1148,7 @@

    The Rust Programming Language

    コードを実行後には、優先度の動作は明らかになります: マッチガードが|演算子で指定される値のリストの最後の値にしか適用されないなら、 アームはマッチし、プログラムはyesと出力したでしょう。

    -

    @束縛

    +

    @束縛

    @@ -1213,7 +1213,7 @@

    The Rust Programming Language

    @を使用することで、値を検査しつつ、1つのパターン内で変数に保存させてくれるのです。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch19-00-advanced-features.html b/docs/second-edition/ch19-00-advanced-features.html index 173c5a9..a3c7068 100644 --- a/docs/second-edition/ch19-00-advanced-features.html +++ b/docs/second-edition/ch19-00-advanced-features.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    高度な機能

    +

    高度な機能

    diff --git a/docs/second-edition/ch19-01-unsafe-rust.html b/docs/second-edition/ch19-01-unsafe-rust.html index 10be0ef..201d4d1 100644 --- a/docs/second-edition/ch19-01-unsafe-rust.html +++ b/docs/second-edition/ch19-01-unsafe-rust.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    Unsafe Rust

    +

    Unsafe Rust

    @@ -175,7 +175,7 @@

    The Rust Programming Language

    直接OSと相互作用したり、独自のOSを書くことさえもそうです。低レベルなシステムプログラミングに取り組むことは、 言語の目標の1つなのです。unsafe Rustでできることとその方法を探究しましょう。

    -

    unsafeの強大な力(superpower)

    +

    unsafeの強大な力(superpower)

    @@ -232,7 +232,7 @@

    The Rust Programming Language

    4つのunsafeなsuperpowerを順に見ていきましょう。unsafeなコードへの安全なインターフェイスを提供する一部の抽象化にも目を向けます。

    -

    生ポインタを参照外しする

    +

    生ポインタを参照外しする

    @@ -355,7 +355,7 @@

    The Rust Programming Language

    Cコードとのインターフェイスです。別のユースケースは、借用チェッカーには理解できない安全な抽象を構成する時です。 unsafeな関数を導入し、それからunsafeコードを使用する安全な抽象の例に目を向けます。

    -

    unsafeな関数やメソッドを呼ぶ

    +

    unsafeな関数やメソッドを呼ぶ

    @@ -404,7 +404,7 @@

    The Rust Programming Language

    unsafe関数の本体は、実効的にunsafeブロックになるので、unsafe関数内でunsafeな別の処理を行うのに、 別のunsafeブロックは必要ないのです。

    -

    unsafeコードに安全な抽象を行う

    +

    unsafeコードに安全な抽象を行う

    @@ -576,7 +576,7 @@

    The Rust Programming Language

    この任意の場所のメモリは所有していなく、このコードが生成するスライスに有効なi32値が含まれる保証もありません。 sliceを有効なスライスであるかのように使用しようとすると、未定義動作に陥ります。

    -

    extern関数を使用して、外部のコードを呼び出す

    +

    extern関数を使用して、外部のコードを呼び出す

    @@ -644,7 +644,7 @@

    The Rust Programming Language

    -

    他の言語からRustの関数を呼び出す

    +

    他の言語からRustの関数を呼び出す

    また、externを使用して他の言語にRustの関数を呼ばせるインターフェイスを生成することもできます。 externブロックの代わりに、externキーワードを追加し、fnキーワードの直前に使用するABIを指定します。 さらに、#[no_mangle]注釈を追加してRustコンパイラに関数名をマングルしないように指示する必要もあります。 @@ -664,7 +664,7 @@

    The Rust Programming Language

    このexternの使用法では、unsafeは必要ありません。

    -

    可変で静的な変数にアクセスしたり、変更する

    +

    可変で静的な変数にアクセスしたり、変更する

    @@ -751,7 +751,7 @@

    The Rust Programming Language

    Rustは可変で静的な変数をunsafeと考えるのです。可能なら、コンパイラが、データが異なるスレッドからアクセスされることが安全に行われているかを確認するように、 第16章で議論した並行性テクニックとスレッド安全なスマートポインタを使用するのが望ましいです。

    -

    unsafeなトレイトを実装する

    +

    unsafeなトレイトを実装する

    @@ -795,7 +795,7 @@

    The Rust Programming Language

    複数のスレッドから安全にアクセスできるという保証を保持しているか確かめられません; 故に、そのチェックを手動で行い、 unsafeでそのように示唆する必要があります。

    -

    いつunsafeコードを使用するべきか

    +

    いつunsafeコードを使用するべきか

    diff --git a/docs/second-edition/ch19-02-advanced-lifetimes.html b/docs/second-edition/ch19-02-advanced-lifetimes.html index af49cb5..bd7838f 100644 --- a/docs/second-edition/ch19-02-advanced-lifetimes.html +++ b/docs/second-edition/ch19-02-advanced-lifetimes.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    高度なライフタイム

    +

    高度なライフタイム

    @@ -162,7 +162,7 @@

    The Rust Programming Language

  • トレイトオブジェクトのライフタイムの推論: コンパイラにトレイトオブジェクトのライフタイムを推論させることと指定する必要があるタイミング
  • -

    ライフタイム・サブタイピングにより、あるライフタイムが他よりも長生きすることを保証する

    +

    ライフタイム・サブタイピングにより、あるライフタイムが他よりも長生きすることを保証する

    @@ -492,7 +492,7 @@

    The Rust Programming Language

    非常に長くぐにゃぐにゃした例でしたが、この章の冒頭で触れたように、Rustの高度な機能は、非常に限定的です。 この例で解説した記法は、あまり必要になりませんが、そのような場面では、何かを参照し、それに必要なライフタイムを与える方法を知っているでしょう。

    -

    ジェネリックな型への参照に対するライフタイム境界

    +

    ジェネリックな型への参照に対するライフタイム境界

    @@ -592,7 +592,7 @@

    The Rust Programming Language

    参照が十分長生きしないと心配することに関しては、参照が何もない型と永久に生きる参照がある型を現実的に区別できません: どちらも、参照が参照先のライフタイムよりも短いか決定することに関しては同じです。

    -

    トレイトオブジェクトライフタイムの推論

    +

    トレイトオブジェクトライフタイムの推論

    diff --git a/docs/second-edition/ch19-03-advanced-traits.html b/docs/second-edition/ch19-03-advanced-traits.html index 88d8b73..cb7d8da 100644 --- a/docs/second-edition/ch19-03-advanced-traits.html +++ b/docs/second-edition/ch19-03-advanced-traits.html @@ -142,14 +142,14 @@

    The Rust Programming Language

    -

    高度なトレイト

    +

    高度なトレイト

    最初にトレイトについて講義したのは、第10章の「トレイト: 共通の振る舞いを定義する」節でしたが、 ライフタイム同様、より高度な詳細は議論しませんでした。今や、Rustに詳しくなったので、核心に迫れるでしょう。

    -

    関連型でトレイト定義においてプレースホルダーの型を指定する

    +

    関連型でトレイト定義においてプレースホルダーの型を指定する

    @@ -252,7 +252,7 @@

    The Rust Programming Language

    1つしかimpl Iterator for Counterがないからです。Counternextを呼び出す度に、 u32値のイテレータが欲しいと指定しなくてもよいわけです。

    -

    デフォルトのジェネリック型引数と演算子オーバーロード

    +

    デフォルトのジェネリック型引数と演算子オーバーロード

    @@ -398,7 +398,7 @@

    The Rust Programming Language

    最初の目的は2番目に似ていますが、逆です: 既存のトレイトに型引数を追加したいなら、既定を与えて、 既存の実装コードを破壊せずにトレイトの機能を拡張できるのです。

    -

    明確化のためのフルパス記法: 同じ名前のメソッドを呼ぶ

    +

    明確化のためのフルパス記法: 同じ名前のメソッドを呼ぶ

    @@ -713,7 +713,7 @@

    The Rust Programming Language

    フルパス記法を使用することもできるでしょうが、プログラムの他の情報からコンパイラが推論できるこの記法のどの部分も省略することが許容されています。 同じ名前を使用する実装が複数あり、どの実装を呼び出したいかコンパイラが特定するのに助けが必要な場合だけにこのより冗長な記法を使用する必要があるのです。

    -

    スーパートレイトを使用して別のトレイト内で、あるトレイトの機能を必要とする

    +

    スーパートレイトを使用して別のトレイト内で、あるトレイトの機能を必要とする

    @@ -829,7 +829,7 @@

    The Rust Programming Language

    そうすれば、PointOutlinePrintトレイトを実装してもコンパイルは成功し、 Pointインスタンスに対してoutline_printを呼び出し、アスタリスクのふちの中に表示することができます。

    -

    ニュータイプパターンを使用して外部の型に外部のトレイトを実装する

    +

    ニュータイプパターンを使用して外部の型に外部のトレイトを実装する

    diff --git a/docs/second-edition/ch19-04-advanced-types.html b/docs/second-edition/ch19-04-advanced-types.html index 0048317..15385c2 100644 --- a/docs/second-edition/ch19-04-advanced-types.html +++ b/docs/second-edition/ch19-04-advanced-types.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    高度な型

    +

    高度な型

    @@ -157,7 +157,7 @@

    The Rust Programming Language

    注釈: 次の節は、前節「外部の型に外部のトレイトを実装するニュータイプパターン」を読了済みであることを前提にしています。

    -

    型安全性と抽象化を求めてニュータイプパターンを使用する

    +

    型安全性と抽象化を求めてニュータイプパターンを使用する

    @@ -190,7 +190,7 @@

    The Rust Programming Language

    ニュータイプパターンは、カプセル化を実現して実装の詳細を隠匿する軽い方法であり、 実装の詳細を隠匿することは、第17章の「カプセル化は実装詳細を隠蔽する」節で議論しましたね。

    -

    型エイリアスで型同義語を生成する

    +

    型エイリアスで型同義語を生成する

    @@ -333,7 +333,7 @@

    The Rust Programming Language

    別名なので、ただのResult<T, E>であり、要するにResult<T, E>に対して動くメソッドはなんでも使えるし、 ?演算子のような特殊な記法も使えます。

    -

    never型は絶対に返らない

    +

    never型は絶対に返らない

    @@ -441,7 +441,7 @@

    The Rust Programming Language

    ここで、ループは終わりませんので、!が式の値です。ところが、breakを含んでいたら、これは真実にはならないでしょう。 breakに到達した際にループが終了してしまうからです。

    -

    動的サイズ付け型とSizedトレイト

    +

    動的サイズ付け型とSizedトレイト

    diff --git a/docs/second-edition/ch19-05-advanced-functions-and-closures.html b/docs/second-edition/ch19-05-advanced-functions-and-closures.html index a7175a4..cab1a39 100644 --- a/docs/second-edition/ch19-05-advanced-functions-and-closures.html +++ b/docs/second-edition/ch19-05-advanced-functions-and-closures.html @@ -142,12 +142,12 @@

    The Rust Programming Language

    -

    高度な関数とクロージャ

    +

    高度な関数とクロージャ

    最後に関数とクロージャに関連する高度な機能の一部を探究し、これには関数ポインタとクロージャの返却が含まれます。

    -

    関数ポインタ

    +

    関数ポインタ

    @@ -247,7 +247,7 @@

    The Rust Programming Language

    このスタイルを好む方もいますし、クロージャを使うのを好む方もいます。どちらも結果的に同じコードにコンパイルされるので、 どちらでも、自分にとって明確な方を使用してください。

    -

    クロージャを返却する

    +

    クロージャを返却する

    @@ -296,7 +296,7 @@

    The Rust Programming Language

    このコードは、問題なくコンパイルできます。トレイトオブジェクトについて詳しくは、 第17章の「トレイトオブジェクトで異なる型の値を許容する」節を参照してください。

    -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/ch20-00-final-project-a-web-server.html b/docs/second-edition/ch20-00-final-project-a-web-server.html index 5bc88fe..62a0f24 100644 --- a/docs/second-edition/ch20-00-final-project-a-web-server.html +++ b/docs/second-edition/ch20-00-final-project-a-web-server.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    最後のプロジェクト: マルチスレッドのWebサーバを構築する

    +

    最後のプロジェクト: マルチスレッドのWebサーバを構築する

    diff --git a/docs/second-edition/ch20-01-single-threaded.html b/docs/second-edition/ch20-01-single-threaded.html index b0b8dd7..908a9d5 100644 --- a/docs/second-edition/ch20-01-single-threaded.html +++ b/docs/second-edition/ch20-01-single-threaded.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    シングルスレッドのWebサーバを構築する

    +

    シングルスレッドのWebサーバを構築する

    @@ -171,7 +171,7 @@

    The Rust Programming Language

    技術的にはHTTPを他のプロトコルとともに使用することができますが、過半数の場合、HTTPはTCPの上にデータを送信します。 TCPとHTTPのリクエストとレスポンスの生のバイトを取り扱います。

    -

    TCP接続をリッスンする

    +

    TCP接続をリッスンする

    @@ -305,7 +305,7 @@

    The Rust Programming Language

    プログラムを止めることを忘れないでください。そして、一連のコード変更を行った後にcargo runを再起動し、 最新のコードを実行していることを確かめてください。

    -

    リクエストを読み取る

    +

    リクエストを読み取る

    @@ -414,7 +414,7 @@

    The Rust Programming Language

    このリクエストデータを噛み砕いて、ブラウザが我々のプログラムに何を要求しているかを理解しましょう。

    -

    HTTPリクエストを詳しく見る

    +

    HTTPリクエストを詳しく見る

    HTTPはテキストベースのプロトコルで、1つの要求はこのようなフォーマットに則っています:

    Method Request-URI HTTP-Version CRLF
    @@ -462,7 +462,7 @@ 

    The Rust Programming Language

    さて、ブラウザが要求しているものがわかったので、何かデータを返しましょう!

    -

    レスポンスを記述する

    +

    レスポンスを記述する

    さて、クライアントのリクエストに対する返答としてデータの送信を実装します。レスポンスは、以下のようなフォーマットです:

    @@ -536,7 +536,7 @@

    The Rust Programming Language

    Cargoからの出力以外には何も出力はありません。Webブラウザで127.0.0.1:7878をロードすると、 エラーではなく空のページが得られるはずです。HTTPリクエストとレスポンスを手で実装したばかりなのです!

    -

    本物のHTMLを返す

    +

    本物のHTMLを返す

    @@ -621,7 +621,7 @@

    The Rust Programming Language

    この同じHTMLレスポンスが得られるということです。我々のサーバはかなり限定的で、多くのWebサーバとは異なっています。 リクエストに基づいてレスポンスをカスタマイズし、/ への合法なリクエストに対してのみHTMLファイルを送り返したいです。

    -

    リクエストにバリデーションをかけ、選択的にレスポンスを返す

    +

    リクエストにバリデーションをかけ、選択的にレスポンスを返す

    @@ -761,7 +761,7 @@

    The Rust Programming Language

    これらの変更とともに、もう一度サーバを実行してください。127.0.0.1:7878を要求すると、 hello.htmlの中身が返り、127.0.0.1:7878/fooなどの他のリクエストには404.htmlからのエラーHTMLが返るはずです。

    -

    リファクタリングの触り

    +

    リファクタリングの触り

    diff --git a/docs/second-edition/ch20-02-multithreaded.html b/docs/second-edition/ch20-02-multithreaded.html index 2569c8a..4cedb3c 100644 --- a/docs/second-edition/ch20-02-multithreaded.html +++ b/docs/second-edition/ch20-02-multithreaded.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    シングルスレッドサーバをマルチスレッド化する

    +

    シングルスレッドサーバをマルチスレッド化する

    @@ -156,7 +156,7 @@

    The Rust Programming Language

    続くリクエストは長いリクエストが完了するまで待たなければならなくなるでしょう。これを修正する必要がありますが、 まずは、実際に問題が起こっているところを見ます。

    -

    現在のサーバの実装で遅いリクエストをシミュレーションする

    +

    現在のサーバの実装で遅いリクエストをシミュレーションする

    @@ -224,7 +224,7 @@

    The Rust Programming Language

    より多くのリクエストが遅いリクエストの背後に回ってしまうのを回避するようWebサーバが動く方法を変える方法は複数あります; これから実装するのは、スレッドプールです。

    -

    スレッドプールでスループットを向上させる

    +

    スレッドプールでスループットを向上させる

    @@ -285,7 +285,7 @@

    The Rust Programming Language

    第12章のプロジェクトでTDDを使用したように、ここではCompiler Driven Development(コンパイラ駆動開発)を使用します。 欲しい関数を呼び出すコードを書き、それからコンパイラの出すエラーを見てコードが動くように次に何を変更すべきかを決定します。

    -

    各リクエストに対してスレッドを立ち上げられる場合のコードの構造

    +

    各リクエストに対してスレッドを立ち上げられる場合のコードの構造

    @@ -328,7 +328,7 @@

    The Rust Programming Language

    確かに / へのリクエストは、/sleepが完了するのを待機しなくても済むことがわかるでしょう。 ですが、前述したように、無制限にスレッドを生成することになるので、これは最終的にシステムを参らせてしまうでしょう。

    -

    有限数のスレッド用に似たインターフェイスを作成する

    +

    有限数のスレッド用に似たインターフェイスを作成する

    @@ -377,7 +377,7 @@

    The Rust Programming Language

    これはクロージャを取り、実行するためにプール内のスレッドに与えます。このコードはまだコンパイルできませんが、 コンパイラがどう修正したらいいかガイドできるように試してみます。

    -

    コンパイラ駆動開発でThreadPool構造体を構築する

    +

    コンパイラ駆動開発でThreadPool構造体を構築する

    @@ -606,7 +606,7 @@

    The Rust Programming Language

    ここが単体テストを書き始めて、コードがコンパイルでき、かつ欲しい振る舞いを保持していることを確認するのに良い機会でしょう。

    -

    newでスレッド数を検査する

    +

    newでスレッド数を検査する

    @@ -677,7 +677,7 @@

    The Rust Programming Language

    pub fn new(size: usize) -> Result<ThreadPool, PoolCreationError> {
     
    -

    スレッドを格納するスペースを生成する

    +

    スレッドを格納するスペースを生成する

    @@ -756,7 +756,7 @@

    The Rust Programming Language

    再びcargo checkを実行すると、もういくつか警告が出るものの、成功するはずです。

    -

    ThreadPoolからスレッドにコードを送信する責任を負うWorker構造体

    +

    ThreadPoolからスレッドにコードを送信する責任を負うWorker構造体

    @@ -881,7 +881,7 @@

    The Rust Programming Language

    このコードはコンパイルでき、ThreadPool::newへの引数として指定した数のWorkerインスタンスを格納します。 ですがそれでもexecuteで得るクロージャを処理してはいません。次は、それをする方法に目を向けましょう。

    -

    チャンネル経由でスレッドにリクエストを送信する

    +

    チャンネル経由でスレッドにリクエストを送信する

    @@ -1145,7 +1145,7 @@

    The Rust Programming Language

    これらの変更でコードはコンパイルできます!ゴールはもうすぐそこです!

    -

    executeメソッドを実装する

    +

    executeメソッドを実装する

    diff --git a/docs/second-edition/ch20-03-graceful-shutdown-and-cleanup.html b/docs/second-edition/ch20-03-graceful-shutdown-and-cleanup.html index 349b2e5..1816a79 100644 --- a/docs/second-edition/ch20-03-graceful-shutdown-and-cleanup.html +++ b/docs/second-edition/ch20-03-graceful-shutdown-and-cleanup.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    正常なシャットダウンと片付け

    +

    正常なシャットダウンと片付け

    @@ -163,7 +163,7 @@

    The Rust Programming Language

    そして、スレッドに新しいリクエストの受付を停止し、終了するように教える方法を実装します。 このコードが動いているのを確かめるために、サーバを変更して正常にスレッドプールを終了する前に2つしかリクエストを受け付けないようにします。

    -

    ThreadPoolDropトレイトを実装する

    +

    ThreadPoolDropトレイトを実装する

    @@ -305,7 +305,7 @@

    The Rust Programming Language

    ワーカーのスレッドが既にNoneなら、ワーカーはスレッドを既に片付け済みであることがわかるので、 その場合には何も起きません。

    -

    スレッドに仕事をリッスンするのを止めるよう通知する

    +

    スレッドに仕事をリッスンするのを止めるよう通知する

    @@ -762,7 +762,7 @@

    The Rust Programming Language

    そして、APIと頑健性を我々が実装したものと比較する。 -

    まとめ

    +

    まとめ

    diff --git a/docs/second-edition/foreword.html b/docs/second-edition/foreword.html index ac9aa3f..36afb27 100644 --- a/docs/second-edition/foreword.html +++ b/docs/second-edition/foreword.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    まえがき

    +

    まえがき

    diff --git a/docs/second-edition/index.html b/docs/second-edition/index.html index 9b3655a..33ce539 100644 --- a/docs/second-edition/index.html +++ b/docs/second-edition/index.html @@ -141,7 +141,7 @@

    The Rust Programming Language

    -

    まえがき

    +

    まえがき

    diff --git a/docs/second-edition/print.html b/docs/second-edition/print.html index 60830f2..285dd4c 100644 --- a/docs/second-edition/print.html +++ b/docs/second-edition/print.html @@ -142,7 +142,7 @@

    The Rust Programming Language

    -

    まえがき

    +

    まえがき

    @@ -201,7 +201,7 @@

    The Rust Programming Language

    -

    導入

    +

    導入

    @@ -223,12 +223,12 @@

    The Rust Programming Language

    Rustは、その衝突に挑戦しています。バランスのとれた強力な技術の許容量と素晴らしい開発者経験を通して、 Rustは伝統的にそれらの制御と紐付いていた困難全てなしに低レベルの詳細(メモリ使用など)を制御する選択肢を与えてくれます。

    -

    Rustは誰のためのものなの

    +

    Rustは誰のためのものなの

    Rustは、様々な理由により多くの人にとって理想的です。いくつか最も重要なグループを見ていきましょう。

    -

    開発者チーム

    +

    開発者チーム

    @@ -262,7 +262,7 @@

    The Rust Programming Language

    これらや他のツールをRustのエコシステムで使用することで、開発者はシステムレベルのコードを記述しつつ、 生産的になれます。

    -

    学生

    +

    学生

    @@ -273,7 +273,7 @@

    The Rust Programming Language

    多くの人がOS開発などの話題を学んできました。コミュニティはとても暖かく、喜んで学生の質問に答えてくれます。 この本のような努力を通じて、Rustチームはシステムの概念を多くの人、特にプログラミング初心者にとってアクセス可能にしたいと考えています。

    -

    企業

    +

    企業

    @@ -284,14 +284,14 @@

    The Rust Programming Language

    オーディオとビデオの解析および変換、暗号通貨、生物情報学、サーチエンジン、IoTアプリケーション、 機械学習、Firefoxウェブブラウザの主要部分さえ含まれます。

    -

    オープンソース開発者

    +

    オープンソース開発者

    Rustは、Rustプログラミング言語やコミュニティ、開発者ツール、ライブラリを開発したい方向けです。 あなたがRust言語に貢献されることを心よりお待ちしております。

    -

    スピードと安定性に価値を見出す方

    +

    スピードと安定性に価値を見出す方

    @@ -316,7 +316,7 @@

    The Rust Programming Language

    ただの最大の出資者の一部です。総合すると、Rustの最大の野望は、プログラマが数十年間受け入れてきた代償を排除することです: つまり、安全性生産性、スピードエルゴノミクスです。Rustを試してみて、その選択が自分に合っているか確かめてください。

    -

    この本は誰のためのものなの

    +

    この本は誰のためのものなの

    @@ -328,7 +328,7 @@

    The Rust Programming Language

    プログラミングとはなんなのかやそれについて考える方法について多くを語るつもりはありません。 もし、完全なプログラミング初心者であれば、プログラミング入門を特に行う本を読むことでよりよく役に立つでしょう。

    -

    この本の使い方

    +

    この本の使い方

    @@ -434,7 +434,7 @@

    The Rust Programming Language

    周りのテキストを読んで実行しようとしている例がエラーになることを意図しているのか確認することを確かめてください。 ほとんどの場合、コンパイルできないあらゆるコードの正しいバージョンへと導きます。

    -

    ソースコード

    +

    ソースコード

    この本が生成されるソースファイルは、GitHubで見つかります。

    @@ -442,7 +442,7 @@

    The Rust Programming Language

    訳注: 日本語版はこちらです。

    -

    事始め

    +

    事始め

    Rustの旅を始めましょう!学ぶべきことはたくさんありますが、いかなる旅もどこかから始まります。 @@ -456,7 +456,7 @@

    The Rust Programming Language

  • cargoというRustのパッケージマネージャ兼ビルドシステムを使用する
  • -

    インストール

    +

    インストール

    @@ -489,14 +489,14 @@

    The Rust Programming Language

    -

    コマンドライン表記

    +

    コマンドライン表記

    この章及び、本を通して、端末で使用するなんらかのコマンドを示すことがあります。読者が入力するべき行は、 全て$で始まります。$文字を入れる必要はありません; 各コマンドの開始を示しているだけです。 $で始まらない行は、典型的には直前のコマンドの出力を示します。また、PowerShell限定の例は、 $ではなく、>を使用します。

    -

    LinuxとmacOSにrustupをインストールする

    +

    LinuxとmacOSにrustupをインストールする

    LinuxかmacOSを使用しているなら、端末を開き、以下のコマンドを入力してください:

    $ curl https://sh.rustup.rs -sSf | sh
    @@ -538,7 +538,7 @@ 

    The Rust Programming Language

    一般的なRustパッケージの中には、Cコードに依存し、Cコンパイラが必要になるものもあります。 故に今インストールする価値はあるかもしれません。

    -

    Windowsでrustupをインストールする

    +

    Windowsでrustupをインストールする

    @@ -554,7 +554,7 @@

    The Rust Programming Language

    これ以降、cmd.exeとPowerShellの両方で動くコマンドを使用します。 特定の違いがあったら、どちらを使用すべきか説明します。

    -

    更新及びアンインストール

    +

    更新及びアンインストール

    rustup経由でRustをインストールしたら、最新版への更新は、簡単になります。シェルから、 @@ -567,7 +567,7 @@

    The Rust Programming Language

    $ rustup self uninstall
     
    -

    トラブルシューティング

    +

    トラブルシューティング

    Rustが正常にインストールされているか確かめるには、シェルを開いて以下の行を入力してください:

    @@ -598,7 +598,7 @@

    The Rust Programming Language

    明日使えるかもしれないトリビアでした。

    -

    ローカルのドキュメンテーション

    +

    ローカルのドキュメンテーション

    @@ -609,7 +609,7 @@

    The Rust Programming Language

    標準ライブラリにより型や関数が提供され、それがなんなのかや使用方法に確信が持てない度に、APIドキュメンテーションを使用して探してください!

    -

    Hello, World!

    +

    Hello, World!

    @@ -631,7 +631,7 @@

    The Rust Programming Language

    その前線で急激に成果があがっています!

    -

    プロジェクトのディレクトリを作成する

    +

    プロジェクトのディレクトリを作成する

    @@ -664,7 +664,7 @@

    The Rust Programming Language

    > cd hello_world
    -

    Rustプログラムを書いて走らせる

    +

    Rustプログラムを書いて走らせる

    @@ -707,7 +707,7 @@

    The Rust Programming Language

    Hello, world!が確かに出力されたら、おめでとうございます!正式にRustプログラムを書きました。 Rustプログラマになったのです!ようこそ!

    -

    Rustプログラムの解剖

    +

    Rustプログラムの解剖

    Hello, world!プログラムでちょうど何が起こったのか詳しく確認しましょう。 @@ -767,7 +767,7 @@

    The Rust Programming Language

    4番目にこの行をセミコロン(;)で終え、この式が終わり、次の式の準備ができていると示唆していることです。 Rustコードのほとんどの行は、セミコロンで終わります。

    -

    コンパイルと実行は個別のステップ

    +

    コンパイルと実行は個別のステップ

    新しく作成したプログラムをちょうど実行したので、その途中の手順を調査しましょう。

    @@ -832,7 +832,7 @@

    The Rust Programming Language

    オプションを全て管理し、自分のコードを簡単に共有したくなるでしょう。次は、Cargoツールを紹介します。 これは、現実世界のRustプログラムを書く手助けをしてくれるでしょう。

    -

    Hello, Cargo!

    +

    Hello, Cargo!

    @@ -864,7 +864,7 @@

    The Rust Programming Language

    バージョンナンバーが見えたら、インストールされています!command not foundなどのエラーが見えたら、 自分のインストール方法をドキュメンテーションで確認して、Cargoを個別にインストールする方法を決定してください。

    -

    Cargoでプロジェクトを作成する

    +

    Cargoでプロジェクトを作成する

    @@ -967,7 +967,7 @@

    The Rust Programming Language

    実際にCargoを使用するプロジェクトに変換することができます。プロジェクトのコードをsrcディレクトリに移動し、 適切なCargo.tomlファイルを作成してください。

    -

    Cargoプロジェクトをビルドし、実行する

    +

    Cargoプロジェクトをビルドし、実行する

    @@ -1055,7 +1055,7 @@

    The Rust Programming Language

    Cargoを使用する追加の利点は、使用しているOSに関わらず、同じコマンドが使用できることです。 故にこの時点で、WindowsとLinux及びmacOSで特定の手順を提供することは最早なくなります。

    -

    リリースビルドを行う

    +

    リリースビルドを行う

    @@ -1073,7 +1073,7 @@

    The Rust Programming Language

    頻繁に再ビルドをかけたい開発用と、繰り返し再ビルドすることはなく、できるだけ高速に動いてユーザにあげる最終的なプログラムをビルドする用です。 コードの実行時間をベンチマークするなら、cargo build --releaseを確実に実行し、target/releaseの実行可能ファイルでベンチマークしてください。

    -

    習慣としてのCargo

    +

    習慣としてのCargo

    @@ -1094,7 +1094,7 @@

    The Rust Programming Language

    Cargoについてより詳しく知るには、ドキュメンテーションを確認してください。

    -

    まとめ

    +

    まとめ

    既にRustの旅の素晴らしいスタートを切っています!この章では、以下の方法を学びました:

    @@ -1118,7 +1118,7 @@

    The Rust Programming Language

    数当てゲームを構築します。むしろ一般的なプログラミングの概念がRustでどう動くのか学ぶことから始めたいのであれば、 第3章を見て、それから第2章に戻ってください。

    -

    数当てゲームをプログラムする

    +

    数当てゲームをプログラムする

    @@ -1139,7 +1139,7 @@

    The Rust Programming Language

    その予想が小さすぎたか大きすぎたかを出力します。予想が当たっていれば、ゲームは祝福メッセージを表示し、 終了します。

    -

    新規プロジェクトの立ち上げ

    +

    新規プロジェクトの立ち上げ

    新規プロジェクトを立ち上げるには、第1章で作成したprojectsディレクトリに行き、 @@ -1197,7 +1197,7 @@

    The Rust Programming Language

    再度src/main.rsファイルを開きましょう。ここにすべてのコードを書いていきます。

    -

    予想を処理する

    +

    予想を処理する

    @@ -1266,7 +1266,7 @@

    The Rust Programming Language

    このコードは、このゲームが何かを出力し、ユーザに入力を求めています。

    -

    値を変数に保持する

    +

    値を変数に保持する

    次に、ユーザ入力を保持する場所を作りましょう。こんな感じに:

    let mut guess = String::new();
    @@ -1371,7 +1371,7 @@ 

    The Rust Programming Language

    故に、&guessと書くのではなく、&mut guessと書いて、可変にする必要があるのです。 (第4章で参照についてより詳細に説明します)

    -

    Result型で失敗の可能性を扱う

    +

    Result型で失敗の可能性を扱う

    @@ -1456,7 +1456,7 @@

    The Rust Programming Language

    問題が起きた時にプロラグムをクラッシュさせたいので、expectを使用できるわけです。 エラーから復旧する方法については、第9章で学ぶでしょう。

    -

    println!マクロのプレースホルダーで値を出力する

    +

    println!マクロのプレースホルダーで値を出力する

    閉じ波かっこを除けば、ここまでに追加されたコードのうち議論すべきものは、残り1行であり、それは以下の通りです:

    @@ -1484,7 +1484,7 @@

    The Rust Programming Language

    このコードは、x = 5 and y = 10と出力するでしょう.

    -

    最初の部分をテストする

    +

    最初の部分をテストする

    数当てゲームの最初の部分をテストしてみましょう。cargo runでプログラムを走らせてください:

    $ cargo run
    @@ -1500,7 +1500,7 @@ 

    The Rust Programming Language

    ここまでで、ゲームの最初の部分は完成になります: キーボードからの入力を受け付け、出力できています。

    -

    秘密の数字を生成する

    +

    秘密の数字を生成する

    @@ -1511,7 +1511,7 @@

    The Rust Programming Language

    Rustの標準ライブラリには、乱数機能はまだ含まれていません。ですが、実は、 Rustの開発チームがrandクレートを用意してくれています。

    -

    クレートを使用して機能を追加する

    +

    クレートを使用して機能を追加する

    @@ -1606,7 +1606,7 @@

    The Rust Programming Language

    依存は変更していないので、Cargoは、既にダウンロードしてコンパイルまで済ませてある依存を使用できると検知します。 自分で書いたコードのみ再ビルドをかけるわけです。

    -

    Cargo.lockファイルで再現可能なビルドを保証する

    +

    Cargo.lockファイルで再現可能なビルドを保証する

    @@ -1633,7 +1633,7 @@

    The Rust Programming Language

    このことにより、自動的に再現可能なビルドを構成できるのです。つまり、明示的にアップグレードしない限り、 プロジェクトが使用するバージョンは0.3.14に保たれるのです。Cargo.lockファイルのおかげでね。

    -

    クレートを更新して新バージョンを取得する

    +

    クレートを更新して新バージョンを取得する

    @@ -1681,7 +1681,7 @@

    The Rust Programming Language

    Cargoのおかげでライブラリはとても簡単に再利用ができるので、 Rustaceanは数多くのパッケージから構成された小規模のプロジェクトを書くことができるのです。

    -

    乱数を生成する

    +

    乱数を生成する

    Cargo.tomlrandクレートを追加したので、randクレートを使用開始しましょう。 @@ -1780,7 +1780,7 @@

    The Rust Programming Language

    毎回異なる乱数が出て、その数字はすべて1から100の範囲になるはずです。よくやりました!

    -

    予想と秘密の数字を比較する

    +

    予想と秘密の数字を比較する

    @@ -2029,7 +2029,7 @@

    The Rust Programming Language

    ここまでで大方ゲームはうまく動くようになりましたが、まだユーザは1回しか予想できません。 ループを追加して、その部分を変更しましょう!

    -

    ループで複数回の予想を可能にする

    +

    ループで複数回の予想を可能にする

    loopキーワードは、無限ループを作り出します。これを追加して、ユーザが何回も予想できるようにしましょう:

    @@ -2104,7 +2104,7 @@

    The Rust Programming Language

    quitと入力すれば、実際にゲームを終了できるわけですが、別に他の数字以外の入力でもそうなります。 しかしながら、これは最低限度と言えるでしょう。正しい数字が予想されたら、自動的にゲームが停止してほしいわけです。

    -

    正しい予想をした後に終了する

    +

    正しい予想をした後に終了する

    break文を追加して、ユーザが勝った時にゲームが終了するようにプログラムしましょう:

    @@ -2129,7 +2129,7 @@

    The Rust Programming Language

    プログラムはループを抜けるようになりました。ついでに、ループを抜けることは、プログラムを終了することを意味します。 ループがmain関数の最後の部分だからですね。

    -

    不正な入力を処理する

    +

    不正な入力を処理する

    @@ -2262,7 +2262,7 @@

    The Rust Programming Language

    リスト2-6: 数当てゲームの完全なコード

    -

    まとめ

    +

    まとめ

    ここまでで、数当てゲームの構築に成功しました。おめでとうございます!

    @@ -2281,7 +2281,7 @@

    The Rust Programming Language

    第4章では、所有権について見ます。これにより、Rustは他の言語とかけ離れた存在になっています。 第5章では、構造体とメソッド記法について議論し、第6章ではenumの動作法を説明します。

    -

    一般的なプログラミングの概念

    +

    一般的なプログラミングの概念

    @@ -2306,7 +2306,7 @@

    The Rust Programming Language

    -

    キーワード

    +

    キーワード

    Rust言語にも他の言語同様、キーワードが存在し、これらは言語だけが使用できるようになっています。 これらの単語は、変数や関数名には使えないことを弁えておいてください。ほとんどのキーワードは、特別な意味を持っており、 自らのRustプログラムにおいて、様々な作業をこなすために使用することができます; @@ -2314,7 +2314,7 @@

    The Rust Programming Language

    キーワードの一覧は、付録Aで確認できます。

    -

    変数と可変性

    +

    変数と可変性

    @@ -2434,7 +2434,7 @@

    The Rust Programming Language

    小規模なデータ構造なら、新規インスタンスを生成して、もっと関数型っぽいコードを書く方が通して考えやすくなるため、 低パフォーマンスは、その簡潔性を得るのに足りうるペナルティになるかもしれません。

    -

    変数と定数(constants)の違い

    +

    変数と定数(constants)の違い

    @@ -2487,7 +2487,7 @@

    The Rust Programming Language

    コードの将来的な管理者にとって値の意味を汲むのに役に立ちます。将来、ハードコードされた値を変える必要が出た時に、 たった1箇所を変更するだけで済むようにもしてくれます。

    -

    シャドーイング

    +

    シャドーイング

    @@ -2580,7 +2580,7 @@

    The Rust Programming Language

    さあ、変数が動作する方法を見てきたので、今度は変数が取りうるデータ型について見ていきましょう。

    -

    データ型

    +

    データ型

    @@ -2621,7 +2621,7 @@

    The Rust Programming Language

    他のデータ型についても、様々な型注釈を目にすることになるでしょう。

    -

    スカラー型

    +

    スカラー型

    @@ -2629,7 +2629,7 @@

    The Rust Programming Language

    整数、浮動小数点数、論理値、最後に文字です。他のプログラミング言語でも、これらの型を見かけたことはあるでしょう。 Rustでの動作方法に飛び込みましょう。

    -

    整数型

    +

    整数型

    @@ -2718,7 +2718,7 @@

    The Rust Programming Language

    Rustの基準型は一般的にいい選択肢になります。整数型の基準はi32型です: 64ビットシステム上でも、 この型が普通最速になります。isizeusizeを使う主な状況は、何らかのコレクションにアクセスすることです。

    -

    浮動小数点型

    +

    浮動小数点型

    @@ -2742,7 +2742,7 @@

    The Rust Programming Language

    浮動小数点数は、IEEE-754規格に従って表現されています。f32が単精度浮動小数点数、 f64が倍精度浮動小数点数です。

    -

    数値演算

    +

    数値演算

    @@ -2791,7 +2791,7 @@

    The Rust Programming Language

    これらの文の各式は、数学演算子を使用しており、一つの値に評価され、そして、変数に束縛されます。 付録BにRustで使える演算子の一覧が載っています。

    -

    論理値型

    +

    論理値型

    @@ -2818,7 +2818,7 @@

    The Rust Programming Language

    論理値を使う主な手段は、条件式です。例えば、if式などですね。if式のRustでの動作方法については、 「制御フロー」節で講義します。

    -

    文字型

    +

    文字型

    @@ -2848,13 +2848,13 @@

    The Rust Programming Language

    ところが、「文字」は実はユニコードの概念ではないので、文字とは何かという人間としての直観は、 Rustにおけるchar値が何かとは合致しない可能性があります。この話題については、第8章の「文字列」で詳しく議論しましょう。

    -

    複合型

    +

    複合型

    複合型により、複数の値を一つの型にまとめることができます。Rustには、 2種類の基本的な複合型があります: タプルと配列です。

    -

    タプル型

    +

    タプル型

    タプルは、複数の型の何らかの値を一つの複合型にまとめ上げる一般的な手段です。

    @@ -2917,7 +2917,7 @@

    The Rust Programming Language

    このプログラムは、新しいタプルxを作成し、添え字アクセスで各要素に対して新しい変数も作成しています。 多くのプログラミング言語同様、タプルの最初の添え字は0です。

    -

    配列型

    +

    配列型

    @@ -2959,7 +2959,7 @@

    The Rust Programming Language

    "August", "September", "October", "November", "December"]; #}
    -
    配列の要素にアクセスする
    +
    配列の要素にアクセスする

    配列は、スタック上に確保される一塊のメモリです。添え字によって、 @@ -2979,7 +2979,7 @@

    The Rust Programming Language

    この例では、firstという名前の変数には1という値が格納されます。配列の[0]番目にある値が、 それだからですね。secondという名前の変数には、配列の[1]番目の値2が格納されます。

    -
    配列要素への無効なアクセス
    +
    配列要素への無効なアクセス
    @@ -3027,7 +3027,7 @@

    The Rust Programming Language

    Rustでは、メモリアクセスを許可し、処理を継続する代わりに即座にプログラムを終了することで、 この種のエラーからプログラマを保護しています。Rustのエラー処理については、第9章でもっと議論します。

    -

    関数

    +

    関数

    @@ -3088,7 +3088,7 @@

    The Rust Programming Language

    行出力は、main関数内に書かれた順序で実行されています。最初に"Hello, world"メッセージが出て、 それからanother_functionが呼ばれて、こちらのメッセージが出力されています。

    -

    関数の引数

    +

    関数の引数

    @@ -3175,7 +3175,7 @@

    The Rust Programming Language

    この値で出力されました。

    -

    関数本体は、文と式を含む

    +

    関数本体は、文と式を含む

    @@ -3283,7 +3283,7 @@

    The Rust Programming Language

    式は終端にセミコロンを含みません。式の終端にセミコロンを付けたら、文に変えてしまいます。そして、文は値を返しません。 次に関数の戻り値や式を見ていく際にこのことを肝に銘じておいてください。

    -

    戻り値のある関数

    +

    戻り値のある関数

    @@ -3399,7 +3399,7 @@

    The Rust Programming Language

    結果としてエラーになるわけです。この出力内で、コンパイラは問題を修正する手助けになりそうなメッセージも出していますね: セミコロンを削除するよう提言しています。そして、そうすれば、エラーは直るわけです。

    -

    コメント

    +

    コメント

    @@ -3449,7 +3449,7 @@

    The Rust Programming Language

    Rustには他の種類のコメント、ドキュメントコメントもあり、それについては第14章で議論します。

    -

    フロー制御

    +

    フロー制御

    @@ -3459,7 +3459,7 @@

    The Rust Programming Language

    条件が真の間繰り返しコードを走らせるか決定したりすることは、多くのプログラミング言語において、基本的な構成ブロックです。 Rustコードの実行フローを制御する最も一般的な文法要素は、if式とループです。

    -

    if

    +

    if

    @@ -3576,7 +3576,7 @@

    The Rust Programming Language

    このコードを実行したら、number was something other than zeroと表示されるでしょう。

    -

    else ifで複数の条件を扱う

    +

    else ifで複数の条件を扱う

    ifelseを組み合わせてelse if式にすることで複数の条件を持たせることもできます。例です:

    @@ -3627,7 +3627,7 @@

    The Rust Programming Language

    コードをリファクタリングしたくなるかもしれません。これらのケースに有用なmatchと呼ばれる、 強力なRustの枝分かれ文法要素については第6章で解説します。

    -

    let文内でif式を使う

    +

    let文内でif式を使う

    ifは式なので、let文の右辺に持ってくることができます。リスト3-2のようにですね。

    @@ -3719,7 +3719,7 @@

    The Rust Programming Language

    どの変数に対しても、架空の複数の型があることを追いかけなければならないのであれば、コンパイラはより複雑になり、 コードに対して行える保証が少なくなってしまうでしょう。

    -

    ループでの繰り返し

    +

    ループでの繰り返し

    @@ -3731,7 +3731,7 @@

    The Rust Programming Language

    Rustには3種類のループが存在します: loopwhileforです。それぞれ試してみましょう。

    -

    loopでコードを繰り返す

    +

    loopでコードを繰り返す

    loopキーワードを使用すると、同じコードを何回も何回も永遠に、明示的にやめさせるまで実行します。

    @@ -3778,7 +3778,7 @@

    The Rust Programming Language

    第2章の「正しい予想をした後に終了する」節の数当てゲーム内でこれをして、ユーザが予想を的中させ、 ゲームに勝った時にプログラムを終了させたことを思い出してください。

    -

    whileで条件付きループ

    +

    whileで条件付きループ

    @@ -3819,7 +3819,7 @@

    The Rust Programming Language

    この文法要素により、loopifelsebreakを使った時に必要になるネストがなくなり、 より明確になります。条件が真の間、コードは実行されます; そうでなければ、ループを抜けます.

    -

    forでコレクションを覗き見る

    +

    forでコレクションを覗き見る

    while要素を使って配列などのコレクションの要素を覗き見ることができます。例えば、リスト3-4を見ましょう。

    @@ -3928,7 +3928,7 @@

    The Rust Programming Language

    こちらのコードの方が少しいいでしょう?

    -

    まとめ

    +

    まとめ

    @@ -3949,7 +3949,7 @@

    The Rust Programming Language

    次に進む準備ができたら、他の言語にはあまり存在しないRustの概念について話しましょう: 所有権です。

    -

    所有権を理解する

    +

    所有権を理解する

    @@ -3959,7 +3959,7 @@

    The Rust Programming Language

    故に、Rustにおいて、所有権がどう動作するのかを理解するのは重要です。この章では、所有権以外にも、関連する機能を いくつか話していきます: 借用、スライス、そして、コンパイラがデータをメモリにどう配置するかです。

    -

    所有権とは?

    +

    所有権とは?

    Rustの中心的な機能は、所有権です。この機能は、説明するのは簡単なのですが、言語の残りの機能全てにかかるほど @@ -4044,7 +4044,7 @@

    The Rust Programming Language

    -

    スタックとヒープ

    +

    スタックとヒープ

    多くのプログラミング言語において、スタックとヒープについて考える機会はそう多くないでしょう。 しかし、Rustのようなシステムプログラミング言語においては、値がスタックに積まれるかヒープに置かれるかは、 言語の振る舞い方や、特定の決断を下す理由などに影響以上のものを与えるのです。 @@ -4087,7 +4087,7 @@

    The Rust Programming Language

    -

    所有権規則

    +

    所有権規則

    まず、所有権のルールについて見ていきましょう。 @@ -4101,7 +4101,7 @@

    The Rust Programming Language

  • 所有者がスコープから外れたら、値は破棄される。
  • -

    変数スコープ

    +

    変数スコープ

    @@ -4161,7 +4161,7 @@

    The Rust Programming Language

    ここで、スコープと変数が有効になる期間の関係は、他の言語に類似しています。さて、この理解のもとに、 String型を導入して構築していきましょう。

    -

    String

    +

    String

    @@ -4228,7 +4228,7 @@

    The Rust Programming Language

    では、ここでの違いは何でしょうか?なぜ、String型は可変化できるのに、リテラルはできないのでしょうか? 違いは、これら二つの型がメモリを扱う方法にあります。

    -

    メモリと確保

    +

    メモリと確保

    @@ -4320,7 +4320,7 @@

    The Rust Programming Language

    ヒープ上に確保されたデータを複数の変数に使用させるようなもっと複雑な場面では、コードの振る舞いは、 予期しないものになる可能性もあります。これから、そのような場面を掘り下げてみましょう。

    -

    変数とデータの相互作用法: ムーブ

    +

    変数とデータの相互作用法: ムーブ

    Rustにおいては、複数の変数が同じデータに対して異なる手段で相互作用することができます。 @@ -4477,7 +4477,7 @@

    The Rust Programming Language

    自動的にデータの"deep copy"が行われることは絶対にないわけです。それ故に、あらゆる自動コピーは、実行時性能の観点で言うと、 悪くないと考えてよいことになります。

    -

    変数とデータの相互作用法: クローン

    +

    変数とデータの相互作用法: クローン

    @@ -4505,7 +4505,7 @@

    The Rust Programming Language

    cloneメソッドの呼び出しを見かけたら、何らかの任意のコードが実行され、その実行コストは高いと把握できます。 何か違うことが起こっているなと見た目でわかるわけです。

    -

    スタックのみのデータ: コピー

    +

    スタックのみのデータ: コピー

    まだ話題にしていない別の問題があります。 @@ -4570,7 +4570,7 @@

    The Rust Programming Language

    (i32, String)は違う。 -

    所有権と関数

    +

    所有権と関数

    @@ -4634,7 +4634,7 @@

    The Rust Programming Language

    これらの静的チェックにより、ミスを犯さないでいられます。sxを使用するコードをmainに追加してみて、 どこで使えて、そして、所有権規則により、どこで使えないかを確認してください。

    -

    戻り値とスコープ

    +

    戻り値とスコープ

    値を返すことでも、所有権は移動します。リスト4-4は、リスト4-3と似た注釈のついた例です。

    @@ -4747,7 +4747,7 @@

    The Rust Programming Language

    でも、これでは、大袈裟すぎますし、ありふれているはずの概念に対して、作業量が多すぎます。 私たちにとって幸運なことに、Rustにはこの概念に対する機能があり、参照と呼ばれます。

    -

    参照と借用

    +

    参照と借用

    @@ -4878,7 +4878,7 @@

    The Rust Programming Language

    変数が標準で不変なのと全く同様に、参照も不変なのです。参照している何かを変更することは叶わないわけです。

    -

    可変な参照

    +

    可変な参照

    一捻り加えるだけでリスト4-6のコードのエラーは解決します:

    @@ -5016,7 +5016,7 @@

    The Rust Programming Language

    これらのエラーは、時としてイライラするものではありますが、Rustコンパイラがバグの可能性を早期に指摘してくれ(それも実行時ではなくコンパイル時に)、 問題の発生箇所をズバリ示してくれるのだと覚えておいてください。そうして想定通りにデータが変わらない理由を追いかける必要がなくなります。

    -

    宙に浮いた参照

    +

    宙に浮いた参照

    @@ -5110,7 +5110,7 @@

    The Rust Programming Language

    これは何の問題もなく動きます。所有権はムーブされ、何も解放されることはありません。

    -

    参照の規則

    +

    参照の規則

    参照について議論したことを再確認しましょう:

    @@ -5123,7 +5123,7 @@

    The Rust Programming Language

    次は、違う種類の参照を見ていきましょう: スライスです。

    -

    スライス型

    +

    スライス型

    @@ -5292,7 +5292,7 @@

    The Rust Programming Language

    運のいいことに、Rustにはこの問題への解決策が用意されています: 文字列スライスです。

    -

    文字列スライス

    +

    文字列スライス

    文字列スライスとは、Stringの一部への参照で、こんな見た目をしています:

    
    @@ -5478,7 +5478,7 @@ 

    The Rust Programming Language

    clearStringを切り詰める必要があるので、可変な参照を得ようとして失敗しているわけです。 RustのおかげでAPIが使いやすくなるだけでなく、ある種のエラー全てを完全にコンパイル時に排除してくれるのです!

    -

    文字列リテラルはスライスである

    +

    文字列リテラルはスライスである

    文字列は、バイナリに埋め込まれると話したことを思い出してください。今やスライスのことを知ったので、 @@ -5494,7 +5494,7 @@

    The Rust Programming Language

    ここでのsの型は、&strです: バイナリのその特定の位置を指すスライスです。 これは、文字列が不変である理由にもなっています。要するに、&strは不変な参照なのです。

    -

    引数としての文字列スライス

    +

    引数としての文字列スライス

    リテラルやString値のスライスを得ることができると知ると、first_wordに対して、もう一つ改善点を見出すことができます。 @@ -5571,7 +5571,7 @@

    The Rust Programming Language

    }
    -

    他のスライス

    +

    他のスライス

    文字列リテラルは、ご想像通り、文字列に特化したものです。ですが、もっと一般的なスライス型も存在します。 @@ -5601,7 +5601,7 @@

    The Rust Programming Language

    この種のスライスは使用するでしょう。これらのコレクションについて詳しくは、 第8章でベクタについて話すときに議論します。

    -

    まとめ

    +

    まとめ

    @@ -5617,7 +5617,7 @@

    The Rust Programming Language

    所有権は、Rustの他のいろんな部分が動作する方法に影響を与えるので、これ以降もこれらの概念についてさらに語っていく予定です。 第5章に移って、structでデータをグループ化することについて見ていきましょう。

    -

    構造体を使用して関係のあるデータを構造化する

    +

    構造体を使用して関係のあるデータを構造化する

    @@ -5633,7 +5633,7 @@

    The Rust Programming Language

    構造体のデータに紐付く振る舞いを指定する方法について議論します。構造体とenum(第6章で議論します)は、 自分のプログラム領域で新しい型を定義し、Rustのコンパイル時型精査機能をフル活用する構成要素になります。

    -

    構造体を定義し、インスタンス化する

    +

    構造体を定義し、インスタンス化する

    @@ -5773,7 +5773,7 @@

    The Rust Programming Language

    構造体にもっとフィールドがあれば、名前を繰り返すことはさらに煩わしくなるでしょう。 幸運なことに、便利な省略記法があります!

    -

    フィールドと変数が同名の時にフィールド初期化省略記法を使う

    +

    フィールドと変数が同名の時にフィールド初期化省略記法を使う

    @@ -5814,7 +5814,7 @@

    The Rust Programming Language

    emailフィールドとemail引数は同じ名前なので、email: emailと書くのではなく、 emailと書くだけで済むのです。

    -

    構造体更新記法で他のインスタンスからインスタンスを生成する

    +

    構造体更新記法で他のインスタンスからインスタンスを生成する

    多くは前のインスタンスの値を使用しつつ、変更する箇所もある形で新しいインスタンスを生成できるとしばしば有用です。 @@ -5890,7 +5890,7 @@

    The Rust Programming Language

    リスト5-7のコードも、emailusernameについては異なる値、activesign_in_countフィールドについては、 user1と同じ値になるインスタンスをuser2に生成します。

    -

    異なる型を生成する名前付きフィールドのないタプル構造体を使用する

    +

    異なる型を生成する名前付きフィールドのないタプル構造体を使用する

    @@ -5930,7 +5930,7 @@

    The Rust Programming Language

    3つのi32値からできていてもです。それ以外については、タプル構造体のインスタンスは、 タプルと同じように振る舞います: 分配して個々の部品にしたり、.と添え字を使用して個々の値にアクセスするなどです。

    -

    フィールドのないユニット(よう)構造体

    +

    フィールドのないユニット(よう)構造体

    @@ -5992,7 +5992,7 @@

    The Rust Programming Language

    -

    構造体データの所有権

    +

    構造体データの所有権

    リスト5-1のUser構造体定義において、&str文字列スライス型ではなく、所有権のあるString型を使用しました。 これは意図的な選択です。というのも、この構造体のインスタンスには全データを所有してもらう必要があり、 このデータは、構造体全体が有効な間はずっと有効である必要があるのです。

    @@ -6036,7 +6036,7 @@

    The Rust Programming Language

    当面、今回のようなエラーは、&strのような参照の代わりに、Stringのような所有された型を使うことで修正します。

    -

    構造体を使ったプログラム例

    +

    構造体を使ったプログラム例

    @@ -6075,7 +6075,7 @@

    The Rust Programming Language

    (四角形の面積は、1500平方ピクセルです)
    -

    タプルでリファクタリングする

    +

    タプルでリファクタリングする

    @@ -6096,7 +6096,7 @@

    The Rust Programming Language

    幅と高さを一緒にグループ化する方が、より読みやすく、扱いやすくなるでしょう。 それをする一つの方法については、第3章の「タプル型」節ですでに議論しました: タプルを使うのです。

    -

    タプルでリファクタリングする

    +

    タプルでリファクタリングする

    リスト5-9は、タプルを使う別バージョンのプログラムを示しています。

    @@ -6137,7 +6137,7 @@

    The Rust Programming Language

    容易く、このことを忘れたり、これらの値を混ぜこぜにしたりしてエラーを発生させてしまうでしょう。 データの意味をコードに載せていないからです。

    -

    構造体でリファクタリングする: より意味付けする

    +

    構造体でリファクタリングする: より意味付けする

    @@ -6192,7 +6192,7 @@

    The Rust Programming Language

    Rectangleの面積を計算します。これにより、幅と高さが相互に関係していることが伝わり、 タプルの01という添え字を使うよりも、これらの値に説明的な名前を与えられるのです。プログラムの意図が明瞭になりました。

    -

    トレイトの導出で有用な機能を追加する

    +

    トレイトの導出で有用な機能を追加する

    @@ -6326,7 +6326,7 @@

    The Rust Programming Language

    area関数をRectangle型に定義されたareaメソッドに変形することで、 このコードをリファクタリングし続けられる方法について見ていきましょう。

    -

    メソッド記法

    +

    メソッド記法

    @@ -6339,7 +6339,7 @@

    The Rust Programming Language

    メソッドは構造体の文脈(あるいはenumかトレイトオブジェクトの。これらについては各々第6章と17章で解説します)で定義されるという点で、 関数とは異なり、最初の引数は必ずselfになり、これはメソッドが呼び出されている構造体インスタンスを表します。

    -

    メソッドを定義する

    +

    メソッドを定義する

    @@ -6463,7 +6463,7 @@

    The Rust Programming Language

    -

    ->演算子はどこに行ったの?

    +

    ->演算子はどこに行ったの?

    CとC++では、メソッド呼び出しには2種類の異なる演算子が使用されます: オブジェクトに対して直接メソッドを呼び出すのなら、.を使用するし、オブジェクトのポインタに対してメソッドを呼び出し、 先にポインタを参照外しする必要があるなら、->を使用するわけです。 @@ -6502,7 +6502,7 @@

    The Rust Programming Language

    所有権を実際に使うのがRustにおいて簡単である大きな理由です。

    -

    より引数の多いメソッド

    +

    より引数の多いメソッド

    @@ -6591,7 +6591,7 @@

    The Rust Programming Language

    メソッドは、self引数の後にシグニチャに追加した引数を複数取ることができ、 その引数は、関数の引数と同様に動作するのです。

    -

    関連関数

    +

    関連関数

    @@ -6634,7 +6634,7 @@

    The Rust Programming Language

    この関数は、構造体によって名前空間分けされています: ::という記法は、関連関数とモジュールによって作り出される名前空間両方に使用されます。 モジュールについては第7章で議論します。

    -

    複数のimplブロック

    +

    複数のimplブロック

    @@ -6670,7 +6670,7 @@

    The Rust Programming Language

    ここでこれらのメソッドを個々のimplブロックに分ける理由はないのですが、合法な書き方です。 複数のimplブロックが有用になるケースは第10章で見ますが、そこではジェネリック型と、トレイトについて議論します。

    -

    まとめ

    +

    まとめ

    @@ -6686,7 +6686,7 @@

    The Rust Programming Language

    しかし、構造体だけが独自の型を作成する手段ではありません: Rustのenum機能に目を向けて、 別の道具を道具箱に追加しましょう。

    -

    Enumとパターンマッチング

    +

    Enumとパターンマッチング

    @@ -6709,7 +6709,7 @@

    The Rust Programming Language

    enumは多くの言語に存在する機能ですが、その能力は言語ごとに異なります。Rustのenumは、F#、OCaml、Haskellなどの、 関数型言語に存在する代数的データ型に最も酷似しています。

    -

    Enumを定義する

    +

    Enumを定義する

    @@ -6747,7 +6747,7 @@

    The Rust Programming Language

    これで、IpAddrKindはコードの他の場所で使用できる独自のデータ型になります。

    -

    Enumの値

    +

    Enumの値

    以下のようにして、IpAddrKindの各列挙子のインスタンスは生成できます:

    
    @@ -7039,7 +7039,7 @@ 

    The Rust Programming Language

    非常に一般的で有用な別の標準ライブラリのenumを見てみましょう: Optionです。

    -

    Option enumとNull値に勝る利点

    +

    Option enumとNull値に勝る利点

    @@ -7224,7 +7224,7 @@

    The Rust Programming Language

    match式が、enumとともに使用した時にこれだけの動作をするフロー制御文法要素になります: enumの列挙子によって、違うコードが走り、そのコードがマッチした値の中のデータを使用できるのです。

    -

    matchフロー制御演算子

    +

    matchフロー制御演算子

    @@ -7333,7 +7333,7 @@

    The Rust Programming Language

    } #}
    -

    値に束縛されるパターン

    +

    値に束縛されるパターン

    @@ -7437,7 +7437,7 @@

    The Rust Programming Language

    UsState::Alaskaという値です。そして、println!式でその束縛を使用することができ、 そのため、Coin enumの列挙子からQuarterに対する中身のstateの値を取得できたわけです。

    -

    Option<T>とのマッチ

    +

    Option<T>とのマッチ

    @@ -7513,7 +7513,7 @@

    The Rust Programming Language

    一旦慣れてしまえば、全ての言語にあってほしいと願うことになるでしょう。一貫してユーザのお気に入りなのです。

    -

    マッチは包括的

    +

    マッチは包括的

    もう一つ議論する必要のあるmatchの観点があります。一点バグがありコンパイルできないこんなバージョンのplus_one関数を考えてください:

    @@ -7547,7 +7547,7 @@

    The Rust Programming Language

    nullになるかもしれないのに値があると思い込まないよう、すなわち前に議論した10億ドルの失敗を犯さないよう、 コンパイラが保護してくれるわけです。

    -

    _というプレースホルダー

    +

    _というプレースホルダー

    @@ -7582,7 +7582,7 @@

    The Rust Programming Language

    ですが、一つのケースにしか興味がないような場面では、match式はちょっと長ったらしすぎます。 このような場面用に、Rustには、if letが用意されています。

    -

    if letで簡潔なフロー制御

    +

    if letで簡潔なフロー制御

    @@ -7702,7 +7702,7 @@

    The Rust Programming Language

    matchを使って表現するには冗長的すぎるロジックがプログラムにあるようなシチュエーションに遭遇したら、 if letもRust道具箱にあることを思い出してください。

    -

    まとめ

    +

    まとめ

    @@ -7724,7 +7724,7 @@

    The Rust Programming Language

    使用するのに率直な整理整頓されたAPIをユーザに提供し、ユーザが必要とするものだけを公開するために、 今度は、Rustのモジュールに目を向けてみましょう。

    -

    モジュールを使用してコードを体系化し、再利用する

    +

    モジュールを使用してコードを体系化し、再利用する

    @@ -7761,7 +7761,7 @@

    The Rust Programming Language

    この各部品を見て、それらが全体にどうはまり込むかを理解します。

    -

    modとファイルシステム

    +

    modとファイルシステム

    @@ -7816,7 +7816,7 @@

    The Rust Programming Language

    コードの意図によって、いろんなシチュエーションで最適になるライブラリコードを体系化する別のオプションをお目にかけます。

    -

    モジュール定義

    +

    モジュール定義

    @@ -7948,7 +7948,7 @@

    The Rust Programming Language

    こちらで示したテクニックを使用して、並列したモジュールや、ネストしたモジュールなど、どんな構造のモジュールでも、 作成してください。

    -

    モジュールを別ファイルに移す

    +

    モジュールを別ファイルに移す

    @@ -8283,7 +8283,7 @@

    The Rust Programming Language

    network::clientというサブモジュールは専用のsrc/network/client.rsファイルを持てるわけです。 これで、頂点にあるsrc/client.rsは間違いなく、clientモジュールに属するコードになるわけです。

    -

    モジュールファイルシステムの規則

    +

    モジュールファイルシステムの規則

    ファイルに関するモジュール規則をまとめましょう:

    @@ -8316,7 +8316,7 @@

    The Rust Programming Language

    次は、pubキーワードについて話し、警告を取り除きます!

    -

    pubで公開するか制御する

    +

    pubで公開するか制御する

    @@ -8437,7 +8437,7 @@

    The Rust Programming Language

    関数が「使用されている」という架空の外部使用の可能性を考慮してくれます。それ故に、関数が公開とマークされれば、 コンパイラはそれが自分のプログラムで使用されるべきという要求をなくし、その関数が未使用という警告も止めるのです。

    -

    関数を公開にする

    +

    関数を公開にする

    @@ -8566,7 +8566,7 @@

    The Rust Programming Language

    残る警告は1つなので、自分で解消してみてください!

    -

    プライバシー規則

    +

    プライバシー規則

    まとめると、要素の公開性は以下のようなルールになります:

    @@ -8577,7 +8577,7 @@

    The Rust Programming Language

  • 要素が非公開なら、直接の親モジュールとその親の子モジュールのみアクセスできます。
  • -

    プライバシー例

    +

    プライバシー例

    @@ -8613,7 +8613,7 @@

    The Rust Programming Language

    このコードをコンパイルする前に、try_me関数のどの行がエラーになるか当ててみてください。 それからコンパイルを試して、合ってたかどうか確かめ、エラーの議論を求めて読み進めてください!

    -

    エラーを確かめる

    +

    エラーを確かめる

    @@ -8642,7 +8642,7 @@

    The Rust Programming Language

    insideという名前のモジュールは非公開で子モジュールを持たないので、現在のモジュールであるoutermostからのみアクセスできます。 つまり、try_me関数は、outermost::inside::inner_functionoutermost::inside::secret_functionも呼び出すことを許されないのです。

    -

    エラーを修正する

    +

    エラーを修正する

    @@ -8665,7 +8665,7 @@

    The Rust Programming Language

    今度は、useキーワードで要素をスコープに導入する話をしましょう。

    -

    異なるモジュールの名前を参照する

    +

    異なるモジュールの名前を参照する

    @@ -8693,7 +8693,7 @@

    The Rust Programming Language

    見てお分かりの通り、フルパス指定した名前を参照すると非常に長ったらしくなります。 幸い、Rustには、これらの呼び出しをもっと簡潔にするキーワードが用意されています。

    -

    useキーワードで名前をスコープに導入する

    +

    useキーワードで名前をスコープに導入する

    @@ -8770,7 +8770,7 @@

    The Rust Programming Language

    Greenuse文に含んでいないので、まだGreenバリアント用にTrafficLight名前空間を指定しています。

    -

    Globで全ての名前をスコープに導入する

    +

    Globで全ての名前をスコープに導入する

    @@ -8798,7 +8798,7 @@

    The Rust Programming Language

    あまりglobは使用するべきではありません: 便利ではありますが、globは予想以上の要素を引き込んで、 名前衝突を引き起こす可能性があるのです。

    -

    superを使用して親モジュールにアクセスする

    +

    superを使用して親モジュールにアクセスする

    @@ -8938,7 +8938,7 @@

    The Rust Programming Language

    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
    -

    まとめ

    +

    まとめ

    @@ -8948,7 +8948,7 @@

    The Rust Programming Language

    次は、自分の素晴らしく綺麗なコードで使用できる標準ライブラリのコレクションデータ構造について見ていきましょう。

    -

    一般的なコレクション

    +

    一般的なコレクション

    @@ -8985,7 +8985,7 @@

    The Rust Programming Language

    ベクタ型、文字列、ハッシュマップの生成と更新方法や、各々が特別な点について議論していきましょう。

    -

    ベクタで一連の値を保持する

    +

    ベクタで一連の値を保持する

    @@ -8996,7 +8996,7 @@

    The Rust Programming Language

    ベクタには、同じ型の値しか保持できません。要素のリストがある場合に有用です。 例えば、テキストファイルの各行とか、ショッピングカートのアイテムの価格などです。

    -

    新しいベクタを生成する

    +

    新しいベクタを生成する

    新しい空のベクタを作るには、リスト8-1に示されたように、Vec::new関数を呼べばよいです。

    @@ -9046,7 +9046,7 @@

    The Rust Programming Language

    初期値のi32値を与えたので、コンパイラは、vの型がVec<i32>であると推論でき、型注釈は必要なくなりました。 次は、ベクタを変更する方法を見ましょう。

    -

    ベクタを更新する

    +

    ベクタを更新する

    ベクタを生成し、それから要素を追加するには、リスト8-3に示したように、pushメソッドを使用できます。

    @@ -9071,7 +9071,7 @@

    The Rust Programming Language

    中に配置する数値は全てi32型であり、コンパイラはこのことをデータから推論するので、 Vec<i32>という注釈は必要なくなります。

    -

    ベクタをドロップすれば、要素もドロップする

    +

    ベクタをドロップすれば、要素もドロップする

    他のあらゆる構造体同様、ベクタもスコープを抜ければ、解放されます。リスト8-4に注釈したようにですね。

    @@ -9102,7 +9102,7 @@

    The Rust Programming Language

    片付けられるということです。これは一見単純な点に見えるかもしれませんが、ベクタの要素への参照を導入した途端、 もうちょっと複雑になる可能性を秘めています。次は、それに挑んでいきましょう!

    -

    ベクタの要素を読む

    +

    ベクタの要素を読む

    @@ -9237,7 +9237,7 @@

    The Rust Programming Language

    訳注: 日本語版のThe Rustonomiconはこちらです。

    -

    ベクタの値を走査する

    +

    ベクタの値を走査する

    @@ -9277,7 +9277,7 @@

    The Rust Programming Language

    可変参照が参照している値を変更するには、+=演算子を使用する前に、 参照外し演算子(*)を使用してiの値に辿り着かないといけません。

    -

    Enumを使って複数の型を保持する

    +

    Enumを使って複数の型を保持する

    @@ -9344,7 +9344,7 @@

    The Rust Programming Language

    APIドキュメントを確認することを心得てください。例として、pushに加えて、popメソッドは最後の要素を削除して返します。 次のコレクション型に移りましょう: Stringです!

    -

    文字列でUTF-8でエンコードされたテキストを保持する

    +

    文字列でUTF-8でエンコードされたテキストを保持する

    @@ -9368,7 +9368,7 @@

    The Rust Programming Language

    また、Stringが他のコレクションと異なる点についても議論します。具体的には、人間とコンピュータがStringデータを解釈する方法の差異により、 Stringに添え字アクセスする方法がどう複雑なのかということです。

    -

    文字列とは?

    +

    文字列とは?

    @@ -9405,7 +9405,7 @@

    The Rust Programming Language

    メモリ上の表現が異なったりします。この章では、これらの他の種類の文字列については議論しません; 使用方法やどれが最適かについては、APIドキュメントを参照してください。

    -

    新規文字列を生成する

    +

    新規文字列を生成する

    @@ -9487,14 +9487,14 @@

    The Rust Programming Language

    これらは全て、有効なStringの値です。

    -

    文字列を更新する

    +

    文字列を更新する

    Stringは、サイズを伸ばすことができ、Vec<T>の中身のように、追加のデータをプッシュすれば、中身も変化します。 付け加えると、String値を連結する+演算子や、format!マクロを便利に使用することができます。

    -

    push_strpushで文字列に追加する

    +

    push_strpushで文字列に追加する

    push_strメソッドで文字列スライスを追記することで、Stringを伸ばすことができます。 @@ -9551,7 +9551,7 @@

    The Rust Programming Language

    日本語のwwwみたいなものですね。

    -

    +演算子、またはformat!マクロで連結

    +

    +演算子、またはformat!マクロで連結

    2つのすでにある文字列を組み合わせたくなることがよくあります。リスト8-18に示したように、 @@ -9658,7 +9658,7 @@

    The Rust Programming Language

    出力をスクリーンに行う代わりに、中身をStringで返すのです。format!を使用したコードの方がはるかに読みやすく、 引数の所有権を奪いません。

    -

    文字列に添え字アクセスする

    +

    文字列に添え字アクセスする

    @@ -9689,7 +9689,7 @@

    The Rust Programming Language

    エラーと注釈が全てを物語っています: Rustの文字列は、添え字アクセスをサポートしていないのです。 でも、なぜでしょうか?その疑問に答えるには、Rustがメモリにどのように文字列を保持しているかについて議論する必要があります。

    -

    内部表現

    +

    内部表現

    StringVec<u8>のラッパです。リスト8-14から適切にUTF-8でエンコードされた文字列の例をご覧ください。 @@ -9743,7 +9743,7 @@

    The Rust Programming Language

    予期しない値を返し、すぐには判明しないバグを引き起こさないために、Rustはこのコードを全くコンパイルせず、 開発過程の早い段階で誤解を防いでくれるのです。

    -

    バイトとスカラー値と書記素クラスタ!なんてこった!

    +

    バイトとスカラー値と書記素クラスタ!なんてこった!

    @@ -9786,7 +9786,7 @@

    The Rust Programming Language

    しかし、Stringでそのパフォーマンスを保証することはできません。というのも、 合法な文字がいくつあるか決定するのに、最初から添え字まで中身を走査する必要があるからです。

    -

    文字列をスライスする

    +

    文字列をスライスする

    @@ -9822,7 +9822,7 @@

    The Rust Programming Language

    範囲を使用して文字列スライスを作る際にはプログラムをクラッシュさせることがあるので、気をつけるべきです。

    -

    文字列を走査するメソッド群

    +

    文字列を走査するメソッド群

    幸いなことに、他の方法でも文字列の要素にアクセスすることができます。

    @@ -9875,7 +9875,7 @@

    The Rust Programming Language

    書記素クラスタを文字列から得る方法は複雑なので、この機能は標準ライブラリでは提供されていません。 この機能が必要なら、crates.ioでクレートを入手可能です。

    -

    文字列はそう単純じゃない

    +

    文字列はそう単純じゃない

    @@ -9892,7 +9892,7 @@

    The Rust Programming Language

    もう少し複雑でないものに切り替えていきましょう: ハッシュマップです!

    -

    ハッシュマップに値に紐づいたキーを格納する

    +

    ハッシュマップに値に紐づいたキーを格納する

    @@ -9918,7 +9918,7 @@

    The Rust Programming Language

    HashMap<K, V>上に定義された関数に隠されています。いつものように、 もっと情報が欲しければ、標準ライブラリのドキュメントをチェックしてください。

    -

    新規ハッシュマップを生成する

    +

    新規ハッシュマップを生成する

    @@ -9987,7 +9987,7 @@

    The Rust Programming Language

    コンパイラは指定しない限り、どれを所望なのかわからないからです。ところが、キーと値の型引数については、 アンダースコアを使用しており、コンパイラはベクタのデータ型に基づいてハッシュマップが含む型を推論することができるのです。

    -

    ハッシュマップと所有権

    +

    ハッシュマップと所有権

    @@ -10024,7 +10024,7 @@

    The Rust Programming Language

    最低でもハッシュマップが有効な間は、有効でなければなりません。これらの問題について詳細には、 第10章の「ライフタイムで参照を有効化する」節で語ります。

    -

    ハッシュマップの値にアクセスする

    +

    ハッシュマップの値にアクセスする

    リスト8-23に示したように、キーをgetメソッドに提供することで、ハッシュマップから値を取り出すことができます。

    @@ -10075,7 +10075,7 @@

    The Rust Programming Language

    Blue: 10 -

    ハッシュマップを更新する

    +

    ハッシュマップを更新する

    @@ -10090,7 +10090,7 @@

    The Rust Programming Language

    新しい値を無視し、キーにまだ値がない場合に新しい値を追加するだけにすることもできます。 あるいは、古い値と新しい値を組み合わせることもできます。各方法について見ていきましょう!

    -

    値を上書きする

    +

    値を上書きする

    @@ -10118,7 +10118,7 @@

    The Rust Programming Language

    このコードは、{"Blue": 25}と出力するでしょう。10という元の値は上書きされたのです。

    -

    キーに値がなかった時のみ値を挿入する

    +

    キーに値がなかった時のみ値を挿入する

    @@ -10165,7 +10165,7 @@

    The Rust Programming Language

    最初のentry呼び出しは、まだイエローチームに対する値がないので、値50でイエローチームのキーを挿入します。 entryの2回目の呼び出しはハッシュマップを変更しません。なぜなら、ブルーチームには既に10という値があるからです。

    -

    古い値に基づいて値を更新する

    +

    古い値に基づいて値を更新する

    @@ -10207,7 +10207,7 @@

    The Rust Programming Language

    まずアスタリスク(*)でcountを参照外ししなければならないのです。この可変参照は、 forループの終端でスコープを抜けるので、これらの変更は全て安全であり、借用規則により許可されるのです。

    -

    ハッシュ関数

    +

    ハッシュ関数

    @@ -10227,7 +10227,7 @@

    The Rust Programming Language

    必ずしも独自のhasherを1から作り上げる必要はありません; crates.ioには、 他のRustユーザによって共有された多くの一般的なハッシュアルゴリズムを実装したhasherを提供するライブラリがあります。

    -

    まとめ

    +

    まとめ

    @@ -10264,7 +10264,7 @@

    The Rust Programming Language

    処理が失敗することもあるような、より複雑なプログラムに入り込んできています; ということは、 エラーの処理法について議論するのにぴったりということです。次はそれをします!

    -

    エラー処理

    +

    エラー処理

    @@ -10298,7 +10298,7 @@

    The Rust Programming Language

    この章では、まずpanic!の呼び出しを講義し、それからResult<T, E>を戻り値にする話をします。 加えて、エラーからの回復を試みるか、実行を中止するか決定する際に考慮すべき事項についても、探究しましょう。

    -

    panic!で回復不能なエラー

    +

    panic!で回復不能なエラー

    @@ -10326,7 +10326,7 @@

    The Rust Programming Language

    -

    パニックに対してスタックを巻き戻すか異常終了するか

    +

    パニックに対してスタックを巻き戻すか異常終了するか

    標準では、パニックが発生すると、プログラムは巻き戻しを始めます。つまり、言語がスタックを遡り、 遭遇した各関数のデータを片付けるということです。しかし、この遡りと片付けはすべきことが多くなります。 対立案は、即座に異常終了し、片付けをせずにプログラムを終了させることです。そうなると、プログラムが使用していたメモリは、 @@ -10377,7 +10377,7 @@

    The Rust Programming Language

    panic!マクロが呼び出されている他人のコードになるでしょう。panic!呼び出しの発生元である関数のバックトレースを使用して、 問題を起こしている自分のコードの箇所を割り出すことができます。バックトレースがどんなものか、次に議論しましょう。

    -

    panic!バックトレースを使用する

    +

    panic!バックトレースを使用する

    @@ -10534,7 +10534,7 @@

    The Rust Programming Language

    この章の後ほど、「panic!するかpanic!するまいか」節でpanic!とエラー状態を扱うのにpanic!を使うべき時と使わぬべき時に戻ってきます。 次は、Resultを使用してエラーから回復する方法を見ましょう。

    -

    Resultで回復可能なエラー

    +

    Resultで回復可能なエラー

    @@ -10687,7 +10687,7 @@

    The Rust Programming Language

    通常通り、この出力は、一体何がおかしくなったのかを物語っています。

    -

    色々なエラーにマッチする

    +

    色々なエラーにマッチする

    @@ -10776,7 +10776,7 @@

    The Rust Programming Language

    内部にもmatch式を追加する必要があるのです。ファイルが開けないなら、異なるエラーメッセージが出力されるでしょう。 外側のmatchの最後のアームは同じままなので、ファイルが存在しないエラー以外ならプログラムはパニックします。

    -

    エラー時にパニックするショートカット: unwrapexpect

    +

    エラー時にパニックするショートカット: unwrapexpect

    @@ -10843,7 +10843,7 @@

    The Rust Programming Language

    ズバリどのunwrapがパニックを引き起こしているのか理解するのは、より時間がかかる可能性があります。 パニックするunwrap呼び出しは全て、同じメッセージを出力するからです。

    -

    エラーを委譲する

    +

    エラーを委譲する

    @@ -10951,7 +10951,7 @@

    The Rust Programming Language

    Rustにおいて、この種のエラー委譲は非常に一般的なので、Rustにはこれをしやすくする?演算子が用意されています。

    -

    エラー委譲のショートカット: ?演算子

    +

    エラー委譲のショートカット: ?演算子

    @@ -11050,7 +11050,7 @@

    The Rust Programming Language

    エラーを返すというよりもそれでも、sにユーザ名を含むOk値を返します。機能もまたリスト9-6及び、9-7と同じです; ただ単に異なるバージョンのよりエルゴノミックな書き方なのです。

    -

    ?演算子は、Resultを返す関数でしか使用できない

    +

    ?演算子は、Resultを返す関数でしか使用できない

    @@ -11101,7 +11101,7 @@

    The Rust Programming Language

    さて、panic!呼び出しやResultを返す詳細について議論し終えたので、 どんな場合にどちらを使うのが適切か決める方法についての話に戻りましょう。

    -

    panic!すべきかするまいか

    +

    panic!すべきかするまいか

    @@ -11131,7 +11131,7 @@

    The Rust Programming Language

    それからコンパイラではありえない失敗だと気づけなくとも、人間なら気づける場面を議論しましょう。 そして、ライブラリコードでパニックするか決定する方法についての一般的なガイドラインで結論づけましょう。

    -

    例、プロトタイプコード、テスト

    +

    例、プロトタイプコード、テスト

    @@ -11154,7 +11154,7 @@

    The Rust Programming Language

    テスト全体が失敗してほしいでしょう。panic!が、テストが失敗と印づけられる手段なので、 unwrapexpectの呼び出しはスバリ起こるべきことです。

    -

    コンパイラよりもプログラマがより情報を持っている場合

    +

    コンパイラよりもプログラマがより情報を持っている場合

    @@ -11193,7 +11193,7 @@

    The Rust Programming Language

    プログラムにハードコードされるのではなく、IPアドレス文字列がユーザ起源でそれ故に確かに失敗する可能性がある場合、 Resultをもっと頑健な方法で処理したほうが絶対にいいでしょう。

    -

    エラー処理のガイドライン

    +

    エラー処理のガイドライン

    @@ -11277,7 +11277,7 @@

    The Rust Programming Language

    コンパイルが通りもしませんので、その場合を実行時に検査する必要はないわけです。 別の例は、u32のような符号なし整数を使うことであり、この場合、引数は負には絶対にならないことが確認されます。

    -

    検証のために独自の型を作る

    +

    検証のために独自の型を作る

    @@ -11417,7 +11417,7 @@

    The Rust Programming Language

    そうしたら、引数を一つ持つか、1から100の範囲の数値のみを返す関数は、シグニチャでu32ではなく、 Guessを取るか返し、本体内で追加の確認を行う必要はなくなると宣言できるでしょう。

    -

    まとめ

    +

    まとめ

    @@ -11438,7 +11438,7 @@

    The Rust Programming Language

    今や、標準ライブラリがOptionResult enumなどでジェネリクスを有効活用するところを目の当たりにしたので、 ジェネリクスの動作法と自分のコードでの使用方法について語りましょう。

    -

    ジェネリック型、トレイト、ライフタイム

    +

    ジェネリック型、トレイト、ライフタイム

    @@ -11476,7 +11476,7 @@

    The Rust Programming Language

    最後に、ライフタイムを議論します。ライフタイムとは、コンパイラに参照がお互いにどう関係しているかの情報を与える一種のジェネリクスです。 ライフタイムのおかげでコンパイラに参照が有効であることを確認してもらうことを可能にしつつ、多くの場面で値を借用できます。

    -

    関数を抽出することで重複を取り除く

    +

    関数を抽出することで重複を取り除く

    @@ -11636,7 +11636,7 @@

    The Rust Programming Language

    例えば、関数が2つあるとしましょう: 1つはi32値のスライスから最大の要素を探し、1つはchar値のスライスから最大要素を探します。 この重複はどう排除するのでしょうか?答えを見つけましょう!

    -

    ジェネリックなデータ型

    +

    ジェネリックなデータ型

    @@ -11646,7 +11646,7 @@

    The Rust Programming Language

    ジェネリクスで関数、構造体、enum、メソッドを定義する方法を見ましょう。それから、 ジェネリクスがコードのパフォーマンスに与える影響を議論します。

    -

    関数定義では

    +

    関数定義では

    @@ -11798,7 +11798,7 @@

    The Rust Programming Language

    ジェネリックな型が特定のトレイトを持つと指定する方法は「トレイト境界」節で習うでしょうが、 先にジェネリックな型引数を使用する他の方法を探究しましょう。

    -

    構造体定義では

    +

    構造体定義では

    @@ -11895,7 +11895,7 @@

    The Rust Programming Language

    数個以上使用すると、コードが読みづらくなります。コードで多くのジェネリックな型が必要な時は、 コードの小分けが必要なサインかもしれません。

    -

    enum定義では

    +

    enum定義では

    @@ -11949,7 +11949,7 @@

    The Rust Programming Language

    自分のコード内で、保持している値の型のみが異なる構造体やenum定義の場面を認識したら、 代わりにジェネリックな型を使用することで重複を避けることができます。

    -

    メソッド定義では

    +

    メソッド定義では

    @@ -12079,7 +12079,7 @@

    The Rust Programming Language

    ここで、ジェネリックな引数TUimplの後に宣言されています。構造体定義にはまるからです。 ジェネリックな引数VWfn mixupの後に宣言されています。何故なら、このメソッドにしか関係ないからです。

    -

    ジェネリクスを使用したコードのパフォーマンス

    +

    ジェネリクスを使用したコードのパフォーマンス

    @@ -12150,7 +12150,7 @@

    The Rust Programming Language

    それぞれの定義を手作業で複製した時のように振る舞います。単相化の過程により、 Rustのジェネリクスは実行時に究極的に効率的になるのです。

    -

    トレイト: 共通の振る舞いを定義する

    +

    トレイト: 共通の振る舞いを定義する

    @@ -12164,7 +12164,7 @@

    The Rust Programming Language

    注釈: 違いはあるものの、トレイトは他の言語でよくインターフェイスと呼ばれる機能に類似しています。

    -

    トレイトを定義する

    +

    トレイトを定義する

    @@ -12219,7 +12219,7 @@

    The Rust Programming Language

    トレイトには、本体に複数のメソッドを含むことができます: メソッドシグニチャは行ごとに列挙され、 各行はセミコロンで終止します。

    -

    トレイトを型に実装する

    +

    トレイトを型に実装する

    @@ -12343,7 +12343,7 @@

    The Rust Programming Language

    その逆が起きないことを保証してくれます。この規則がなければ、2つのクレートが同じ型に対して同じトレイトを実装できてしまい、 コンパイラはどちらの実装を使うべきかわからなくなってしまうでしょう。

    -

    デフォルト実装

    +

    デフォルト実装

    @@ -12463,7 +12463,7 @@

    The Rust Programming Language

    同じメソッドのオーバーライドした実装からは、デフォルト実装を呼び出すことができないことに注意してください。

    -

    トレイト境界

    +

    トレイト境界

    @@ -12525,7 +12525,7 @@

    The Rust Programming Language

    この関数シグニチャは、多くのトレイト境界のない関数のように、関数名、引数リスト、戻り値の型が一緒になって近いという点でごちゃごちゃしていません。

    -

    トレイト境界でlargest関数を修正する

    +

    トレイト境界でlargest関数を修正する

    @@ -12655,7 +12655,7 @@

    The Rust Programming Language

    CloneCopyトレイト境界は必要なくなり、ヒープ確保も避けられるでしょう。 試しにこれらの対立的な解決策もご自身で実装してみてください!

    -

    トレイト境界を使用して、メソッド実装を条件分けする

    +

    トレイト境界を使用して、メソッド実装を条件分けする

    @@ -12755,7 +12755,7 @@

    The Rust Programming Language

    型が欲しい振る舞いを保持していることを保証するのではなく、必要な間だけ参照が有効であることをライフタイムは保証します。 ライフタイムがどうやってそれを行うかを見ましょう。

    -

    ライフタイムで参照を検証する

    +

    ライフタイムで参照を検証する

    @@ -12781,7 +12781,7 @@

    The Rust Programming Language

    ライフタイム記法と遭遇する可能性のある一般的な手段を議論するので、その概念に馴染めます。 もっと詳しく知るには、第19章の「高度なライフタイム」節を参照されたし。

    -

    ライフタイムでダングリング参照を回避する

    +

    ライフタイムでダングリング参照を回避する

    @@ -12853,7 +12853,7 @@

    The Rust Programming Language

    rで行おうとするいかなることもちゃんと動作しないでしょう。では、どうやってコンパイラはこのコードが無効であると決定しているのでしょうか? 借用チェッカーを使用しています。

    -

    借用精査機

    +

    借用精査機

    @@ -12913,7 +12913,7 @@

    The Rust Programming Language

    今や、参照のライフタイムがどれだけあり、コンパイラがライフタイムを解析して参照が常に有効であることを保証する仕組みがわかったので、 関数の文脈でジェネリックな引数と戻り値のライフタイムを探究しましょう。

    -

    関数のジェネリックなライフタイム

    +

    関数のジェネリックなライフタイム

    @@ -13005,7 +13005,7 @@

    The Rust Programming Language

    借用チェッカーもこれを決定することはできません。xyのライフタイムがどう戻り値のライフタイムと関係するかわからないからです。 このエラーを修正するには、借用チェッカーが解析を実行できるように、参照間の関係を定義するジェネリックなライフタイム引数を追加します。

    -

    ライフタイム注釈記法

    +

    ライフタイム注釈記法

    @@ -13050,7 +13050,7 @@

    The Rust Programming Language

    この関数にはさらに、'aのライフタイム付きのi32への別の参照となるsecondという別の引数もあります。 ライフタイム注釈は、firstsecondの参照がどちらもジェネリックなライフタイムと同じだけ生きることを示唆します。

    -

    関数シグニチャにおけるライフタイム注釈

    +

    関数シグニチャにおけるライフタイム注釈

    @@ -13226,7 +13226,7 @@

    The Rust Programming Language

    試しに値や、longest関数に渡される参照のライフタイムや、返される参照の使用法が異なる実験をもっとしてみてください。 自分の実験がコンパイル前に借用チェッカーを通るかどうか仮説を立ててください; そして、正しいか確かめてください!

    -

    ライフタイムの観点で思考する

    +

    ライフタイムの観点で思考する

    @@ -13312,7 +13312,7 @@

    The Rust Programming Language

    一旦、繋がりができたら、メモリ安全な処理を許可するのに十分な情報がコンパイラにはあり、 ダングリングポインタを生成するであろう処理を不認可し、さもなくばメモリ安全性を侵害するのです。

    -

    構造体定義のライフタイム注釈

    +

    構造体定義のライフタイム注釈

    @@ -13359,7 +13359,7 @@

    The Rust Programming Language

    加えて、ImportantExcerptがスコープを抜けるまでnovelはスコープを抜けないので、 ImportantExcerptインスタンスの参照は有効なのです。

    -

    ライフタイム省略

    +

    ライフタイム省略

    @@ -13519,7 +13519,7 @@

    The Rust Programming Language

    3番目の規則は本当にメソッドシグニチャでしか適用されないので、次にその文脈でライフタイムを観察し、 3番目の規則が、メソッドシグニチャであまり頻繁にライフタイムを注釈しなくても済むことを意味する理由を確認します。

    -

    メソッド定義におけるライフタイム注釈

    +

    メソッド定義におけるライフタイム注釈

    @@ -13586,7 +13586,7 @@

    The Rust Programming Language

    引数の1つが&selfなので、戻り値型は&selfのライフタイムを得て、 全てのライフタイムが説明されました。

    -

    静的ライフタイム

    +

    静的ライフタイム

    @@ -13616,7 +13616,7 @@

    The Rust Programming Language

    ほとんどの場合、問題は、ダングリング参照を生成しようとしているか、利用可能なライフタイムの不一致が原因です。 そのような場合、解決策はその問題を修正することであり、'staticライフタイムを指定することではありません。

    -

    ジェネリックな型引数、トレイト境界、ライフタイムを一度に

    +

    ジェネリックな型引数、トレイト境界、ライフタイムを一度に

    ジェネリックな型引数、トレイト境界、ライフタイムを指定する記法を全て1関数でちょっと眺めましょう!

    @@ -13652,7 +13652,7 @@

    The Rust Programming Language

    Displayトレイト境界が必要なのです。ライフタイムは一種のジェネリックなので、 ライフタイム引数'aとジェネリックな型引数Tが関数名の後、山カッコ内の同じリストに収まっています。

    -

    まとめ

    +

    まとめ

    @@ -13677,7 +13677,7 @@

    The Rust Programming Language

    第19章では、ライフタイム注釈が関わるもっと複雑な筋書きと何か高度な型システムの機能を講義します。 ですが次は、コードがあるべき通りに動いていることを確かめられるように、Rustでテストを書く方法を学びます。

    -

    自動テストを書く

    +

    自動テストを書く

    @@ -13727,7 +13727,7 @@

    The Rust Programming Language

    Rustのテスト機構のメカニズムについて議論します。テストを書く際に利用可能になるアノテーションとマクロについて、 テストを実行するのに提供されているオプションと標準の動作、さらにテストをユニットテストや統合テストに体系化する方法について語ります。

    -

    テストの記述法

    +

    テストの記述法

    @@ -13747,7 +13747,7 @@

    The Rust Programming Language

    Rustが、特にこれらの動作を行うテストを書くために用意している機能を見ていきましょう。 これには、test属性、いくつかのマクロ、should_panic属性が含まれます。

    -

    テスト関数の解剖

    +

    テスト関数の解剖

    @@ -13972,7 +13972,7 @@

    The Rust Programming Language

    異なる筋書きでのテスト結果がどんな風になるか見てきたので、テストを行う際に有用になるpanic!以外のマクロに目を向けましょう。

    -

    assert!マクロで結果を確認する

    +

    assert!マクロで結果を確認する

    @@ -14137,7 +14137,7 @@

    The Rust Programming Language

    テストによりバグが捕捉されました!larger.lengthが8、smaller.lengthが5なので、 can_hold内の長さの比較が今はfalseを返すようになったのです: 8は5より小さくないですからね。

    -

    assert_eq!assert_ne!マクロで等値性をテストする

    +

    assert_eq!assert_ne!マクロで等値性をテストする

    @@ -14276,7 +14276,7 @@

    The Rust Programming Language

    これは通常、構造体やenum定義に#[derive(PartialEq, Debug)]という注釈を追加するくらい単純になります。 これらや他の導出可能なトレイトに関する詳細については、付録Cをご覧ください。

    -

    カスタムの失敗メッセージを追加する

    +

    カスタムの失敗メッセージを追加する

    @@ -14378,7 +14378,7 @@

    The Rust Programming Language

    実際に得られた値がテスト出力に見られ、起こると想定していたものではなく、 起こったものをデバッグするのに役に立ちます。

    -

    should_panicでパニックを確認する

    +

    should_panicでパニックを確認する

    @@ -14600,7 +14600,7 @@

    The Rust Programming Language

    今やテスト記法を複数知ったので、テストを走らせる際に起きていることに目を向け、 cargo testで使用できるいろんなオプションを探究しましょう。

    -

    テストの実行され方を制御する

    +

    テストの実行され方を制御する

    @@ -14625,7 +14625,7 @@

    The Rust Programming Language

    それからテストバイナリにかかる引数を列挙します。cargo test --helpを走らせると、cargo testで使用できるオプションが表示され、 cargo test -- --helpを走らせると、--という区分記号の後に使えるオプションが表示されます。

    -

    テストを並行または連続して実行する

    +

    テストを並行または連続して実行する

    @@ -14670,7 +14670,7 @@

    The Rust Programming Language

    1スレッドのみを使用してテストを実行すると、並行に実行するより時間がかかりますが、 状態を共有していても、お互いに邪魔をすることはありません。

    -

    関数の出力を表示する

    +

    関数の出力を表示する

    @@ -14776,7 +14776,7 @@

    The Rust Programming Language

    -test-threads=1オプションと--nocaptureフラグを使ってみて、 その時、出力がどうなるか確かめてください!

    -

    名前でテストの一部を実行する

    +

    名前でテストの一部を実行する

    @@ -14831,7 +14831,7 @@

    The Rust Programming Language

    test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out -

    単独のテストを走らせる

    +

    単独のテストを走らせる

    あらゆるテスト関数の名前をcargo testに渡して、そのテストのみを実行することができます:

    $ cargo test one_hundred
    @@ -14853,7 +14853,7 @@ 

    The Rust Programming Language

    この方法では、複数のテストの名前を指定することはできません; cargo testに与えられた最初の値のみが使われるのです。 ですが、複数のテストを走らせる方法もあります。

    -

    複数のテストを実行するようフィルターをかける

    +

    複数のテストを実行するようフィルターをかける

    @@ -14877,7 +14877,7 @@

    The Rust Programming Language

    また、テストが出現するモジュールがテスト名の一部になっていて、 モジュール名でフィルターをかけることで、あるモジュール内のテスト全てを実行できることに注目してください。

    -

    特に要望のない限りテストを無視する

    +

    特に要望のない限りテストを無視する

    @@ -14940,7 +14940,7 @@

    The Rust Programming Language

    ignoredテストの結果を確認することが道理に合い、結果を待つだけの時間ができたときに、 代わりにcargo test -- --ignoredを走らせることができます。

    -

    テストの体系化

    +

    テストの体系化

    @@ -14958,7 +14958,7 @@

    The Rust Programming Language

    どちらのテストを書くのも、ライブラリの一部が個別かつ共同でしてほしいことをしていることを確認するのに重要なのです。

    -

    単体テスト

    +

    単体テスト

    @@ -14971,7 +14971,7 @@

    The Rust Programming Language

    慣習は、各ファイルにtestsという名前のモジュールを作り、テスト関数を含ませ、 そのモジュールをcfg(test)で注釈することです。

    -

    テストモジュールと#[cfg(test)]

    +

    テストモジュールと#[cfg(test)]

    @@ -15016,7 +15016,7 @@

    The Rust Programming Language

    Cargoがテストコードをコンパイルします。これには、このモジュールに含まれるかもしれないヘルパー関数全ても含まれ、 #[test]で注釈された関数だけにはなりません。

    -

    非公開関数をテストする

    +

    非公開関数をテストする

    @@ -15060,7 +15060,7 @@

    The Rust Programming Language

    testsモジュールもただのモジュールでしかないので、テスト内でinternal_adderを普通にインポートし呼び出すことができます。 非公開関数はテストするべきではないとお考えなら、Rustにはそれを強制するものは何もありません。

    -

    結合テスト

    +

    結合テスト

    @@ -15075,7 +15075,7 @@

    The Rust Programming Language

    結合したコードのテストの範囲も同様に重要になるのです。結合テストを作成するには、 まずtestsディレクトリが必要になります。

    -

    testsディレクトリ

    +

    testsディレクトリ

    @@ -15181,7 +15181,7 @@

    The Rust Programming Language

    このコマンドは、tests/integration_test.rsファイルにあるテストのみを実行します。

    -

    結合テスト内のサブモジュール

    +

    結合テスト内のサブモジュール

    @@ -15294,7 +15294,7 @@

    The Rust Programming Language

    mod common;という宣言は、リスト7-4で模擬したモジュール宣言と同じであることに注意してください。 それから、テスト関数内でcommon::setup()関数を呼び出すことができます。

    -

    バイナリクレート用の結合テスト

    +

    バイナリクレート用の結合テスト

    @@ -15317,7 +15317,7 @@

    The Rust Programming Language

    この構造を使用して結合テストは、extern crateを使用して重要な機能を用いることでライブラリクレートをテストすることができます。 この重要な機能が動作すれば、src/main.rsファイルの少量のコードも動作し、その少量のコードはテストする必要がないわけです。

    -

    まとめ

    +

    まとめ

    @@ -15336,7 +15336,7 @@

    The Rust Programming Language

    この章と以前の章で学んだ知識を結集して、とあるプロジェクトに取り掛かりましょう!

    -

    入出力プロジェクト: コマンドラインプログラムを構築する

    +

    入出力プロジェクト: コマンドラインプログラムを構築する

    @@ -15393,7 +15393,7 @@

    The Rust Programming Language

    さらに、クロージャ、イテレータ、トレイトオブジェクトなど、第13章、17章で詳しく講義するものもちょっとだけ紹介します。

    -

    コマンドライン引数を受け付ける

    +

    コマンドライン引数を受け付ける

    @@ -15417,7 +15417,7 @@

    The Rust Programming Language

    コマンドライン引数を受け付けるプログラムを書く手助けをしてくれるものもありますが、ちょうどこの概念を学んでいる最中なので、 この能力を自分で実装しましょう。

    -

    引数の値を読み取る

    +

    引数の値を読み取る

    @@ -15472,7 +15472,7 @@

    The Rust Programming Language

    -

    args関数と不正なユニコード

    +

    args関数と不正なユニコード

    引数のどれかが不正なユニコードを含んでいたら、std::env::argsはパニックすることに注意してください。 プログラムが不正なユニコードを含む引数を受け付ける必要があるなら、代わりにstd::env::args_osを使用してください。 この関数は、String値ではなく、OsString値を生成するイテレータを返します。ここでは、 @@ -15516,7 +15516,7 @@

    The Rust Programming Language

    メッセージで出力したり、プログラムを起動するのに使用されたコマンドラインエイリアスによってプログラムの振る舞いを変えたい場合に、 プログラム名にアクセスするのにしばしば便利です。ですが、この章の目的には、これを無視し、必要な二つの引数のみを保存します。

    -

    引数の値を変数に保存する

    +

    引数の値を変数に保存する

    @@ -15574,7 +15574,7 @@

    The Rust Programming Language

    何らかのエラー処理を加えて、ユーザが引数を提供しなかった場合など、可能性のある特定のエラー状況に対処します; 今は、そのような状況はないものとし、代わりにファイル読み取り能力を追加することに取り組みます。

    -

    ファイルを読み込む

    +

    ファイルを読み込む

    @@ -15714,7 +15714,7 @@

    The Rust Programming Language

    それを綺麗に解消するのは困難になっていきます。プログラムを開発する際に早い段階でリファクタングを行うのは、 良い戦術です。リファクタリングするコードの量が少なければ、はるかに簡単になりますからね。次は、それを行いましょう。

    -

    リファクタリングしてモジュール性とエラー処理を向上させる

    +

    リファクタリングしてモジュール性とエラー処理を向上させる

    プログラムを改善するために、プログラムの構造と起こりうるエラーに対処する方法に関連する4つの問題を修正していきましょう。

    @@ -15768,7 +15768,7 @@

    The Rust Programming Language

    プロジェクトをリファクタリングして、これら4つの問題を扱いましょう。

    -

    バイナリプロジェクトの責任の分離

    +

    バイナリプロジェクトの責任の分離

    @@ -15813,7 +15813,7 @@

    The Rust Programming Language

    main.rsに残る唯一のコードは、読めばその正当性が評価できるだけ小規模になるでしょう。 この工程に従って、プログラムのやり直しをしましょう。

    -

    引数解析器を抽出する

    +

    引数解析器を抽出する

    @@ -15863,7 +15863,7 @@

    The Rust Programming Language

    少しずつ段階的にリファクタリングしているのです。この変更後、プログラムを再度実行して、 引数解析がまだ動作していることを実証してください。問題が発生した時に原因を特定する助けにするために頻繁に進捗を確認するのはいいことです。

    -

    設定値をまとめる

    +

    設定値をまとめる

    @@ -15963,7 +15963,7 @@

    The Rust Programming Language

    -

    cloneを使用する代償

    +

    cloneを使用する代償

    実行時コストのためにcloneを使用して所有権問題を解消するのを避ける傾向が多くのRustaceanにあります。 第13章で、この種の状況においてより効率的なメソッドの使用法を学ぶでしょう。ですがとりあえずは、 これらのコピーをするのは1回だけですし、ファイル名とクエリ文字列は非常に小さなものなので、 @@ -15984,7 +15984,7 @@

    The Rust Programming Language

    これでコードはqueryfilenameが関連していることと、その目的がプログラムの振る舞い方を設定するということをより明確に伝えます。 これらの値を使用するあらゆるコードは、configインスタンスの目的の名前を冠したフィールドにそれらを発見することを把握しています。

    -

    Configのコンストラクタを作成する

    +

    Configのコンストラクタを作成する

    @@ -16048,7 +16048,7 @@

    The Rust Programming Language

    parse_configの名前をnewに変え、implブロックに入れ込んだので、new関数とConfigが紐づくようになりました。 再度このコードをコンパイルしてみて、動作することを確かめてください。

    -

    エラー処理を修正する

    +

    エラー処理を修正する

    @@ -16070,7 +16070,7 @@

    The Rust Programming Language

    境界外アクセス: 長さは1なのに添え字も1ですという行は、プログラマ向けのエラーメッセージです。 エンドユーザが起きたことと代わりにすべきことを理解する手助けにはならないでしょう。これを今修正しましょう。

    -

    エラーメッセージを改善する

    +

    エラーメッセージを改善する

    @@ -16125,7 +16125,7 @@

    The Rust Programming Language

    panic!の呼び出しは、第9章で議論したように、使用の問題よりもプログラミング上の問題により適しています。 代わりに、第9章で学んだもう一つのテクニックを使用することができます。成功か失敗かを示唆するResultを返すことです。

    -

    panic!を呼び出す代わりにnewからResultを返す

    +

    panic!を呼び出す代わりにnewからResultを返す

    @@ -16179,7 +16179,7 @@

    The Rust Programming Language

    Config::newからErr値を返すことにより、main関数は、new関数から返ってくるResult値を処理し、 エラー時により綺麗にプロセスから抜け出すことができます。

    -

    Config::newを呼び出し、エラーを処理する

    +

    Config::newを呼び出し、エラーを処理する

    @@ -16250,7 +16250,7 @@

    The Rust Programming Language

    素晴らしい!この出力の方が遥かにユーザに優しいです。

    -

    mainからロジックを抽出する

    +

    mainからロジックを抽出する

    @@ -16299,7 +16299,7 @@

    The Rust Programming Language

    これでrun関数は、ファイル読み込みから始まるmain関数の残りのロジック全てを含むようになりました。 このrun関数は、引数にConfigインスタンスを取ります。

    -

    run関数からエラーを返す

    +

    run関数からエラーを返す

    @@ -16388,7 +16388,7 @@

    The Rust Programming Language

    コンパイラは、ここにエラー処理コードを書くつもりだったんじゃないかと思い出させてくれています! 今、その問題を改修しましょう。

    -

    mainrunから返ってきたエラーを処理する

    +

    mainrunから返ってきたエラーを処理する

    リスト12-10のConfig::newに対して行った方法に似たテクニックを使用してエラーを確認し、扱いますが、 @@ -16422,7 +16422,7 @@

    The Rust Programming Language

    if letunwrap_or_else関数の中身はどちらも同じです: エラーを出力して終了します。

    -

    コードをライブラリクレートに分割する

    +

    コードをライブラリクレートに分割する

    @@ -16519,7 +16519,7 @@

    The Rust Programming Language

    古いコードでは大変だけれども、新しいコードでは楽なことをして新発見のモジュール性を活用しましょう: テストを書くのです!

    -

    テスト駆動開発でライブラリの機能を開発する

    +

    テスト駆動開発でライブラリの機能を開発する

    @@ -16559,7 +16559,7 @@

    The Rust Programming Language

    実際にクエリ文字列の検索を行う機能の実装をテスト駆動し、クエリに合致する行のリストを生成します。 この機能をsearchという関数に追加しましょう。

    -

    失敗するテストを記述する

    +

    失敗するテストを記述する

    @@ -16715,7 +16715,7 @@

    The Rust Programming Language

    素晴らしい。テストは全く想定通りに失敗しています。テストが通るようにしましょう!

    -

    テストを通過させるコードを書く

    +

    テストを通過させるコードを書く

    空のベクタを常に返しているために、現状テストは失敗しています。それを修正し、searchを実装するには、 @@ -16735,7 +16735,7 @@

    The Rust Programming Language

    各行を繰り返す作業から、この手順に順に取り掛かりましょう。

    -

    linesメソッドで各行を繰り返す

    +

    linesメソッドで各行を繰り返す

    @@ -16761,7 +16761,7 @@

    The Rust Programming Language

    リスト3-5でこのようなイテレータの使用法は見かけたことを思い出してください。 そこでは、イテレータにforループを使用してコレクションの各要素に対して何らかのコードを走らせていました。

    -

    クエリを求めて各行を検索する

    +

    クエリを求めて各行を検索する

    @@ -16784,7 +16784,7 @@

    The Rust Programming Language

    リスト12-18: 行がqueryの文字列を含むか確認する機能を追加する

    -

    合致した行を保存する

    +

    合致した行を保存する

    @@ -16832,7 +16832,7 @@

    The Rust Programming Language

    検索関数のコードは悪すぎるわけではありませんが、イテレータの有用な機能の一部を活用していません。 この例には第13章で再度触れ、そこでは、イテレータをより深く探究し、さらに改善する方法に目を向けます。

    -

    run関数内でsearch関数を使用する

    +

    run関数内でsearch関数を使用する

    @@ -16895,7 +16895,7 @@

    The Rust Programming Language

    このプロジェクトをまとめ上げるために、環境変数を扱う方法と標準エラー出力に出力する方法を少しだけデモします。 これらはどちらも、コマンドラインプログラムを書く際に有用です。

    -

    環境変数を取り扱う

    +

    環境変数を取り扱う

    @@ -16907,7 +16907,7 @@

    The Rust Programming Language

    代わりに環境変数を使用します。そうすることでユーザは1回環境変数をセットすれば、そのターミナルセッションの間は、 大文字小文字無視の検索を行うことができるようになるわけです。

    -

    大文字小文字を区別しないsearch関数用に失敗するテストを書く

    +

    大文字小文字を区別しないsearch関数用に失敗するテストを書く

    @@ -16991,7 +16991,7 @@

    The Rust Programming Language

    “Trust me.”という行にもマッチするはずです。これが失敗するテストであり、まだsearch_case_insensitive関数を定義していないので、 コンパイルは失敗するでしょう。リスト12-16のsearch関数で行ったのと同様に空のベクタを常に返すような仮実装を追加し、テストがコンパイルされるものの、失敗する様をご自由に確認してください。

    -

    search_case_insensitive関数を実装する

    +

    search_case_insensitive関数を実装する

    @@ -17250,7 +17250,7 @@

    The Rust Programming Language

    std::envモジュールは、環境変数を扱うもっと多くの有用な機能を有しています: ドキュメンテーションを確認して、何が利用可能か確かめてください。

    -

    標準出力ではなく標準エラーにエラーメッセージを書き込む

    +

    標準出力ではなく標準エラーにエラーメッセージを書き込む

    @@ -17265,7 +17265,7 @@

    The Rust Programming Language

    println!関数は、標準出力に出力する能力しかないので、標準エラーに出力するには他のものを使用しなければなりません。

    -

    エラーが書き込まれる場所を確認する

    +

    エラーが書き込まれる場所を確認する

    @@ -17306,7 +17306,7 @@

    The Rust Programming Language

    そうです。エラーメッセージは標準出力に出力されているのです。このようなエラーメッセージは標準エラーに出力され、 成功した状態のデータのみがファイルに残ると遥かに有用です。それを変更します。

    -

    エラーを標準エラーに出力する

    +

    エラーを標準エラーに出力する

    @@ -17364,7 +17364,7 @@

    The Rust Programming Language

    これは、もう成功した出力には標準出力を、エラー出力には標準エラーを適切に使用していることをデモしています。

    -

    まとめ

    +

    まとめ

    @@ -17381,7 +17381,7 @@

    The Rust Programming Language

    次は、関数型言語に影響されたRust機能を一部探究します: クロージャとイテレータです。

    -

    関数型言語の機能: イテレータとクロージャ

    +

    関数型言語の機能: イテレータとクロージャ

    @@ -17416,7 +17416,7 @@

    The Rust Programming Language

    クロージャとイテレータをマスターすることは、慣用的で速いRustコードを書くことの重要な部分なので、 この章を丸ごと捧げます。

    -

    クロージャ: 環境をキャプチャできる匿名関数

    +

    クロージャ: 環境をキャプチャできる匿名関数

    @@ -17428,7 +17428,7 @@

    The Rust Programming Language

    関数と異なり、呼び出されたスコープの値をクロージャは、キャプチャすることができます。 これらのクロージャの機能がコードの再利用や、動作のカスタマイズを行わせてくれる方法を模擬しましょう。

    -

    クロージャで動作の抽象化を行う

    +

    クロージャで動作の抽象化を行う

    @@ -17611,7 +17611,7 @@

    The Rust Programming Language

    この関数を現時点で呼んでいるところを切り捨てたくもあります。要するに、結果が必要なければ関数を呼び出したくなく、 それでも1回だけ呼び出したいのです。

    -

    関数でリファクタリング

    +

    関数でリファクタリング

    @@ -17674,7 +17674,7 @@

    The Rust Programming Language

    プログラムの1箇所でコードを定義したいですが、結果が本当に必要なところでだけコードを実行します。 これは、クロージャのユースケースです!

    -

    クロージャでリファクタリングして、コードを保存する

    +

    クロージャでリファクタリングして、コードを保存する

    @@ -17796,7 +17796,7 @@

    The Rust Programming Language

    クロージャは他の解決法も用意してくれます。その解決策については、もう少し先で語りましょう。でもまずは、 クロージャ定義に型注釈がない理由とクロージャに関わるトレイトについて話しましょう。

    -

    クロージャの型推論と注釈

    +

    クロージャの型推論と注釈

    @@ -17909,7 +17909,7 @@

    The Rust Programming Language

    そして、その型がexample_closureのクロージャに閉じ込められ、同じクロージャを異なる型で使用しようとすると、 型エラーが出るのです。

    -

    ジェネリック引数とFnトレイトを使用してクロージャを保存する

    +

    ジェネリック引数とFnトレイトを使用してクロージャを保存する

    @@ -18156,7 +18156,7 @@

    The Rust Programming Language

    必要以上に重い計算を呼び出さないことを保証するのに必要なロジックの面倒をCacherは見るので、 generate_workoutはビジネスロジックに集中できるのです。

    -

    Cacher実装の限界

    +

    Cacher実装の限界

    @@ -18219,7 +18219,7 @@

    The Rust Programming Language

    例えば、文字列スライスを取り、usizeを返すクロージャの結果をキャッシュしたくなるかもしれません。 この問題を修正するには、Cacher機能の柔軟性を向上させるためによりジェネリックな引数を導入してみてください。

    -

    クロージャで環境をキャプチャする

    +

    クロージャで環境をキャプチャする

    @@ -18384,7 +18384,7 @@

    The Rust Programming Language

    環境をキャプチャできるクロージャが関数の引数として有用な場面を説明するために、次のトピックに移りましょう: イテレータです。

    -

    一連の要素をイテレータで処理する

    +

    一連の要素をイテレータで処理する

    @@ -18453,7 +18453,7 @@

    The Rust Programming Language

    イテレータにより、添え字を使えるデータ構造、ベクタなどだけではなく、多くの異なるシーケンスに対して同じロジックを使う柔軟性も得られます。 イテレータがそれをする方法を調査しましょう。

    -

    Iteratorトレイトとnextメソッド

    +

    Iteratorトレイトとnextメソッド

    全てのイテレータは、標準ライブラリで定義されているIteratorというトレイトを実装しています。 @@ -18535,7 +18535,7 @@

    The Rust Programming Language

    iterではなくinto_iterを呼び出すことができます。同様に、可変参照を繰り返したいなら、 iterではなくiter_mutを呼び出せます。

    -

    イテレータを消費するメソッド

    +

    イテレータを消費するメソッド

    @@ -18579,7 +18579,7 @@

    The Rust Programming Language

    sumは呼び出し対象のイテレータの所有権を奪うので、sum呼び出し後にv1_iterを使用することはできません。

    -

    他のイテレータを生成するメソッド

    +

    他のイテレータを生成するメソッド

    @@ -18660,7 +18660,7 @@

    The Rust Programming Language

    これは、Iteratorトレイトが提供する繰り返し動作を再利用しつつ、 クロージャにより一部の動作をカスタマイズできる好例になっています。

    -

    環境をキャプチャするクロージャを使用する

    +

    環境をキャプチャするクロージャを使用する

    @@ -18736,7 +18736,7 @@

    The Rust Programming Language

    shoes_in_my_sizeを呼び出した時に、指定した値と同じサイズの靴だけが得られることをテストは示しています。

    -

    Iteratorトレイトで独自のイテレータを作成する

    +

    Iteratorトレイトで独自のイテレータを作成する

    @@ -18830,7 +18830,7 @@

    The Rust Programming Language

    countの値が5以下なら、nextSomeに包まれた現在の値を返しますが、 countが6以上なら、イテレータはNoneを返します。

    -

    Counterイテレータのnextメソッドを使用する

    +

    Counterイテレータのnextメソッドを使用する

    @@ -18883,7 +18883,7 @@

    The Rust Programming Language

    それからイテレータにほしい動作が実装し終わっていることを実証しながら、nextを繰り返し呼び出しています: 1から5の値を返すことです。

    -

    他のIteratorトレイトメソッドを使用する

    +

    他のIteratorトレイトメソッドを使用する

    @@ -18956,7 +18956,7 @@

    The Rust Programming Language

    nextメソッドの動作方法を指定し、標準ライブラリがnextを呼び出す他のメソッドにデフォルト実装を提供しているので、 これらのメソッド呼び出しは全て可能です。

    -

    入出力プロジェクトを改善する

    +

    入出力プロジェクトを改善する

    @@ -18966,7 +18966,7 @@

    The Rust Programming Language

    このイテレータに関する新しい知識があれば、イテレータを使用してコードのいろんな場所をより明確で簡潔にすることで、 第12章の入出力プロジェクトを改善することができます。イテレータがConfig::new関数とsearch関数の実装を改善する方法に目を向けましょう。

    -

    イテレータを使用してcloneを取り除く

    +

    イテレータを使用してcloneを取り除く

    @@ -19022,7 +19022,7 @@

    The Rust Programming Language

    ひとたび、Config::newがイテレータの所有権を奪い、借用する添え字アクセス処理をやめたら、 cloneを呼び出して新しくメモリ確保するのではなく、イテレータからのString値をConfigにムーブできます。

    -

    返却されるイテレータを直接使う

    +

    返却されるイテレータを直接使う

    入出力プロジェクトのsrc/main.rsファイルを開いてください。こんな見た目のはずです:

    @@ -19090,7 +19090,7 @@

    The Rust Programming Language

    std::env::Argsになりました。argsの所有権を奪い、繰り返しを行うことでargsを可変化する予定なので、 args引数の仕様にmutキーワードを追記でき、可変にします。

    -

    添え字の代わりにIteratorトレイトのメソッドを使用する

    +

    添え字の代わりにIteratorトレイトのメソッドを使用する

    @@ -19149,7 +19149,7 @@

    The Rust Programming Language

    matchを使用してその値を抜き出します。Noneを返したら、十分な引数が与えられなかったということなので、 Err値で早期リターンします。filename値に対しても同じことをします。

    -

    イテレータアダプタでコードをより明確にする

    +

    イテレータアダプタでコードをより明確にする

    入出力プロジェクトのsearch関数でも、イテレータを活用することができます。その関数はリスト12-19に示していますが、以下のリスト13-28に再掲します。

    @@ -19225,7 +19225,7 @@

    The Rust Programming Language

    ですが、本当に2つの実装は等価なのでしょうか?直観的な仮説は、より低レベルのループの方がより高速ということかもしれません。 パフォーマンスに触れましょう。

    -

    パフォーマンス比較: ループVSイテレータ

    +

    パフォーマンス比較: ループVSイテレータ

    @@ -19333,7 +19333,7 @@

    The Rust Programming Language

    もうイテレータとクロージャを恐れなしに使用することができますね!それらのおかげでコードは、高レベルだけれども、 そうすることに対して実行時のパフォーマンスを犠牲にしないようになります。

    -

    まとめ

    +

    まとめ

    @@ -19347,7 +19347,7 @@

    The Rust Programming Language

    今や入出力プロジェクトの表現力を改善したので、プロジェクトを世界と共有するのに役に立つcargoの機能にもっと目を向けましょう。

    -

    CargoとCrates.ioについてより詳しく

    +

    CargoとCrates.ioについてより詳しく

    @@ -19372,7 +19372,7 @@

    The Rust Programming Language

    また、Cargoはこの章で講義する以上のこともできるので、機能の全解説を見るには、 ドキュメンテーションを参照されたし。

    -

    リリースプロファイルでビルドをカスタマイズする

    +

    リリースプロファイルでビルドをカスタマイズする

    @@ -19453,7 +19453,7 @@

    The Rust Programming Language

    設定の選択肢と各プロファイルのデフォルト設定の一覧は、Cargoのドキュメンテーションを参照されたし。

    -

    Crates.ioにクレートを公開する

    +

    Crates.ioにクレートを公開する

    @@ -19469,7 +19469,7 @@

    The Rust Programming Language

    RustとCargoは、公開したパッケージを人が使用し、そもそも見つけやすくしてくれる機能を有しています。 これらの機能の一部を次に語り、そして、パッケージの公開方法を説明します。

    -

    役に立つドキュメンテーションコメントを行う

    +

    役に立つドキュメンテーションコメントを行う

    @@ -19533,7 +19533,7 @@

    The Rust Programming Language

    図14-1: add_one関数のHTMLドキュメント

    -

    よく使われるセクション

    +

    よく使われるセクション

    @@ -19563,7 +19563,7 @@

    The Rust Programming Language

    多くのドキュメンテーションコメントでは、これら全てのセクションが必要になることはありませんが、 これは自分のコードを呼び出している人が知りたいと思うコードの方向性を思い出させてくれるいいチェックリストになります。

    -

    テストとしてのドキュメンテーションコメント

    +

    テストとしてのドキュメンテーションコメント

    @@ -19590,7 +19590,7 @@

    The Rust Programming Language

    さて、例のassert_eq!がパニックするように、関数か例を変更し、再度cargo testを実行したら、 docテストが、例とコードがお互いに同期されていないことを捕捉するところを目撃するでしょう!

    -

    含まれている要素にコメントする

    +

    含まれている要素にコメントする

    @@ -19648,7 +19648,7 @@

    The Rust Programming Language

    要素内のドキュメンテーションコメントは、特にクレートやモジュールを解説するのに有用です。 コンテナの全体の目的を説明し、クレートの使用者がクレートの体系を理解する手助けをするのに使用してください。

    -

    pub useで便利な公開APIをエクスポートする

    +

    pub useで便利な公開APIをエクスポートする

    @@ -19852,7 +19852,7 @@

    The Rust Programming Language

    その内部構造をユーザに提示する構造から切り離してくれます。インストールしてある他のクレートを見て、 内部構造が公開APIと異なっているか確認してみてください。

    -

    Crates.ioのアカウントをセットアップする

    +

    Crates.ioのアカウントをセットアップする

    @@ -19878,7 +19878,7 @@

    The Rust Programming Language

    このトークンは、秘密です: 他人とは共有しないでください。なんらかの理由で他人と実際に共有してしまったら、 古いものを破棄してcrates.ioで新しいトークンを生成するべきです。

    -

    新しいクレートにメタデータを追加する

    +

    新しいクレートにメタデータを追加する

    @@ -19978,7 +19978,7 @@

    The Rust Programming Language

    Cargoのドキュメンテーションには、 指定して他人が発見し、より容易くクレートを使用できることを保証する他のメタデータが解説されています。

    -

    Crates.ioに公開する

    +

    Crates.ioに公開する

    @@ -20013,7 +20013,7 @@

    The Rust Programming Language

    おめでとうございます!Rustコミュニティとコードを共有し、誰でもあなたのクレートを依存として簡単に追加できます。

    -

    既存のクレートの新バージョンを公開する

    +

    既存のクレートの新バージョンを公開する

    @@ -20024,7 +20024,7 @@

    The Rust Programming Language

    セマンティックバージョンルールを使用して加えた変更の種類に基づいて次の適切なバージョン番号を決定してください。 そして、cargo publishを実行し、新バージョンをアップロードします。

    -

    cargo yankでCrates.ioからバージョンを削除する

    +

    cargo yankでCrates.ioからバージョンを削除する

    @@ -20058,7 +20058,7 @@

    The Rust Programming Language

    取り下げは、コードの削除は一切しません。例として、取り下げ機能は、誤ってアップロードされた秘密鍵を削除するためのものではありません。 もしそうなってしまったら、即座に秘密鍵をリセットしなければなりません。

    -

    Cargoのワークスペース

    +

    Cargoのワークスペース

    @@ -20069,7 +20069,7 @@

    The Rust Programming Language

    ライブラリクレートの肥大化が続き、その上で複数のライブラリクレートにパッケージを分割したくなることでしょう。 この場面において、Cargoはワークスペースという協調して開発された関連のある複数のパッケージを管理するのに役立つ機能を提供しています。

    -

    ワークスペースを生成する

    +

    ワークスペースを生成する

    @@ -20142,7 +20142,7 @@

    The Rust Programming Language

    targetディレクトリに生成物がある状態にしなければならないでしょう。一つのtargetディレクトリを共有することで、 クレートは不必要な再ビルドを回避できるのです。

    -

    ワークスペース内に2番目のクレートを作成する

    +

    ワークスペース内に2番目のクレートを作成する

    @@ -20243,7 +20243,7 @@

    The Rust Programming Language

    これにより、adder/src/main.rsのコードが実行され、これはadd_oneクレートに依存しています。

    -

    ワークスペースの外部クレートに依存する

    +

    ワークスペースの外部クレートに依存する

    @@ -20318,7 +20318,7 @@

    The Rust Programming Language

    同じバージョンを使っていることを確かめてくれるのです。ワークスペース全体でrandの同じバージョンを使用することにより、 複数のコピーが存在しないのでスペースを節約し、ワークスペースのクレートが相互に互換性を維持することを確かめます。

    -

    ワークスペースにテストを追加する

    +

    ワークスペースにテストを追加する

    さらなる改善として、add_oneクレート内にadd_one::add_one関数のテストを追加しましょう:

    @@ -20417,7 +20417,7 @@

    The Rust Programming Language

    微細で個別のコンポーネントの方が理解しやすいです。またワークスペースにクレートを保持することは、 同時に変更されることが多いのなら、協調しやすくなることにも繋がります。

    -

    cargo installでCrates.ioからバイナリをインストールする

    +

    cargo installでCrates.ioからバイナリをインストールする

    @@ -20464,7 +20464,7 @@

    The Rust Programming Language

    インストールディレクトリが$PATHに存在する限り、前述したように、rg --helpを走らせて、 より高速でRustらしいファイル検索ツールを使用し始めることができます!

    -

    独自のコマンドでCargoを拡張する

    +

    独自のコマンドでCargoを拡張する

    @@ -20477,7 +20477,7 @@

    The Rust Programming Language

    cargo --listを実行すると、列挙もされます。cargo installを使用して拡張をインストールし、 それから組み込みのCargoツール同様に実行できることは、Cargoの設計上の非常に便利な恩恵です!

    -

    まとめ

    +

    まとめ

    @@ -20490,7 +20490,7 @@

    The Rust Programming Language

    積極的にcrates.ioで自分にとって有用なコードを共有してください; 他の誰かにとっても、役に立つものであることでしょう!

    -

    スマートポインタ

    +

    スマートポインタ

    @@ -20570,7 +20570,7 @@

    The Rust Programming Language

    さあ、飛び込みましょう!

    -

    ヒープのデータを指すBox<T>を使用する

    +

    ヒープのデータを指すBox<T>を使用する

    @@ -20613,7 +20613,7 @@

    The Rust Programming Language

    すべてその話題を説明するためだけのものです。 従って、ここで学ぶのと同じことが第17章においても適用するでしょう!

    -

    Box<T>を使ってヒープにデータを格納する

    +

    Box<T>を使ってヒープにデータを格納する

    Box<T>のこのユースケースを議論する前に、Box<T>の記法と、Box<T>内に格納された値を読み書きする方法について講義しましょう。

    @@ -20649,7 +20649,7 @@

    The Rust Programming Language

    単独のi32のような値は、既定で格納される場所であるスタックに置くことが、大多数の場合にはより適切です。 ボックスがなかったら定義することの叶わない型をボックスが定義させてくれる場合を見ましょう。

    -

    ボックスで再帰的な型を可能にする

    +

    ボックスで再帰的な型を可能にする

    @@ -20669,7 +20669,7 @@

    The Rust Programming Language

    我々が定義するコンスリストは、再帰を除いて素直です; 故に、これから取り掛かる例の概念は、 再帰的な型が関わるもっと複雑な場面に遭遇したら必ず役に立つでしょう。

    -

    コンスリストについてもっと詳しく

    +

    コンスリストについてもっと詳しく

    @@ -20778,7 +20778,7 @@

    The Rust Programming Language

    自身の別の値を直接保持しているのです。結果として、コンパイラは、List値を格納するのに必要な領域が計算できないのです。 このエラーが得られた理由を少し噛み砕きましょう。まず、非再帰的な型の値を格納するのに必要な領域をどうコンパイラが決定しているかを見ましょう。

    -

    非再帰的な型のサイズを計算する

    +

    非再帰的な型のサイズを計算する

    第6章でenum定義を議論した時にリスト6-2で定義したMessage enumを思い出してください:

    @@ -20822,7 +20822,7 @@

    The Rust Programming Language

    図15-1: 無限のCons列挙子からなる無限のList

    -

    Box<T>で既知のサイズの再帰的な型を得る

    +

    Box<T>で既知のサイズの再帰的な型を得る

    @@ -20913,7 +20913,7 @@

    The Rust Programming Language

    これら2つのトレイトをより詳しく探究しましょう。これら2つのトレイトは、 この章の残りで議論する他のスマートポインタ型で提供される機能にとってさらに重要でしょう。

    -

    Derefトレイトでスマートポインタを普通の参照のように扱う

    +

    Derefトレイトでスマートポインタを普通の参照のように扱う

    @@ -20934,7 +20934,7 @@

    The Rust Programming Language

    Derefトレイトを実装することでスマートポインタが参照と似た方法で動作するようにできる方法を探求します。 そして、Rustの参照外し型強制機能と、それにより参照やスマートポインタと協調できる方法を見ます。

    -

    参照外し演算子で値までポインタを追いかける

    +

    参照外し演算子で値までポインタを追いかける

    @@ -20984,7 +20984,7 @@

    The Rust Programming Language

    参照と数値は異なる型なので、比較することは許容されていません。参照外し演算子を使用して、 参照を指している値まで追いかけなければならないのです。

    -

    Box<T>を参照のように使う

    +

    Box<T>を参照のように使う

    リスト15-6のコードを参照の代わりにBox<T>を使うように書き直すことができます; @@ -21013,7 +21013,7 @@

    The Rust Programming Language

    最後のアサートで参照外し演算子を使用してyが参照だった時のようにボックスのポインタを追いかけることができます。 次に、独自のボックス型を定義することで参照外し演算子を使用させてくれるBox<T>について何が特別なのかを探究します。

    -

    独自のスマートポインタを定義する

    +

    独自のスマートポインタを定義する

    @@ -21081,7 +21081,7 @@

    The Rust Programming Language

    MyBox<T>に参照外しの能力を実装していないので、参照外しできません。*演算子で参照外しできるようにするには、 Derefトレイトを実装します。

    -

    Derefトレイトを実装して型を参照のように扱う

    +

    Derefトレイトを実装して型を参照のように扱う

    @@ -21159,7 +21159,7 @@

    The Rust Programming Language

    *演算子は、コードで*を打つたびに、ただ1回、derefメソッドの呼び出し、そして*演算子の呼び出しに置き換えられることに注意してください。 *演算子の置き換えは、無限に繰り返されないので、型i32に行き着き、リスト15-9でassert_eq!5と合致します。

    -

    関数やメソッドで暗黙的な参照外し型強制

    +

    関数やメソッドで暗黙的な参照外し型強制

    @@ -21302,7 +21302,7 @@

    The Rust Programming Language

    参照を得、引数の型と一致させます。Deref::derefが挿入される必要のある回数は、コンパイル時に解決されるので、 参照外し型強制を活用するための実行時の代償は何もありません。

    -

    参照外し型強制が可変性と相互作用する方法

    +

    参照外し型強制が可変性と相互作用する方法

    @@ -21343,7 +21343,7 @@

    The Rust Programming Language

    不変参照を可変参照にするには、そのデータへの不変参照がたった1つしかないことが必要ですが、 借用規則はそれを保証してくれません。故に、不変参照を可変参照に変換することが可能であるという前提を敷けません。

    -

    Dropトレイトで片付け時にコードを走らせる

    +

    Dropトレイトで片付け時にコードを走らせる

    @@ -21439,7 +21439,7 @@

    The Rust Programming Language

    この例は、dropメソッドの動き方を見た目で案内するだけですが、通常は、メッセージ出力ではなく、 自分の型が走らせる必要のあるクリーンアップコードを指定するでしょう。

    -

    std::mem::dropで早期に値をドロップする

    +

    std::mem::dropで早期に値をドロップする

    @@ -21565,7 +21565,7 @@

    The Rust Programming Language

    これでBox<T>とスマートポインタの特徴の一部を調査したので、標準ライブラリに定義されている他のスマートポインタをいくつか見ましょう。

    -

    Rc<T>は、参照カウント方式のスマートポインタ

    +

    Rc<T>は、参照カウント方式のスマートポインタ

    @@ -21608,7 +21608,7 @@

    The Rust Programming Language

    Rc<T>は、シングルスレッドの筋書きで使用するためだけのものであることに注意してください。 第16章で並行性について議論する時に、マルチスレッドプログラムで参照カウントをする方法を講義します。

    -

    Rc<T>でデータを共有する

    +

    Rc<T>でデータを共有する

    @@ -21741,7 +21741,7 @@

    The Rust Programming Language

    視覚的にディープコピーをする類のクローンと参照カウントを増やす種類のクローンを区別することができます。 コード内でパフォーマンスの問題を探す際、ディープコピーのクローンだけを考慮し、Rc::cloneの呼び出しを無視できるのです。

    -

    Rc<T>をクローンすると、参照カウントが増える

    +

    Rc<T>をクローンすると、参照カウントが増える

    aRc<List>への参照を作ったりドロップする毎に参照カウントが変化するのが確かめられるように、 @@ -21826,7 +21826,7 @@

    The Rust Programming Language

    データを可変化する能力はとても有用です!次の節では、内部可変性パターンと、 Rc<T>と絡めて使用してこの不変性制限を手がけられるRefCell<T>型について議論します。

    -

    RefCell<T>と内部可変性パターン

    +

    RefCell<T>と内部可変性パターン

    @@ -21846,7 +21846,7 @@

    The Rust Programming Language

    内部可変性パターンに従うRefCell<T>型を眺めてこの概念を探究しましょう。

    -

    RefCell<T>で実行時に借用規則を強制する

    +

    RefCell<T>で実行時に借用規則を強制する

    @@ -21928,7 +21928,7 @@

    The Rust Programming Language

    不変な値の中の値を可変化することは、内部可変性パターンです。内部可変性が有用になる場面を見て、 それが可能になる方法を調査しましょう。

    -

    内部可変性: 不変値への可変借用

    +

    内部可変性: 不変値への可変借用

    借用規則の結果は、不変値がある時、可変で借用することはできないということです。 @@ -21966,7 +21966,7 @@

    The Rust Programming Language

    RefCell<T>を使用して不変値を可変化する実践的な例に取り組み、それが役に立つ理由を確認しましょう。

    -

    内部可変性のユースケース: モックオブジェクト

    +

    内部可変性のユースケース: モックオブジェクト

    @@ -22225,7 +22225,7 @@

    The Rust Programming Language

    RefCell<T>の使用法を見かけたので、動作の仕方を深掘りしましょう!

    -

    RefCell<T>で実行時に借用を追いかける

    +

    RefCell<T>で実行時に借用を追いかける

    @@ -22307,7 +22307,7 @@

    The Rust Programming Language

    自身を変更して見かけたメッセージを追跡するモックオブジェクトを書くことが可能になります。 代償はありますが、RefCell<T>を使用すれば、普通の参照よりも多くの機能を得ることができるわけです。

    -

    Rc<T>RefCell<T>を組み合わせることで可変なデータに複数の所有者を持たせる

    +

    Rc<T>RefCell<T>を組み合わせることで可変なデータに複数の所有者を持たせる

    @@ -22408,7 +22408,7 @@

    The Rust Programming Language

    これらの型の違いをより詳しく知るには、標準ライブラリのドキュメンテーションをチェックしてください。

    -

    循環参照は、メモリをリークすることもある

    +

    循環参照は、メモリをリークすることもある

    @@ -22424,7 +22424,7 @@

    The Rust Programming Language

    要素がお互いに循環して参照する参照を生成することも可能ということです。循環の各要素の参照カウントが絶対に0にならないので、 これはメモリリークを起こし、値は絶対にドロップされません。

    -

    循環参照させる

    +

    循環参照させる

    @@ -22612,7 +22612,7 @@

    The Rust Programming Language

    リスト15-25では、常にCons列挙子にリストを所有してほしいので、データ構造を再構成することはできません。 親ノードと子ノードからなるグラフを使った例に目を向けて、どんな時に所有権のない関係が循環参照を回避するのに適切な方法になるか確認しましょう。

    -

    循環参照を回避する: Rc<T>Weak<T>に変換する

    +

    循環参照を回避する: Rc<T>Weak<T>に変換する

    @@ -22655,7 +22655,7 @@

    The Rust Programming Language

    例として、要素が次の要素を知っているだけのリストを使うのではなく、要素が子要素親要素を知っている木を作りましょう。

    -

    木データ構造を作る: 子ノードのあるNode

    +

    木データ構造を作る: 子ノードのあるNode

    @@ -22725,7 +22725,7 @@

    The Rust Programming Language

    理由は、leafにはbranchへの参照がなく、関係していることを知らないからです。leafbranchが親であることを知ってほしいです。 次はそれを行います。

    -

    子供から親に参照を追加する

    +

    子供から親に参照を追加する

    @@ -22846,7 +22846,7 @@

    The Rust Programming Language

    無限の出力が欠けているということは、このコードは循環参照しないことを示唆します。 このことは、Rc::strong_countRc::weak_countを呼び出すことで得られる値を見てもわかります。

    -

    strong_countweak_countへの変更を可視化する

    +

    strong_countweak_countへの変更を可視化する

    @@ -22956,7 +22956,7 @@

    The Rust Programming Language

    Nodeの定義で子供から親への関係はWeak<T>参照になるべきと指定することで、 循環参照やメモリリークを引き起こさずに親ノードに子ノードを参照させたり、その逆を行うことができます。

    -

    まとめ

    +

    まとめ

    @@ -22986,7 +22986,7 @@

    The Rust Programming Language

    次は、Rustでの並行性について語ります。もういくつか新しいスマートポインタについてさえも学ぶでしょう。

    -

    恐れるな!並行性

    +

    恐れるな!並行性

    @@ -23062,7 +23062,7 @@

    The Rust Programming Language

  • 標準ライブラリが提供する型だけでなく、ユーザが定義した型に対してもRustの並行性の安全保証を拡張するSyncSendトレイト
  • -

    スレッドを使用してコードを同時に走らせる

    +

    スレッドを使用してコードを同時に走らせる

    @@ -23147,7 +23147,7 @@

    The Rust Programming Language

    今やRustにおけるスレッドを定義したので、標準ライブラリで提供されているスレッド関連のAPIの使用法を探究しましょう。

    -

    spawnで新規スレッドを生成する

    +

    spawnで新規スレッドを生成する

    @@ -23212,7 +23212,7 @@

    The Rust Programming Language

    このコードを実行してメインスレッドの出力しか目の当たりにできなかったり、オーバーラップがなければ、 範囲の値を増やしてOSがスレッド切り替えを行う機会を増やしてみてください。

    -

    joinハンドルで全スレッドの終了を待つ

    +

    joinハンドルで全スレッドの終了を待つ

    @@ -23328,7 +23328,7 @@

    The Rust Programming Language

    どこでjoinを呼ぶかといったほんの些細なことが、スレッドが同時に走るかどうかに影響することもあります。

    -

    スレッドでmoveクロージャを使用する

    +

    スレッドでmoveクロージャを使用する

    moveクロージャは、thread::spawnとともによく使用されます。 @@ -23513,7 +23513,7 @@

    The Rust Programming Language

    スレッドとスレッドAPIの基礎知識を得たので、スレッドでできることを見ていきましょう。

    -

    メッセージ受け渡しを使ってスレッド間でデータを転送する

    +

    メッセージ受け渡しを使ってスレッド間でデータを転送する

    @@ -23705,7 +23705,7 @@

    The Rust Programming Language

    完璧です!

    -

    チャンネルと所有権の転送

    +

    チャンネルと所有権の転送

    @@ -23769,7 +23769,7 @@

    The Rust Programming Language

    値がムーブされると、受信側が所有権を得るのです。これにより、送信後に誤って再度値を使用するのを防いでくれます; 所有権システムが、万事問題ないことを確認してくれます。

    -

    複数の値を送信し、受信側が待機するのを確かめる

    +

    複数の値を送信し、受信側が待機するのを確かめる

    @@ -23837,7 +23837,7 @@

    The Rust Programming Language

    メインスレッドのforループには停止したり、遅れせたりするコードは何もないので、 メインスレッドが立ち上げたスレッドから値を受け取るのを待機していることがわかります。

    -

    転送機をクローンして複数の生成器を作成する

    +

    転送機をクローンして複数の生成器を作成する

    @@ -23927,7 +23927,7 @@

    The Rust Programming Language

    チャンネルの動作方法を見たので、他の並行性に目を向けましょう。

    -

    状態共有並行性

    +

    状態共有並行性

    @@ -23955,7 +23955,7 @@

    The Rust Programming Language

    Rustの型システムと所有権規則は、この管理を正しく行う大きな助けになります。 例として、メモリ共有を行うより一般的な並行性の基本型の一つであるミューテックスを見てみましょう。

    -

    ミューテックスを使用して一度に1つのスレッドからデータにアクセスすることを許可する

    +

    ミューテックスを使用して一度に1つのスレッドからデータにアクセスすることを許可する

    @@ -23998,7 +23998,7 @@

    The Rust Programming Language

    ミューテックスの管理は、正しく行うのに著しく技工を要することがあるので、多くの人がチャンネルに熱狂的になるわけです。 しかしながら、Rustの型システムと所有権規則のおかげで、ロックとアンロックをおかしくすることはありません。

    -

    Mutex<T>のAPI

    +

    Mutex<T>のAPI

    ミューテックスの使用方法の例として、ミューテックスをシングルスレッドの文脈で使うことから始めましょう。 @@ -24061,7 +24061,7 @@

    The Rust Programming Language

    ロックをドロップした後、ミューテックスの値を出力し、内部のi32の値を6に変更できたことが確かめられるのです。

    -

    複数のスレッド間でMutex<T>を共有する

    +

    複数のスレッド間でMutex<T>を共有する

    @@ -24233,7 +24233,7 @@

    The Rust Programming Language

    ループの違う繰り返しにある違うスレッドをコンパイラは指し示せないからです。 第15章で議論した複数所有権メソッドによりコンパイルエラーを修正しましょう。

    -

    複数のスレッドで複数の所有権

    +

    複数のスレッドで複数の所有権

    @@ -24329,7 +24329,7 @@

    The Rust Programming Language

    これは間違ったカウントにつながる可能性があり、今度はメモリリークや、使用し終わる前に値がドロップされることにつながる可能性のある潜在的なバグです。 必要なのは、いかにもRc<T>のようだけれども、参照カウントへの変更をスレッドセーフに行うものです。

    -

    Arc<T>で原子的な参照カウント

    +

    Arc<T>で原子的な参照カウント

    @@ -24401,7 +24401,7 @@

    The Rust Programming Language

    計算を独立した部分に小分けにし、その部分をスレッドに分割し、それからMutex<T>を使用して、 各スレッドに最終結果を更新させることができます。

    -

    RefCell<T>/Rc<T>Mutex<T>/Arc<T>の類似性

    +

    RefCell<T>/Rc<T>Mutex<T>/Arc<T>の類似性

    @@ -24435,7 +24435,7 @@

    The Rust Programming Language

    SendSyncトレイトと、それらを独自の型で使用する方法について語って、この章を締めくくります。

    -

    SyncSendトレイトで拡張可能な並行性

    +

    SyncSendトレイトで拡張可能な並行性

    @@ -24448,7 +24448,7 @@

    The Rust Programming Language

    ですが、2つの並行性概念が言語に埋め込まれています: std::markerトレイトのSyncSendです。

    -

    Sendでスレッド間の所有権の転送を許可する

    +

    Sendでスレッド間の所有権の転送を許可する

    @@ -24476,7 +24476,7 @@

    The Rust Programming Language

    完全にSendの型からなる型も全て自動的にSendと印付けされます。生ポインタを除くほとんどの基本型もSendで、 生ポインタについては第19章で議論します。

    -

    Syncで複数のスレッドからのアクセスを許可する

    +

    Syncで複数のスレッドからのアクセスを許可する

    @@ -24498,7 +24498,7 @@

    The Rust Programming Language

    スマートポインタのMutex<T>Syncで、「複数のスレッド間でMutex<T>を共有する」節で見たように、 複数のスレッドでアクセスを共有するのに使用することができます。

    -

    SendSyncを手動で実装するのは非安全である

    +

    SendSyncを手動で実装するのは非安全である

    @@ -24521,7 +24521,7 @@

    The Rust Programming Language

    訳注: 日本語版のThe Rustonomiconはこちらです。

    -

    まとめ

    +

    まとめ

    @@ -24556,7 +24556,7 @@

    The Rust Programming Language

    次は、Rustプログラムが肥大化するにつれて問題をモデル化し、解決策を構造化する慣例的な方法について話します。 さらに、Rustのイディオムがオブジェクト指向プログラミングで馴染み深いかもしれないイディオムにどのように関連しているかについても議論します。

    -

    Rustのオブジェクト指向プログラミング機能

    +

    Rustのオブジェクト指向プログラミング機能

    @@ -24577,7 +24577,7 @@

    The Rust Programming Language

    それらの特徴がこなれたRustでどう表現されるかを探究します。それからオブジェクト指向のデザインパターンをRustで実装する方法を示し、 そうすることとRustの強みを活用して代わりの解決策を実装する方法の代償を議論します。

    -

    オブジェクト指向言語の特徴

    +

    オブジェクト指向言語の特徴

    @@ -24591,7 +24591,7 @@

    The Rust Programming Language

    OOP言語は特定の一般的な特徴を共有しています。具体的には、オブジェクトやカプセル化、 継承などです。それらの個々の特徴が意味するものとRustがサポートしているかを見ましょう。

    -

    オブジェクトは、データと振る舞いを含む

    +

    オブジェクトは、データと振る舞いを含む

    @@ -24617,7 +24617,7 @@

    The Rust Programming Language

    implブロックが構造体とenumにメソッドを提供します。メソッドのある構造体とenumは、 オブジェクトとは呼ばれないものの、GoFのオブジェクト定義によると、同じ機能を提供します。

    -

    カプセル化は、実装詳細を隠蔽する

    +

    カプセル化は、実装詳細を隠蔽する

    @@ -24746,7 +24746,7 @@

    The Rust Programming Language

    カプセル化が、言語がオブジェクト指向と考えられるのに必要な側面ならば、Rustはその条件を満たしています。 コードの異なる部分でpubを使用するかしないかという選択肢のおかげで、実装詳細をカプセル化することが可能になります。

    -

    型システム、およびコード共有としての継承

    +

    型システム、およびコード共有としての継承

    @@ -24797,7 +24797,7 @@

    The Rust Programming Language

    -

    多相性

    +

    多相性

    多くの人にとって、多相性は、継承の同義語です。ですが、実際には複数の型のデータを取り扱えるコードを指すより一般的な概念です。 継承について言えば、それらの型は一般的にはサブクラスです。

    Rustは代わりにジェネリクスを使用して様々な可能性のある型を抽象化し、トレイト境界を使用してそれらの型が提供するものに制約を課します。 @@ -24823,7 +24823,7 @@

    The Rust Programming Language

    これらの理由により、継承ではなくトレイトオブジェクトを使用してRustは異なるアプローチを取っています。 Rustにおいて、トレイトオブジェクトがどう多相性を可能にするかを見ましょう。

    -

    トレイトオブジェクトで異なる型の値を許容する

    +

    トレイトオブジェクトで異なる型の値を許容する

    @@ -24879,7 +24879,7 @@

    The Rust Programming Language

    Componentインスタンスであるかのようにその型全部を扱い、この型に対してdrawを呼び出します。 ですが、Rustに継承は存在しないので、使用者に新しい型で拡張してもらうためにguiライブラリを構成する他の方法が必要です。

    -

    一般的な振る舞いにトレイトを定義する

    +

    一般的な振る舞いにトレイトを定義する

    @@ -25034,7 +25034,7 @@

    The Rust Programming Language

    Box<Button>Box<TextField>を含むVec<T>を保持できます。 この動作方法を見、それから実行時性能の裏の意味について語りましょう。

    -

    トレイトを実装する

    +

    トレイトを実装する

    @@ -25177,7 +25177,7 @@

    The Rust Programming Language

    componentsベクタでBox<Draw>を値の型として指定することで、Screenを、 drawメソッドを呼び出せる値を必要とするように定義できたのです。

    -

    注釈: ダックタイピングについて

    +

    注釈: ダックタイピングについて

    ご存知かもしれませんが、ダックタイピングについて補足です。ダックタイピングとは、動的型付け言語やC++のテンプレートで使用される、 特定のフィールドやメソッドがあることを想定してコンパイルを行い、実行時に実際にあることを確かめるというプログラミング手法です。 ダック・テストという思考法に由来するそうです。

    @@ -25232,7 +25232,7 @@

    The Rust Programming Language

    このエラーは、渡すことを意図していないものをScreenに渡しているので、異なる型を渡すべきか、 Screendrawを呼び出せるようにStringDrawを実装するべきのどちらかであることを知らせてくれています。

    -

    トレイトオブジェクトは、ダイナミックディスパッチを行う

    +

    トレイトオブジェクトは、ダイナミックディスパッチを行う

    @@ -25267,7 +25267,7 @@

    The Rust Programming Language

    そのため、ある種の最適化が不可能になります。ですが、リスト17-5で記述し、 リスト17-9ではサポートできたコードで追加の柔軟性を確かに得られたので、考慮すべき代償です。

    -

    トレイトオブジェクトには、オブジェクト安全性が必要

    +

    トレイトオブジェクトには、オブジェクト安全性が必要

    @@ -25348,7 +25348,7 @@

    The Rust Programming Language

    このエラーは、このようにこのトレイトをトレイトオブジェクトとして使用することはできないことを意味しています。 オブジェクト安全性についての詳細に興味があるのなら、Rust RFC 255を参照されたし。

    -

    オブジェクト指向デザインパターンを実装する

    +

    オブジェクト指向デザインパターンを実装する

    @@ -25452,7 +25452,7 @@

    The Rust Programming Language

    状態の変化を直接管理する必要はありません。また、ユーザは、 査読前に記事を公開するなど状態を誤ることはありません。

    -

    Postを定義し、草稿状態で新しいインスタンスを生成する

    +

    Postを定義し、草稿状態で新しいインスタンスを生成する

    @@ -25512,7 +25512,7 @@

    The Rust Programming Language

    新しいPostを作る度に、草稿から始まることが保証されます。Poststateフィールドは非公開なので、 Postを他の状態で作成する方法はないのです!Post::new関数では、contentフィールドを新しい空のStringにセットしています。

    -

    記事の内容のテキストを格納する

    +

    記事の内容のテキストを格納する

    @@ -25555,7 +25555,7 @@

    The Rust Programming Language

    この振る舞いは、記事の状態によらないので、ステートパターンの一部ではありません。add_textメソッドは、 stateフィールドと全く相互作用しませんが、サポートしたい振る舞いの一部ではあります。

    -

    草稿の記事の内容は空であることを保証する

    +

    草稿の記事の内容は空であることを保証する

    @@ -25595,7 +25595,7 @@

    The Rust Programming Language

    -

    記事の査読を要求すると、状態が変化する

    +

    記事の査読を要求すると、状態が変化する

    次に、記事の査読を要求する機能を追加する必要があり、これをすると、状態がDraftからPendingReviewに変わるはずです。 @@ -25703,7 +25703,7 @@

    The Rust Programming Language

    これでPostPendingReviewDraft状態になり得ますが、PendingReview状態でも、 同じ振る舞いが欲しいです。もうリスト17-11は11行目まで動くようになりました!

    -

    contentの振る舞いを変化させるapproveメソッドを追加する

    +

    contentの振る舞いを変化させるapproveメソッドを追加する

    @@ -25900,7 +25900,7 @@

    The Rust Programming Language

    出来上がりました。要するに、リスト17-11はもう動くようになったのです!ブログ記事ワークフローの規則でステートパターンを実装しました。 その規則に関連するロジックは、Post中に散乱するのではなく、ステートオブジェクトに息づいています。

    -

    ステートパターンの代償

    +

    ステートパターンの代償

    @@ -25986,7 +25986,7 @@

    The Rust Programming Language

    オブジェクト指向言語で定義されている通り忠実にステートパターンを実装することで、 Rustの強みをできるだけ発揮していません。blogクレートに対して行える無効な状態と遷移をコンパイルエラーにできる変更に目を向けましょう。

    -

    状態と振る舞いを型としてコード化する

    +

    状態と振る舞いを型としてコード化する

    @@ -26080,7 +26080,7 @@

    The Rust Programming Language

    従って、これでプログラムは、全ての記事が草稿記事から始まり、草稿記事は表示できる内容がないことを保証します。 この制限をかいくぐる試みは、全てコンパイルエラーに落ち着くでしょう。

    -

    遷移を異なる型への変形として実装する

    +

    遷移を異なる型への変形として実装する

    @@ -26219,7 +26219,7 @@

    The Rust Programming Language

    コンパイル時に一部のバグを回避できるなどの利益が得られることもあります。オブジェクト指向のパターンは、 オブジェクト指向言語にはない所有権などの特定の機能によりRustでは、必ずしも最善の解決策ではないでしょう。

    -

    まとめ

    +

    まとめ

    @@ -26241,7 +26241,7 @@

    The Rust Programming Language

    次は、パターンを見ます。パターンも多くの柔軟性を可能にするRustの別の機能です。 本全体を通して僅かに見かけましたが、まだその全能力は目の当たりにしていません。さあ、行きましょう!

    -

    パターンとマッチング

    +

    パターンとマッチング

    @@ -26284,14 +26284,14 @@

    The Rust Programming Language

    目撃する可能性のある色々な種類のパターン記法を講義します。章の終わりまでに、 パターンを使用して多くの概念をはっきり表現する方法を知るでしょう。

    -

    パターンが使用されることのある箇所全部

    +

    パターンが使用されることのある箇所全部

    Rustにおいて、パターンはいろんな箇所に出現し、そうと気づかないうちにたくさん使用してきました! この節は、パターンが合法な箇所全部を議論します。

    -

    matchアーム

    +

    matchアーム

    @@ -26322,7 +26322,7 @@

    The Rust Programming Language

    例えば、_パターンは、指定されていないあらゆる値を無視したい時に有用です。 _パターンについて詳しくは、この章の後ほど、「パターンで値を無視する」節で講義します。

    -

    条件分岐if let

    +

    条件分岐if let

    @@ -26405,7 +26405,7 @@

    The Rust Programming Language

    if let式を使うことの欠点は、コンパイラが網羅性を確認してくれないことです。一方でmatch式ではしてくれます。 最後のelseブロックを省略して故に、扱い忘れたケースがあっても、コンパイラは、ロジックバグの可能性を指摘してくれないでしょう。

    -

    while let条件分岐ループ

    +

    while let条件分岐ループ

    @@ -26438,7 +26438,7 @@

    The Rust Programming Language

    ベクタが空なら、popNoneを返します。whileループはpopSomeを返す限り、ブロックのコードを実行し続けます。 popNoneを返すと、ループは停止します。while letを使用してスタックから全ての要素を取り出せるのです。

    -

    forループ

    +

    forループ

    @@ -26476,7 +26476,7 @@

    The Rust Programming Language

    enumerateの最初の呼び出しは、タプル(0, 'a')を生成します。この値がパターン(index, value)とマッチさせられると、 index0value'a'になり、出力の最初の行を出力するのです。

    -

    let

    +

    let

    @@ -26556,7 +26556,7 @@

    The Rust Programming Language

    _..を使用できるでしょう。パターンに変数が多すぎるというのが問題なら、変数の数がタプルの要素数と一致するように変数を減らすことで、 型を一致させることが解決策です。

    -

    関数の引数

    +

    関数の引数

    @@ -26609,7 +26609,7 @@

    The Rust Programming Language

    この時点で、パターンを使用する方法をいくつか見てきましたが、パターンを使用できる箇所全部で同じ動作をするわけではありません。 パターンが論駁不可能でなければならない箇所もあります。他の状況では、論駁可能にもなり得ます。この2つの概念を次に議論します。

    -

    論駁可能性: パターンが合致しないかどうか

    +

    論駁可能性: パターンが合致しないかどうか

    @@ -26726,14 +26726,14 @@

    The Rust Programming Language

    今やパターンを使用すべき箇所と論駁可能と論駁不可能なパターンの違いを知ったので、 パターンを生成するために使用できる全ての記法を講義しましょう。

    -

    パターン記法

    +

    パターン記法

    本全体で、多くの種類のパターンの例を見かけてきました。この節では、パターンで合法な記法全てを集め、 それぞれを使用したくなる可能性がある理由について議論します。

    -

    リテラルにマッチする

    +

    リテラルにマッチする

    第6章で目撃したように、パターンを直接リテラルに合致させられます。以下のコードが例を挙げています:

    @@ -26754,7 +26754,7 @@

    The Rust Programming Language

    このコードは、xの値が1なので、oneを出力します。この記法は、コードが特定の具体的な値を得た時に行動を起こしてほしい時に有用です。

    -

    名前付き変数にマッチする

    +

    名前付き変数にマッチする

    @@ -26830,7 +26830,7 @@

    The Rust Programming Language

    代わりにマッチガード条件式を使用する必要があるでしょう。マッチガードについては、後ほど、 「マッチガードで追加の条件式」節で語ります。

    -

    複数のパターン

    +

    複数のパターン

    @@ -26855,7 +26855,7 @@

    The Rust Programming Language

    このコードは、one or twoを出力します。

    -

    ...で値の範囲に合致させる

    +

    ...で値の範囲に合致させる

    @@ -26906,13 +26906,13 @@

    The Rust Programming Language

    コンパイラにはcが最初のパターンの範囲にあることがわかり、early ASCII letterと出力されます。

    -

    分配して値を分解する

    +

    分配して値を分解する

    また、パターンを使用して構造体、enum、タプル、参照を分配し、これらの値の異なる部分を使用することもできます。 各値を見ていきましょう。

    -

    構造体を分配する

    +

    構造体を分配する

    リスト18-12は、let文でパターンを使用して分解できる2つのフィールドxyのあるPoint構造体を示しています。

    @@ -27026,7 +27026,7 @@

    The Rust Programming Language

    この例で、値pは0を含むxの力で2番目のアームに一致するので、このコードはOn the y axis at 7と出力します。

    -

    enumを分配する

    +

    enumを分配する

    @@ -27104,7 +27104,7 @@

    The Rust Programming Language

    パターンは、タプルと一致させるために指定するパターンと類似しています。パターンの変数の数は、 マッチ対象の列挙子の要素数と一致しなければなりません。

    -

    参照を分配する

    +

    参照を分配する

    @@ -27168,7 +27168,7 @@

    The Rust Programming Language

    このエラーは、コンパイラがクロージャに&Pointと一致することを期待しているのに、 Pointへの参照ではなく、Point値に直接一致させようとしたことを示唆しています。

    -

    構造体とタプルを分配する

    +

    構造体とタプルを分配する

    @@ -27191,7 +27191,7 @@

    The Rust Programming Language

    パターンで分配することは、構造体の各フィールドからの値のように、複数の値をお互いに区別して使用する便利な方法です。

    -

    パターンの値を無視する

    +

    パターンの値を無視する

    @@ -27205,7 +27205,7 @@

    The Rust Programming Language

    他のパターン内で_パターンを使用すること、アンダースコアで始まる名前を使用すること、..を使用して値の残りの部分を無視することです。 これらのパターンそれぞれを使用する方法と理由を探究しましょう。

    -

    _で値全体を無視する

    +

    _で値全体を無視する

    @@ -27241,7 +27241,7 @@

    The Rust Programming Language

    特定の型シグニチャが必要だけれども、自分の実装の関数本体では引数の1つが必要ない時などです。 そうすれば、代わりに名前を使った場合のようには、未使用関数引数についてコンパイラが警告することはないでしょう。

    -

    ネストされた_で値の一部を無視する

    +

    ネストされた_で値の一部を無視する

    @@ -27315,7 +27315,7 @@

    The Rust Programming Language

    このコードは、Some numbers: 2, 8, 32と出力し、値4と16は無視されます。

    -

    名前を_で始めて未使用の変数を無視する

    +

    名前を_で始めて未使用の変数を無視する

    @@ -27386,7 +27386,7 @@

    The Rust Programming Language

    このコードは、sを何にも束縛しないので、ただ単に上手く動きます。つまり、ムーブされないのです。

    -

    ..で値の残りの部分を無視する

    +

    ..で値の残りの部分を無視する

    @@ -27488,7 +27488,7 @@

    The Rust Programming Language

    変数名のsecondは、コンパイラにとってなんの特別な意味もなく、このように2箇所で..を使うのは曖昧なので、 コンパイルエラーになります。

    -

    refref mutでパターンに参照を生成する

    +

    refref mutでパターンに参照を生成する

    @@ -27590,7 +27590,7 @@

    The Rust Programming Language

    この例はコンパイルが通り、robot_name is: Some("Another name")と出力するでしょう。 nameは可変参照なので、値を可変化するためにマッチアーム内で*演算子を使用して参照外しする必要があります。

    -

    マッチガードで追加の条件式

    +

    マッチガードで追加の条件式

    @@ -27732,7 +27732,7 @@

    The Rust Programming Language

    コードを実行後には、優先度の動作は明らかになります: マッチガードが|演算子で指定される値のリストの最後の値にしか適用されないなら、 アームはマッチし、プログラムはyesと出力したでしょう。

    -

    @束縛

    +

    @束縛

    @@ -27797,7 +27797,7 @@

    The Rust Programming Language

    @を使用することで、値を検査しつつ、1つのパターン内で変数に保存させてくれるのです。

    -

    まとめ

    +

    まとめ

    @@ -27812,7 +27812,7 @@

    The Rust Programming Language

    次の本書の末尾から2番目の章では、Rustの多彩な機能の高度な視点に目を向けます。

    -

    高度な機能

    +

    高度な機能

    @@ -27844,7 +27844,7 @@

    The Rust Programming Language

    皆さんのための何かがあるRustの機能の盛大な儀式です!さあ、飛び込みましょう!

    -

    Unsafe Rust

    +

    Unsafe Rust

    @@ -27877,7 +27877,7 @@

    The Rust Programming Language

    直接OSと相互作用したり、独自のOSを書くことさえもそうです。低レベルなシステムプログラミングに取り組むことは、 言語の目標の1つなのです。unsafe Rustでできることとその方法を探究しましょう。

    -

    unsafeの強大な力(superpower)

    +

    unsafeの強大な力(superpower)

    @@ -27934,7 +27934,7 @@

    The Rust Programming Language

    4つのunsafeなsuperpowerを順に見ていきましょう。unsafeなコードへの安全なインターフェイスを提供する一部の抽象化にも目を向けます。

    -

    生ポインタを参照外しする

    +

    生ポインタを参照外しする

    @@ -28057,7 +28057,7 @@

    The Rust Programming Language

    Cコードとのインターフェイスです。別のユースケースは、借用チェッカーには理解できない安全な抽象を構成する時です。 unsafeな関数を導入し、それからunsafeコードを使用する安全な抽象の例に目を向けます。

    -

    unsafeな関数やメソッドを呼ぶ

    +

    unsafeな関数やメソッドを呼ぶ

    @@ -28106,7 +28106,7 @@

    The Rust Programming Language

    unsafe関数の本体は、実効的にunsafeブロックになるので、unsafe関数内でunsafeな別の処理を行うのに、 別のunsafeブロックは必要ないのです。

    -

    unsafeコードに安全な抽象を行う

    +

    unsafeコードに安全な抽象を行う

    @@ -28278,7 +28278,7 @@

    The Rust Programming Language

    この任意の場所のメモリは所有していなく、このコードが生成するスライスに有効なi32値が含まれる保証もありません。 sliceを有効なスライスであるかのように使用しようとすると、未定義動作に陥ります。

    -

    extern関数を使用して、外部のコードを呼び出す

    +

    extern関数を使用して、外部のコードを呼び出す

    @@ -28346,7 +28346,7 @@

    The Rust Programming Language

    -

    他の言語からRustの関数を呼び出す

    +

    他の言語からRustの関数を呼び出す

    また、externを使用して他の言語にRustの関数を呼ばせるインターフェイスを生成することもできます。 externブロックの代わりに、externキーワードを追加し、fnキーワードの直前に使用するABIを指定します。 さらに、#[no_mangle]注釈を追加してRustコンパイラに関数名をマングルしないように指示する必要もあります。 @@ -28366,7 +28366,7 @@

    The Rust Programming Language

    このexternの使用法では、unsafeは必要ありません。

    -

    可変で静的な変数にアクセスしたり、変更する

    +

    可変で静的な変数にアクセスしたり、変更する

    @@ -28453,7 +28453,7 @@

    The Rust Programming Language

    Rustは可変で静的な変数をunsafeと考えるのです。可能なら、コンパイラが、データが異なるスレッドからアクセスされることが安全に行われているかを確認するように、 第16章で議論した並行性テクニックとスレッド安全なスマートポインタを使用するのが望ましいです。

    -

    unsafeなトレイトを実装する

    +

    unsafeなトレイトを実装する

    @@ -28497,7 +28497,7 @@

    The Rust Programming Language

    複数のスレッドから安全にアクセスできるという保証を保持しているか確かめられません; 故に、そのチェックを手動で行い、 unsafeでそのように示唆する必要があります。

    -

    いつunsafeコードを使用するべきか

    +

    いつunsafeコードを使用するべきか

    @@ -28508,7 +28508,7 @@

    The Rust Programming Language

    unsafeコードを使用する理由があるなら、そうすることができ、明示的にunsafe注釈をすることで問題が起きたら、 その原因を追求するのが容易になります。

    -

    高度なライフタイム

    +

    高度なライフタイム

    @@ -28528,7 +28528,7 @@

    The Rust Programming Language

  • トレイトオブジェクトのライフタイムの推論: コンパイラにトレイトオブジェクトのライフタイムを推論させることと指定する必要があるタイミング
  • -

    ライフタイム・サブタイピングにより、あるライフタイムが他よりも長生きすることを保証する

    +

    ライフタイム・サブタイピングにより、あるライフタイムが他よりも長生きすることを保証する

    @@ -28858,7 +28858,7 @@

    The Rust Programming Language

    非常に長くぐにゃぐにゃした例でしたが、この章の冒頭で触れたように、Rustの高度な機能は、非常に限定的です。 この例で解説した記法は、あまり必要になりませんが、そのような場面では、何かを参照し、それに必要なライフタイムを与える方法を知っているでしょう。

    -

    ジェネリックな型への参照に対するライフタイム境界

    +

    ジェネリックな型への参照に対するライフタイム境界

    @@ -28958,7 +28958,7 @@

    The Rust Programming Language

    参照が十分長生きしないと心配することに関しては、参照が何もない型と永久に生きる参照がある型を現実的に区別できません: どちらも、参照が参照先のライフタイムよりも短いか決定することに関しては同じです。

    -

    トレイトオブジェクトライフタイムの推論

    +

    トレイトオブジェクトライフタイムの推論

    @@ -29022,14 +29022,14 @@

    The Rust Programming Language

    次は、トレイトを管理する他の一部の高度な機能に目を向けましょう。

    -

    高度なトレイト

    +

    高度なトレイト

    最初にトレイトについて講義したのは、第10章の「トレイト: 共通の振る舞いを定義する」節でしたが、 ライフタイム同様、より高度な詳細は議論しませんでした。今や、Rustに詳しくなったので、核心に迫れるでしょう。

    -

    関連型でトレイト定義においてプレースホルダーの型を指定する

    +

    関連型でトレイト定義においてプレースホルダーの型を指定する

    @@ -29132,7 +29132,7 @@

    The Rust Programming Language

    1つしかimpl Iterator for Counterがないからです。Counternextを呼び出す度に、 u32値のイテレータが欲しいと指定しなくてもよいわけです。

    -

    デフォルトのジェネリック型引数と演算子オーバーロード

    +

    デフォルトのジェネリック型引数と演算子オーバーロード

    @@ -29278,7 +29278,7 @@

    The Rust Programming Language

    最初の目的は2番目に似ていますが、逆です: 既存のトレイトに型引数を追加したいなら、既定を与えて、 既存の実装コードを破壊せずにトレイトの機能を拡張できるのです。

    -

    明確化のためのフルパス記法: 同じ名前のメソッドを呼ぶ

    +

    明確化のためのフルパス記法: 同じ名前のメソッドを呼ぶ

    @@ -29593,7 +29593,7 @@

    The Rust Programming Language

    フルパス記法を使用することもできるでしょうが、プログラムの他の情報からコンパイラが推論できるこの記法のどの部分も省略することが許容されています。 同じ名前を使用する実装が複数あり、どの実装を呼び出したいかコンパイラが特定するのに助けが必要な場合だけにこのより冗長な記法を使用する必要があるのです。

    -

    スーパートレイトを使用して別のトレイト内で、あるトレイトの機能を必要とする

    +

    スーパートレイトを使用して別のトレイト内で、あるトレイトの機能を必要とする

    @@ -29709,7 +29709,7 @@

    The Rust Programming Language

    そうすれば、PointOutlinePrintトレイトを実装してもコンパイルは成功し、 Pointインスタンスに対してoutline_printを呼び出し、アスタリスクのふちの中に表示することができます。

    -

    ニュータイプパターンを使用して外部の型に外部のトレイトを実装する

    +

    ニュータイプパターンを使用して外部の型に外部のトレイトを実装する

    @@ -29784,7 +29784,7 @@

    The Rust Programming Language

    もう、トレイトに関してニュータイプパターンが使用される方法を知りました; トレイトが関連しなくても、 有用なパターンでもあります。焦点を変更して、Rustの型システムと相互作用する一部の高度な方法を見ましょう。

    -

    高度な型

    +

    高度な型

    @@ -29799,7 +29799,7 @@

    The Rust Programming Language

    注釈: 次の節は、前節「外部の型に外部のトレイトを実装するニュータイプパターン」を読了済みであることを前提にしています。

    -

    型安全性と抽象化を求めてニュータイプパターンを使用する

    +

    型安全性と抽象化を求めてニュータイプパターンを使用する

    @@ -29832,7 +29832,7 @@

    The Rust Programming Language

    ニュータイプパターンは、カプセル化を実現して実装の詳細を隠匿する軽い方法であり、 実装の詳細を隠匿することは、第17章の「カプセル化は実装詳細を隠蔽する」節で議論しましたね。

    -

    型エイリアスで型同義語を生成する

    +

    型エイリアスで型同義語を生成する

    @@ -29975,7 +29975,7 @@

    The Rust Programming Language

    別名なので、ただのResult<T, E>であり、要するにResult<T, E>に対して動くメソッドはなんでも使えるし、 ?演算子のような特殊な記法も使えます。

    -

    never型は絶対に返らない

    +

    never型は絶対に返らない

    @@ -30083,7 +30083,7 @@

    The Rust Programming Language

    ここで、ループは終わりませんので、!が式の値です。ところが、breakを含んでいたら、これは真実にはならないでしょう。 breakに到達した際にループが終了してしまうからです。

    -

    動的サイズ付け型とSizedトレイト

    +

    動的サイズ付け型とSizedトレイト

    @@ -30189,12 +30189,12 @@

    The Rust Programming Language

    次は、関数とクロージャについて語ります!

    -

    高度な関数とクロージャ

    +

    高度な関数とクロージャ

    最後に関数とクロージャに関連する高度な機能の一部を探究し、これには関数ポインタとクロージャの返却が含まれます。

    -

    関数ポインタ

    +

    関数ポインタ

    @@ -30294,7 +30294,7 @@

    The Rust Programming Language

    このスタイルを好む方もいますし、クロージャを使うのを好む方もいます。どちらも結果的に同じコードにコンパイルされるので、 どちらでも、自分にとって明確な方を使用してください。

    -

    クロージャを返却する

    +

    クロージャを返却する

    @@ -30343,7 +30343,7 @@

    The Rust Programming Language

    このコードは、問題なくコンパイルできます。トレイトオブジェクトについて詳しくは、 第17章の「トレイトオブジェクトで異なる型の値を許容する」節を参照してください。

    -

    まとめ

    +

    まとめ

    @@ -30357,7 +30357,7 @@

    The Rust Programming Language

    次は、本を通して議論してきた全てを実践に配備し、もう1つプロジェクトを(こな)します!

    -

    最後のプロジェクト: マルチスレッドのWebサーバを構築する

    +

    最後のプロジェクト: マルチスレッドのWebサーバを構築する

    @@ -30403,7 +30403,7 @@

    The Rust Programming Language

    他の言語で可能だったり実践的だったりするよりも低レベルまで行くことができます。一般的な考えと将来使う可能性のあるクレートの背後にある技術を学べるように、 手動で基本的なHTTPサーバとスレッドプールを書きます。

    -

    シングルスレッドのWebサーバを構築する

    +

    シングルスレッドのWebサーバを構築する

    @@ -30432,7 +30432,7 @@

    The Rust Programming Language

    技術的にはHTTPを他のプロトコルとともに使用することができますが、過半数の場合、HTTPはTCPの上にデータを送信します。 TCPとHTTPのリクエストとレスポンスの生のバイトを取り扱います。

    -

    TCP接続をリッスンする

    +

    TCP接続をリッスンする

    @@ -30566,7 +30566,7 @@

    The Rust Programming Language

    プログラムを止めることを忘れないでください。そして、一連のコード変更を行った後にcargo runを再起動し、 最新のコードを実行していることを確かめてください。

    -

    リクエストを読み取る

    +

    リクエストを読み取る

    @@ -30675,7 +30675,7 @@

    The Rust Programming Language

    このリクエストデータを噛み砕いて、ブラウザが我々のプログラムに何を要求しているかを理解しましょう。

    -

    HTTPリクエストを詳しく見る

    +

    HTTPリクエストを詳しく見る

    HTTPはテキストベースのプロトコルで、1つの要求はこのようなフォーマットに則っています:

    Method Request-URI HTTP-Version CRLF
    @@ -30723,7 +30723,7 @@ 

    The Rust Programming Language

    さて、ブラウザが要求しているものがわかったので、何かデータを返しましょう!

    -

    レスポンスを記述する

    +

    レスポンスを記述する

    さて、クライアントのリクエストに対する返答としてデータの送信を実装します。レスポンスは、以下のようなフォーマットです:

    @@ -30797,7 +30797,7 @@

    The Rust Programming Language

    Cargoからの出力以外には何も出力はありません。Webブラウザで127.0.0.1:7878をロードすると、 エラーではなく空のページが得られるはずです。HTTPリクエストとレスポンスを手で実装したばかりなのです!

    -

    本物のHTMLを返す

    +

    本物のHTMLを返す

    @@ -30882,7 +30882,7 @@

    The Rust Programming Language

    この同じHTMLレスポンスが得られるということです。我々のサーバはかなり限定的で、多くのWebサーバとは異なっています。 リクエストに基づいてレスポンスをカスタマイズし、/ への合法なリクエストに対してのみHTMLファイルを送り返したいです。

    -

    リクエストにバリデーションをかけ、選択的にレスポンスを返す

    +

    リクエストにバリデーションをかけ、選択的にレスポンスを返す

    @@ -31022,7 +31022,7 @@

    The Rust Programming Language

    これらの変更とともに、もう一度サーバを実行してください。127.0.0.1:7878を要求すると、 hello.htmlの中身が返り、127.0.0.1:7878/fooなどの他のリクエストには404.htmlからのエラーHTMLが返るはずです。

    -

    リファクタリングの触り

    +

    リファクタリングの触り

    @@ -31101,7 +31101,7 @@

    The Rust Programming Language

    何か遅いリクエストをシミュレーションすることで、それが問題になる可能性を調査しましょう。 それから1度にサーバが複数のリクエストを扱えるように修正します。

    -

    シングルスレッドサーバをマルチスレッド化する

    +

    シングルスレッドサーバをマルチスレッド化する

    @@ -31115,7 +31115,7 @@

    The Rust Programming Language

    続くリクエストは長いリクエストが完了するまで待たなければならなくなるでしょう。これを修正する必要がありますが、 まずは、実際に問題が起こっているところを見ます。

    -

    現在のサーバの実装で遅いリクエストをシミュレーションする

    +

    現在のサーバの実装で遅いリクエストをシミュレーションする

    @@ -31183,7 +31183,7 @@

    The Rust Programming Language

    より多くのリクエストが遅いリクエストの背後に回ってしまうのを回避するようWebサーバが動く方法を変える方法は複数あります; これから実装するのは、スレッドプールです。

    -

    スレッドプールでスループットを向上させる

    +

    スレッドプールでスループットを向上させる

    @@ -31244,7 +31244,7 @@

    The Rust Programming Language

    第12章のプロジェクトでTDDを使用したように、ここではCompiler Driven Development(コンパイラ駆動開発)を使用します。 欲しい関数を呼び出すコードを書き、それからコンパイラの出すエラーを見てコードが動くように次に何を変更すべきかを決定します。

    -

    各リクエストに対してスレッドを立ち上げられる場合のコードの構造

    +

    各リクエストに対してスレッドを立ち上げられる場合のコードの構造

    @@ -31287,7 +31287,7 @@

    The Rust Programming Language

    確かに / へのリクエストは、/sleepが完了するのを待機しなくても済むことがわかるでしょう。 ですが、前述したように、無制限にスレッドを生成することになるので、これは最終的にシステムを参らせてしまうでしょう。

    -

    有限数のスレッド用に似たインターフェイスを作成する

    +

    有限数のスレッド用に似たインターフェイスを作成する

    @@ -31336,7 +31336,7 @@

    The Rust Programming Language

    これはクロージャを取り、実行するためにプール内のスレッドに与えます。このコードはまだコンパイルできませんが、 コンパイラがどう修正したらいいかガイドできるように試してみます。

    -

    コンパイラ駆動開発でThreadPool構造体を構築する

    +

    コンパイラ駆動開発でThreadPool構造体を構築する

    @@ -31565,7 +31565,7 @@

    The Rust Programming Language

    ここが単体テストを書き始めて、コードがコンパイルでき、かつ欲しい振る舞いを保持していることを確認するのに良い機会でしょう。

    -

    newでスレッド数を検査する

    +

    newでスレッド数を検査する

    @@ -31636,7 +31636,7 @@

    The Rust Programming Language

    pub fn new(size: usize) -> Result<ThreadPool, PoolCreationError> {
     
    -

    スレッドを格納するスペースを生成する

    +

    スレッドを格納するスペースを生成する

    @@ -31715,7 +31715,7 @@

    The Rust Programming Language

    再びcargo checkを実行すると、もういくつか警告が出るものの、成功するはずです。

    -

    ThreadPoolからスレッドにコードを送信する責任を負うWorker構造体

    +

    ThreadPoolからスレッドにコードを送信する責任を負うWorker構造体

    @@ -31840,7 +31840,7 @@

    The Rust Programming Language

    このコードはコンパイルでき、ThreadPool::newへの引数として指定した数のWorkerインスタンスを格納します。 ですがそれでもexecuteで得るクロージャを処理してはいません。次は、それをする方法に目を向けましょう。

    -

    チャンネル経由でスレッドにリクエストを送信する

    +

    チャンネル経由でスレッドにリクエストを送信する

    @@ -32104,7 +32104,7 @@

    The Rust Programming Language

    これらの変更でコードはコンパイルできます!ゴールはもうすぐそこです!

    -

    executeメソッドを実装する

    +

    executeメソッドを実装する

    @@ -32457,7 +32457,7 @@

    The Rust Programming Language

    これにより、複数のリクエストを並行で提供し、ロックはrecvの呼び出しの間は保持されるけれども、 job.call_boxの呼び出しの前には解放されることを保証します。

    -

    正常なシャットダウンと片付け

    +

    正常なシャットダウンと片付け

    @@ -32478,7 +32478,7 @@

    The Rust Programming Language

    そして、スレッドに新しいリクエストの受付を停止し、終了するように教える方法を実装します。 このコードが動いているのを確かめるために、サーバを変更して正常にスレッドプールを終了する前に2つしかリクエストを受け付けないようにします。

    -

    ThreadPoolDropトレイトを実装する

    +

    ThreadPoolDropトレイトを実装する

    @@ -32620,7 +32620,7 @@

    The Rust Programming Language

    ワーカーのスレッドが既にNoneなら、ワーカーはスレッドを既に片付け済みであることがわかるので、 その場合には何も起きません。

    -

    スレッドに仕事をリッスンするのを止めるよう通知する

    +

    スレッドに仕事をリッスンするのを止めるよう通知する

    @@ -33077,7 +33077,7 @@

    The Rust Programming Language

    そして、APIと頑健性を我々が実装したものと比較する。 -

    まとめ

    +

    まとめ

    @@ -33087,12 +33087,12 @@

    The Rust Programming Language

    もう、ご自身のRustプロジェクトや他の方のプロジェクトのお手伝いをする準備ができています。 あなたがこれからのRustの旅で遭遇する、あらゆる困難の手助けを是非とも行いたいRustaceanたちの温かいコミュニティがあることを心に留めておいてくださいね。

    -

    付録

    +

    付録

    以下の節は、Rustの旅で役に立つと思えるかもしれない参考資料を含んでいます。

    -

    付録A: キーワード

    +

    付録A: キーワード

    @@ -33101,7 +33101,7 @@

    The Rust Programming Language

    そのため、識別子として使用することはできません。識別子の例は、関数名、変数名、引数名、構造体のフィールド名、 モジュール名、クレート名、定数名、マクロ名、静的な値の名前、属性名、型名、トレイト名、ライフタイム名です。

    -

    現在使用されているキーワード

    +

    現在使用されているキーワード

    以下のキーワードは、解説された通りの機能が現状あります。

    @@ -33181,7 +33181,7 @@

    The Rust Programming Language

  • while - 式の結果に基づいて条件的にループする
  • -

    将来的な使用のために予約されているキーワード

    +

    将来的な使用のために予約されているキーワード

    以下のキーワードには機能が何もないものの、将来的に使用される可能性があるので、Rustにより予約されています。

    @@ -33222,14 +33222,14 @@

    The Rust Programming Language

  • yield
  • -

    付録B: 演算子と記号

    +

    付録B: 演算子と記号

    この付録は、演算子や、単独で現れたり、パス、ジェネリクス、トレイト境界、マクロ、属性、コメント、タプル、 かっこの文脈で現れる他の記号を含むRustの記法の用語集を含んでいます。

    -

    演算子

    +

    演算子

    @@ -33376,7 +33376,7 @@

    The Rust Programming Language

    -

    演算子以外のシンボル

    +

    演算子以外のシンボル

    以下のリストは、演算子として機能しない記号全部を含んでいます; つまり、関数やメソッド呼び出しのようには、 @@ -33525,7 +33525,7 @@

    The Rust Programming Language

    /**...*/ 外部ブロックdocコメント -

    タプル

    +

    タプル

    表B-8は、タプルの文脈で出現する記号を示しています。

    @@ -33583,7 +33583,7 @@

    The Rust Programming Language

    expr[..], expr[a..], expr[..b], expr[a..b] RangeRangeFromRangeToRangeFullを「添え字」として使用してコレクション・スライシングの振りをするコレクション添え字アクセス -

    付録C: 導出可能なトレイト

    +

    付録C: 導出可能なトレイト

    @@ -33635,7 +33635,7 @@

    The Rust Programming Language

    deriveと共に使用できるトレイトのリストが実に限りのないものになってしまうのです。deriveの実装には、 プロシージャルなマクロが関連します。マクロについては、付録Dで講義します。

    -

    プログラマ用の出力のDebug

    +

    プログラマ用の出力のDebug

    Debugトレイトにより、フォーマット文字列でのデバッグ整形が可能になり、 @@ -33652,7 +33652,7 @@

    The Rust Programming Language

    このマクロは、プログラマがどうして2つのインスタンスが等価でなかったのか確認できるように、 等価アサートが失敗したら、引数として与えられたインスタンスの値を出力します。

    -

    等価比較のためのPartialEqEq

    +

    等価比較のためのPartialEqEq

    PartialEqトレイトにより、型のインスタンスを比較して、等価性をチェックでき、==!=演算子の使用を可能にします。

    @@ -33682,7 +33682,7 @@

    The Rust Programming Language

    Eqが必要になる一例が、HashMap<K, V>のキーで、HashMap<K, V>が、2つのキーが同じであると判定できます。

    -

    順序付き比較のためのPartialOrdOrd

    +

    順序付き比較のためのPartialOrdOrd

    @@ -33726,7 +33726,7 @@

    The Rust Programming Language

    Ordが必要になる例は、BTreeSet<T>に値を格納する時です。 これは、値のソート順に基づいてデータを格納するデータ構造です。

    -

    値を複製するCloneCopy

    +

    値を複製するCloneCopy

    @@ -33775,7 +33775,7 @@

    The Rust Programming Language

    Copyで可能なこと全てがCloneでも達成可能ですが、コードがより遅い可能性や、 cloneを使用しなければならない箇所があったりします。

    -

    値を固定サイズの値にマップするHash

    +

    値を固定サイズの値にマップするHash

    @@ -33789,7 +33789,7 @@

    The Rust Programming Language

    Hashが必要になる例は、HashMap<K, V>にキーを格納し、データを効率的に格納することです。

    -

    既定値のためのDefault

    +

    既定値のためのDefault

    @@ -33814,7 +33814,7 @@

    The Rust Programming Language

    例えば、Defaultトレイトは、Option<T>インスタンスに対してメソッドunwrap_or_defaultを使用する時に必要になります。 Option<T>Noneならば、メソッドunwrap_or_defaultは、Option<T>に格納された型Tに対してDefault::defaultの結果を返します。

    -

    付録D: マクロ

    +

    付録D: マクロ

    @@ -33842,7 +33842,7 @@

    The Rust Programming Language

    ここで示したコードは、将来のバージョンでも動き続けますが、この本の出版時点では利用可能ではないマクロを書くための追加の能力や、 より簡単な方法があるかもしれません。この付録から何かを実装しようとする場合には、そのことを肝に銘じておいてください。

    -

    マクロと関数の違い

    +

    マクロと関数の違い

    @@ -33903,7 +33903,7 @@

    The Rust Programming Language

    マクロと関数にはもう一つ、重要な違いがあります: ファイル内で呼び出すにマクロはスコープに導入しなければなりませんが、 一方で関数はどこにでも定義でき、どこでも呼び出せます。

    -

    一般的なメタプログラミングのためにmacro_rules!で宣言的なマクロ

    +

    一般的なメタプログラミングのためにmacro_rules!で宣言的なマクロ

    @@ -34042,7 +34042,7 @@

    The Rust Programming Language

    マクロの書き方をもっと学ぶには、オンラインドキュメンテーションか他のリソース、 “The Little Book of Rust Macros(訳注: Rustのマクロの小さな本)などを調べてください。

    -

    独自のderiveのためのプロシージャルマクロ

    +

    独自のderiveのためのプロシージャルマクロ

    @@ -34400,7 +34400,7 @@

    The Rust Programming Language

    プロシージャルマクロのHelloMacroトレイトの実装は、pancakesクレートが実装する必要なく、包含されました; #[derive(HelloMacro)]がトレイトの実装を追加したのです。

    -

    マクロの未来

    +

    マクロの未来

    @@ -34410,18 +34410,18 @@

    The Rust Programming Language

    deriveだけよりもよりパワフルな作業のより多くの種類のプロシージャルマクロを追加するでしょう。 この本の出版時点ではこれらのシステムはまだ開発中です; 最新の情報は、オンラインのRustドキュメンテーションをお調べください。

    -

    付録E: 本の翻訳

    +

    付録E: 本の翻訳

    英語以外の言語のリソースです。ほとんどは翻訳中です; Translationsラベルを確認して、 新しい翻訳の手助けや開始したことをお知らせください!

    -

    付録F: 最新の機能

    +

    付録F: 最新の機能

    この付録は、本の主な部分が完成してから安定版Rustに追加された機能をドキュメント化しています。

    -

    フィールド初期化省略

    +

    フィールド初期化省略

    @@ -34454,7 +34454,7 @@

    The Rust Programming Language

    }
    -

    ループから戻る

    +

    ループから戻る

    @@ -34477,7 +34477,7 @@

    The Rust Programming Language

    }
    -

    use宣言のネストされたグループ

    +

    use宣言のネストされたグループ

    @@ -34510,7 +34510,7 @@

    The Rust Programming Language

    # fn main() {} -

    境界を含む範囲

    +

    境界を含む範囲

    @@ -34531,7 +34531,7 @@

    The Rust Programming Language

    ...記法はそれでも、matchでは受け付けられますが、式では受け付けられません。..=を使用すべきです。

    -

    128ビット整数

    +

    128ビット整数

    Rust1.26.0で128ビットの整数基本型が追加されました:

    @@ -34549,7 +34549,7 @@

    The Rust Programming Language

    これらの基本型は、特定の暗号化アルゴリズムなど、非常に大きな整数を効率的に使用する必要のあるアルゴリズムで、とても有用です。

    -

    付録G: Rustの作られ方と“Nightly Rust”

    +

    付録G: Rustの作られ方と“Nightly Rust”

    @@ -34560,7 +34560,7 @@

    The Rust Programming Language

    それより新しいRustのどんな安定版でもコンパイルでき続けられるはずということに触れました。 この節は、これが本当のことであると保証する方法を説明します!

    -

    停滞なしの安定性

    +

    停滞なしの安定性

    @@ -34577,7 +34577,7 @@

    The Rust Programming Language

    安定版Rustの新しいバージョンにアップグレードするのを恐れる必要は何もないはずです。各アップグレートは痛みのないもののはずですが、 新しい機能、より少ないバグ、高速なコンパイル時間も齎すべきです。

    -

    シュポシュポ!リリースチャンネルと列車に乗ること

    +

    シュポシュポ!リリースチャンネルと列車に乗ること

    @@ -34695,7 +34695,7 @@

    The Rust Programming Language

    ベータリリースが予想した通りに動かなければ、チームに報告して、次の安定版のリリースが起きる前に直してもらうことができるのです! ベータリリースでの破損はどちらかといえば稀ですが、rustcもソフトウェアの一種であり、バグは確実に存在します。

    -

    安定しない機能

    +

    安定しない機能

    @@ -34723,7 +34723,7 @@

    The Rust Programming Language

    確実にこの本が執筆された時と安定版ビルドで有効化された時で異なるからです。ナイトリ限定の機能についてのドキュメンテーションは、 オンラインで発見できます。

    -

    RustupとRustナイトリの役目

    +

    RustupとRustナイトリの役目

    @@ -34760,7 +34760,7 @@

    The Rust Programming Language

    これで ~/projects/needs-nightly内でrustccargoを呼び出す度に、rustupは既定の安定版のRustではなく、 ナイトリRustを使用していることを確かめます。Rustプロジェクトが大量にある時には、重宝します。

    -

    RFCプロセスとチーム

    +

    RFCプロセスとチーム

    diff --git a/docs/second-edition/searcher.js b/docs/second-edition/searcher.js index 63958f9..0acc0be 100644 --- a/docs/second-edition/searcher.js +++ b/docs/second-edition/searcher.js @@ -9,7 +9,14 @@ window.search = window.search || {}; if (!Mark || !elasticlunr) { return; } - + + //IE 11 Compatibility from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith + if (!String.prototype.startsWith) { + String.prototype.startsWith = function(search, pos) { + return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; + }; + } + var search_wrap = document.getElementById('search-wrapper'), searchbar = document.getElementById('searchbar'), searchbar_outer = document.getElementById('searchbar-outer'), @@ -20,11 +27,12 @@ window.search = window.search || {}; content = document.getElementById('content'), searchindex = null, - resultsoptions = { + doc_urls = [], + results_options = { teaser_word_count: 30, limit_results: 30, }, - searchoptions = { + search_options = { bool: "AND", expand: true, fields: { @@ -132,12 +140,17 @@ window.search = window.search || {}; teaser_count++; // The ?URL_MARK_PARAM= parameter belongs inbetween the page and the #heading-anchor - var url = result.ref.split("#"); + var url = doc_urls[result.ref].split("#"); if (url.length == 1) { // no anchor found url.push(""); } - return '' + result.doc.breadcrumbs + '' + '' + teaser + ''; @@ -189,7 +202,7 @@ window.search = window.search || {}; } var window_weight = []; - var window_size = Math.min(weighted.length, resultsoptions.teaser_word_count); + var window_size = Math.min(weighted.length, results_options.teaser_word_count); var cur_sum = 0; for (var wordindex = 0; wordindex < window_size; wordindex++) { @@ -239,11 +252,12 @@ window.search = window.search || {}; return teaser_split.join(''); } - function init() { - resultsoptions = window.search.resultsoptions; - searchoptions = window.search.searchoptions; - searchbar_outer = window.search.searchbar_outer; - searchindex = elasticlunr.Index.load(window.search.index); + function init(config) { + results_options = config.results_options; + search_options = config.search_options; + searchbar_outer = config.searchbar_outer; + doc_urls = config.doc_urls; + searchindex = elasticlunr.Index.load(config.index); // Set up events searchicon.addEventListener('click', function(e) { searchIconClickHandler(); }, false); @@ -434,8 +448,8 @@ window.search = window.search || {}; if (searchindex == null) { return; } // Do the actual search - var results = searchindex.search(searchterm, searchoptions); - var resultcount = Math.min(results.length, resultsoptions.limit_results); + var results = searchindex.search(searchterm, search_options); + var resultcount = Math.min(results.length, results_options.limit_results); // Display search metrics searchresults_header.innerText = formatSearchMetric(resultcount, searchterm); @@ -453,7 +467,16 @@ window.search = window.search || {}; showResults(true); } - init(); + fetch(path_to_root + 'searchindex.json') + .then(response => response.json()) + .then(json => init(json)) + .catch(error => { // Try to load searchindex.js if fetch failed + var script = document.createElement('script'); + script.src = path_to_root + 'searchindex.js'; + script.onload = () => init(window.search); + document.head.appendChild(script); + }); + // Exported functions search.hasFocus = hasFocus; })(window.search); diff --git a/docs/second-edition/searchindex.js b/docs/second-edition/searchindex.js index e71956c..14fb5df 100644 --- a/docs/second-edition/searchindex.js +++ b/docs/second-edition/searchindex.js @@ -1 +1 @@ -window.search = {"index":{"documentStore":{"docInfo":{"appendix-00.html#a付録":{"body":1,"breadcrumbs":0,"title":0},"appendix-01-keywords.html#a付録a-キーワード":{"body":1,"breadcrumbs":0,"title":0},"appendix-01-keywords.html#a将来的な使用のために予約されているキーワード":{"body":17,"breadcrumbs":0,"title":0},"appendix-01-keywords.html#a現在使用されているキーワード":{"body":34,"breadcrumbs":0,"title":0},"appendix-02-operators.html#a付録b-演算子と記号":{"body":1,"breadcrumbs":1,"title":1},"appendix-02-operators.html#a演算子":{"body":147,"breadcrumbs":0,"title":0},"appendix-02-operators.html#a演算子以外のシンボル":{"body":145,"breadcrumbs":0,"title":0},"appendix-03-derivable-traits.html#aプログラマ用の出力の-debug":{"body":5,"breadcrumbs":1,"title":1},"appendix-03-derivable-traits.html#a付録c-導出可能なトレイト":{"body":14,"breadcrumbs":1,"title":1},"appendix-03-derivable-traits.html#a値を固定サイズの値にマップする-hash":{"body":10,"breadcrumbs":1,"title":1},"appendix-03-derivable-traits.html#a値を複製する-clone-と-copy":{"body":33,"breadcrumbs":2,"title":2},"appendix-03-derivable-traits.html#a既定値のための-default":{"body":19,"breadcrumbs":1,"title":1},"appendix-03-derivable-traits.html#a等価比較のための-partialeq-と-eq":{"body":22,"breadcrumbs":2,"title":2},"appendix-03-derivable-traits.html#a順序付き比較のための-partialord-と-ord":{"body":34,"breadcrumbs":2,"title":2},"appendix-04-macros.html#aマクロと関数の違い":{"body":19,"breadcrumbs":0,"title":0},"appendix-04-macros.html#aマクロの未来":{"body":4,"breadcrumbs":0,"title":0},"appendix-04-macros.html#a一般的なメタプログラミングのために-macro_rules-で宣言的なマクロ":{"body":82,"breadcrumbs":1,"title":1},"appendix-04-macros.html#a付録d-マクロ":{"body":5,"breadcrumbs":1,"title":1},"appendix-04-macros.html#a独自の-derive-のためのプロシージャルマクロ":{"body":338,"breadcrumbs":1,"title":1},"appendix-05-translation.html#a付録e-本の翻訳":{"body":1,"breadcrumbs":1,"title":1},"appendix-06-newest-features.html#a128ビット整数":{"body":10,"breadcrumbs":1,"title":1},"appendix-06-newest-features.html#aフィールド初期化省略":{"body":40,"breadcrumbs":0,"title":0},"appendix-06-newest-features.html#aループから戻る":{"body":20,"breadcrumbs":0,"title":0},"appendix-06-newest-features.html#a付録f-最新の機能":{"body":1,"breadcrumbs":1,"title":1},"appendix-06-newest-features.html#a境界を含む範囲":{"body":18,"breadcrumbs":0,"title":0},"appendix-06-newest-features.html#use-宣言のネストされたグループ":{"body":32,"breadcrumbs":1,"title":1},"appendix-07-nightly-rust.html#aシュポシュポリリースチャンネルと列車に乗ること":{"body":59,"breadcrumbs":0,"title":0},"appendix-07-nightly-rust.html#a付録g-rustの作られ方とnightly-rust":{"body":4,"breadcrumbs":3,"title":3},"appendix-07-nightly-rust.html#a停滞なしの安定性":{"body":3,"breadcrumbs":0,"title":0},"appendix-07-nightly-rust.html#a安定しない機能":{"body":4,"breadcrumbs":0,"title":0},"appendix-07-nightly-rust.html#rfcプロセスとチーム":{"body":12,"breadcrumbs":1,"title":1},"appendix-07-nightly-rust.html#rustupとrustナイトリの役目":{"body":45,"breadcrumbs":1,"title":1},"ch00-00-introduction.html#aこの本の使い方":{"body":33,"breadcrumbs":0,"title":0},"ch00-00-introduction.html#aこの本は誰のためのものなの":{"body":0,"breadcrumbs":0,"title":0},"ch00-00-introduction.html#aオープンソース開発者":{"body":2,"breadcrumbs":0,"title":0},"ch00-00-introduction.html#aスピードと安定性に価値を見出す方":{"body":6,"breadcrumbs":0,"title":0},"ch00-00-introduction.html#aソースコード":{"body":1,"breadcrumbs":0,"title":0},"ch00-00-introduction.html#a企業":{"body":4,"breadcrumbs":0,"title":0},"ch00-00-introduction.html#a学生":{"body":3,"breadcrumbs":0,"title":0},"ch00-00-introduction.html#a導入":{"body":14,"breadcrumbs":0,"title":0},"ch00-00-introduction.html#a開発者チーム":{"body":12,"breadcrumbs":0,"title":0},"ch00-00-introduction.html#rustは誰のためのものなの":{"body":1,"breadcrumbs":1,"title":1},"ch01-00-getting-started.html#a事始め":{"body":6,"breadcrumbs":0,"title":0},"ch01-01-installation.html#aインストール":{"body":8,"breadcrumbs":0,"title":0},"ch01-01-installation.html#aコマンドライン表記":{"body":1,"breadcrumbs":0,"title":0},"ch01-01-installation.html#aトラブルシューティング":{"body":23,"breadcrumbs":0,"title":0},"ch01-01-installation.html#aローカルのドキュメンテーション":{"body":3,"breadcrumbs":0,"title":0},"ch01-01-installation.html#a更新及びアンインストール":{"body":9,"breadcrumbs":0,"title":0},"ch01-01-installation.html#linuxとmacosに-rustup-をインストールする":{"body":22,"breadcrumbs":2,"title":2},"ch01-01-installation.html#windowsで-rustup-をインストールする":{"body":11,"breadcrumbs":2,"title":2},"ch01-02-hello-world.html#aコンパイルと実行は個別のステップ":{"body":57,"breadcrumbs":0,"title":0},"ch01-02-hello-world.html#aプロジェクトのディレクトリを作成する":{"body":34,"breadcrumbs":0,"title":0},"ch01-02-hello-world.html#hello-world":{"body":7,"breadcrumbs":2,"title":2},"ch01-02-hello-world.html#rustプログラムの解剖":{"body":32,"breadcrumbs":1,"title":1},"ch01-02-hello-world.html#rustプログラムを書いて走らせる":{"body":39,"breadcrumbs":1,"title":1},"ch01-03-hello-cargo.html#aまとめ":{"body":11,"breadcrumbs":0,"title":0},"ch01-03-hello-cargo.html#aリリースビルドを行う":{"body":11,"breadcrumbs":0,"title":0},"ch01-03-hello-cargo.html#a習慣としてのcargo":{"body":14,"breadcrumbs":1,"title":1},"ch01-03-hello-cargo.html#cargoでプロジェクトを作成する":{"body":81,"breadcrumbs":1,"title":1},"ch01-03-hello-cargo.html#cargoプロジェクトをビルドし実行する":{"body":111,"breadcrumbs":1,"title":1},"ch01-03-hello-cargo.html#hello-cargo":{"body":14,"breadcrumbs":2,"title":2},"ch02-00-guessing-game-tutorial.html#aまとめ":{"body":6,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#aクレートを使用して機能を追加する":{"body":170,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#aループで複数回の予想を可能にする":{"body":101,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a不正な入力を処理する":{"body":153,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a乱数を生成する":{"body":114,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a予想と秘密の数字を比較する":{"body":269,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a予想を処理する":{"body":51,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a値を変数に保持する":{"body":66,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a数当てゲームをプログラムする":{"body":4,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a新規プロジェクトの立ち上げ":{"body":61,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a最初の部分をテストする":{"body":25,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a正しい予想をした後に終了する":{"body":19,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#a秘密の数字を生成する":{"body":4,"breadcrumbs":0,"title":0},"ch02-00-guessing-game-tutorial.html#println-マクロのプレースホルダーで値を出力する":{"body":19,"breadcrumbs":1,"title":1},"ch02-00-guessing-game-tutorial.html#result-型で失敗の可能性を扱う":{"body":65,"breadcrumbs":1,"title":1},"ch03-00-common-programming-concepts.html#aキーワード":{"body":3,"breadcrumbs":0,"title":0},"ch03-00-common-programming-concepts.html#a一般的なプログラミングの概念":{"body":3,"breadcrumbs":0,"title":0},"ch03-01-variables-and-mutability.html#aシャドーイング":{"body":84,"breadcrumbs":0,"title":0},"ch03-01-variables-and-mutability.html#a変数と可変性":{"body":108,"breadcrumbs":0,"title":0},"ch03-01-variables-and-mutability.html#a変数と定数constantsの違い":{"body":9,"breadcrumbs":1,"title":1},"ch03-02-data-types.html#aスカラー型":{"body":148,"breadcrumbs":0,"title":0},"ch03-02-data-types.html#aデータ型":{"body":30,"breadcrumbs":0,"title":0},"ch03-02-data-types.html#a複合型":{"body":153,"breadcrumbs":0,"title":0},"ch03-03-how-functions-work.html#a戻り値のある関数":{"body":116,"breadcrumbs":0,"title":0},"ch03-03-how-functions-work.html#a関数":{"body":48,"breadcrumbs":0,"title":0},"ch03-03-how-functions-work.html#a関数の引数":{"body":92,"breadcrumbs":0,"title":0},"ch03-03-how-functions-work.html#a関数本体は文と式を含む":{"body":83,"breadcrumbs":0,"title":0},"ch03-04-comments.html#aコメント":{"body":41,"breadcrumbs":0,"title":0},"ch03-05-control-flow.html#aまとめ":{"body":5,"breadcrumbs":0,"title":0},"ch03-05-control-flow.html#aフロー制御":{"body":1,"breadcrumbs":0,"title":0},"ch03-05-control-flow.html#aループでの繰り返し":{"body":164,"breadcrumbs":0,"title":0},"ch03-05-control-flow.html#if-式":{"body":253,"breadcrumbs":0,"title":0},"ch04-00-understanding-ownership.html#a所有権を理解する":{"body":2,"breadcrumbs":0,"title":0},"ch04-01-what-is-ownership.html#aスタックとヒープ":{"body":13,"breadcrumbs":0,"title":0},"ch04-01-what-is-ownership.html#aメモリと確保":{"body":231,"breadcrumbs":0,"title":0},"ch04-01-what-is-ownership.html#a変数スコープ":{"body":20,"breadcrumbs":0,"title":0},"ch04-01-what-is-ownership.html#a戻り値とスコープ":{"body":65,"breadcrumbs":0,"title":0},"ch04-01-what-is-ownership.html#a所有権とは":{"body":4,"breadcrumbs":0,"title":0},"ch04-01-what-is-ownership.html#a所有権と関数":{"body":38,"breadcrumbs":0,"title":0},"ch04-01-what-is-ownership.html#a所有権規則":{"body":1,"breadcrumbs":0,"title":0},"ch04-01-what-is-ownership.html#string-型":{"body":25,"breadcrumbs":1,"title":1},"ch04-02-references-and-borrowing.html#a参照と借用":{"body":98,"breadcrumbs":0,"title":0},"ch04-02-references-and-borrowing.html#a参照の規則":{"body":0,"breadcrumbs":0,"title":0},"ch04-02-references-and-borrowing.html#a可変な参照":{"body":125,"breadcrumbs":0,"title":0},"ch04-02-references-and-borrowing.html#a宙に浮いた参照":{"body":73,"breadcrumbs":0,"title":0},"ch04-03-slices.html#aまとめ":{"body":5,"breadcrumbs":0,"title":0},"ch04-03-slices.html#aスライス型":{"body":106,"breadcrumbs":0,"title":0},"ch04-03-slices.html#a他のスライス":{"body":14,"breadcrumbs":0,"title":0},"ch04-03-slices.html#a文字列スライス":{"body":227,"breadcrumbs":0,"title":0},"ch05-00-structs.html#a構造体を使用して関係のあるデータを構造化する":{"body":5,"breadcrumbs":0,"title":0},"ch05-01-defining-structs.html#aフィールドと変数が同名の時にフィールド初期化省略記法を使う":{"body":45,"breadcrumbs":0,"title":0},"ch05-01-defining-structs.html#aフィールドのないユニット様よう構造体":{"body":1,"breadcrumbs":0,"title":0},"ch05-01-defining-structs.html#a構造体を定義しインスタンス化する":{"body":118,"breadcrumbs":0,"title":0},"ch05-01-defining-structs.html#a構造体データの所有権":{"body":52,"breadcrumbs":0,"title":0},"ch05-01-defining-structs.html#a構造体更新記法で他のインスタンスからインスタンスを生成する":{"body":86,"breadcrumbs":0,"title":0},"ch05-01-defining-structs.html#a異なる型を生成する名前付きフィールドのないタプル構造体を使用する":{"body":26,"breadcrumbs":0,"title":0},"ch05-02-example-structs.html#aタプルでリファクタリングする":{"body":25,"breadcrumbs":0,"title":0},"ch05-02-example-structs.html#aトレイトの導出で有用な機能を追加する":{"body":125,"breadcrumbs":0,"title":0},"ch05-02-example-structs.html#a構造体でリファクタリングする-より意味付けする":{"body":55,"breadcrumbs":0,"title":0},"ch05-02-example-structs.html#a構造体を使ったプログラム例":{"body":37,"breadcrumbs":0,"title":0},"ch05-03-method-syntax.html#a--演算子はどこに行ったの":{"body":54,"breadcrumbs":0,"title":0},"ch05-03-method-syntax.html#aまとめ":{"body":1,"breadcrumbs":0,"title":0},"ch05-03-method-syntax.html#aより引数の多いメソッド":{"body":113,"breadcrumbs":0,"title":0},"ch05-03-method-syntax.html#aメソッドを定義する":{"body":74,"breadcrumbs":0,"title":0},"ch05-03-method-syntax.html#aメソッド記法":{"body":3,"breadcrumbs":0,"title":0},"ch05-03-method-syntax.html#a複数の-impl-ブロック":{"body":39,"breadcrumbs":1,"title":1},"ch05-03-method-syntax.html#a関連関数":{"body":28,"breadcrumbs":0,"title":0},"ch06-00-enums.html#enumとパターンマッチング":{"body":9,"breadcrumbs":1,"title":1},"ch06-01-defining-an-enum.html#enumの値":{"body":219,"breadcrumbs":2,"title":1},"ch06-01-defining-an-enum.html#enumを定義する":{"body":18,"breadcrumbs":2,"title":1},"ch06-01-defining-an-enum.html#option--enumとnull値に勝る利点":{"body":124,"breadcrumbs":3,"title":2},"ch06-02-match.html#a_-というプレースホルダー":{"body":23,"breadcrumbs":2,"title":1},"ch06-02-match.html#aマッチは包括的":{"body":29,"breadcrumbs":1,"title":0},"ch06-02-match.html#a値に束縛されるパターン":{"body":73,"breadcrumbs":1,"title":0},"ch06-02-match.html#match-フロー制御演算子":{"body":79,"breadcrumbs":2,"title":1},"ch06-02-match.html#optiont-とのマッチ":{"body":65,"breadcrumbs":2,"title":1},"ch06-03-if-let.html#aまとめ":{"body":7,"breadcrumbs":1,"title":0},"ch06-03-if-let.html#if-let-で簡潔なフロー制御":{"body":91,"breadcrumbs":1,"title":0},"ch07-00-modules.html#aモジュールを使用してコードを体系化し再利用する":{"body":8,"breadcrumbs":0,"title":0},"ch07-01-mod-and-the-filesystem.html#aモジュールを別ファイルに移す":{"body":305,"breadcrumbs":0,"title":0},"ch07-01-mod-and-the-filesystem.html#aモジュールファイルシステムの規則":{"body":18,"breadcrumbs":0,"title":0},"ch07-01-mod-and-the-filesystem.html#aモジュール定義":{"body":87,"breadcrumbs":0,"title":0},"ch07-01-mod-and-the-filesystem.html#mod-とファイルシステム":{"body":40,"breadcrumbs":1,"title":1},"ch07-02-controlling-visibility-with-pub.html#aプライバシー例":{"body":56,"breadcrumbs":0,"title":0},"ch07-02-controlling-visibility-with-pub.html#aプライバシー規則":{"body":0,"breadcrumbs":0,"title":0},"ch07-02-controlling-visibility-with-pub.html#a関数を公開にする":{"body":119,"breadcrumbs":0,"title":0},"ch07-02-controlling-visibility-with-pub.html#pub-で公開するか制御する":{"body":85,"breadcrumbs":1,"title":1},"ch07-03-importing-names-with-use.html#aまとめ":{"body":1,"breadcrumbs":0,"title":0},"ch07-03-importing-names-with-use.html#a異なるモジュールの名前を参照する":{"body":20,"breadcrumbs":0,"title":0},"ch07-03-importing-names-with-use.html#globで全ての名前をスコープに導入する":{"body":20,"breadcrumbs":1,"title":1},"ch07-03-importing-names-with-use.html#super-を使用して親モジュールにアクセスする":{"body":126,"breadcrumbs":1,"title":1},"ch07-03-importing-names-with-use.html#use-キーワードで名前をスコープに導入する":{"body":65,"breadcrumbs":1,"title":1},"ch08-00-common-collections.html#a一般的なコレクション":{"body":3,"breadcrumbs":0,"title":0},"ch08-01-vectors.html#aベクタで一連の値を保持する":{"body":2,"breadcrumbs":0,"title":0},"ch08-01-vectors.html#aベクタの値を走査する":{"body":25,"breadcrumbs":0,"title":0},"ch08-01-vectors.html#aベクタの要素を読む":{"body":101,"breadcrumbs":0,"title":0},"ch08-01-vectors.html#aベクタをドロップすれば要素もドロップする":{"body":11,"breadcrumbs":0,"title":0},"ch08-01-vectors.html#aベクタを更新する":{"body":17,"breadcrumbs":0,"title":0},"ch08-01-vectors.html#a新しいベクタを生成する":{"body":34,"breadcrumbs":0,"title":0},"ch08-01-vectors.html#enumを使って複数の型を保持する":{"body":30,"breadcrumbs":1,"title":1},"ch08-02-strings.html#a文字列でutf-8でエンコードされたテキストを保持する":{"body":9,"breadcrumbs":2,"title":2},"ch08-02-strings.html#a文字列とは":{"body":26,"breadcrumbs":0,"title":0},"ch08-02-strings.html#a文字列に添え字アクセスする":{"body":104,"breadcrumbs":0,"title":0},"ch08-02-strings.html#a文字列はそう単純じゃない":{"body":6,"breadcrumbs":0,"title":0},"ch08-02-strings.html#a文字列をスライスする":{"body":25,"breadcrumbs":0,"title":0},"ch08-02-strings.html#a文字列を更新する":{"body":167,"breadcrumbs":0,"title":0},"ch08-02-strings.html#a文字列を走査するメソッド群":{"body":23,"breadcrumbs":0,"title":0},"ch08-02-strings.html#a新規文字列を生成する":{"body":81,"breadcrumbs":0,"title":0},"ch08-03-hash-maps.html#aまとめ":{"body":14,"breadcrumbs":0,"title":0},"ch08-03-hash-maps.html#aハッシュマップと所有権":{"body":33,"breadcrumbs":0,"title":0},"ch08-03-hash-maps.html#aハッシュマップに値に紐づいたキーを格納する":{"body":8,"breadcrumbs":0,"title":0},"ch08-03-hash-maps.html#aハッシュマップの値にアクセスする":{"body":42,"breadcrumbs":0,"title":0},"ch08-03-hash-maps.html#aハッシュマップを更新する":{"body":93,"breadcrumbs":0,"title":0},"ch08-03-hash-maps.html#aハッシュ関数":{"body":9,"breadcrumbs":0,"title":0},"ch08-03-hash-maps.html#a新規ハッシュマップを生成する":{"body":46,"breadcrumbs":0,"title":0},"ch09-00-error-handling.html#aエラー処理":{"body":11,"breadcrumbs":0,"title":0},"ch09-01-unrecoverable-errors-with-panic.html#aパニックに対してスタックを巻き戻すか異常終了するか":{"body":51,"breadcrumbs":0,"title":0},"ch09-01-unrecoverable-errors-with-panic.html#panic-で回復不能なエラー":{"body":3,"breadcrumbs":1,"title":1},"ch09-01-unrecoverable-errors-with-panic.html#panic-バックトレースを使用する":{"body":164,"breadcrumbs":1,"title":1},"ch09-02-recoverable-errors-with-result.html#aエラーを委譲する":{"body":225,"breadcrumbs":0,"title":0},"ch09-02-recoverable-errors-with-result.html#aエラー時にパニックするショートカット--unwrap-と-expect":{"body":95,"breadcrumbs":2,"title":2},"ch09-02-recoverable-errors-with-result.html#a色々なエラーにマッチする":{"body":75,"breadcrumbs":0,"title":0},"ch09-02-recoverable-errors-with-result.html#result-で回復可能なエラー":{"body":156,"breadcrumbs":1,"title":1},"ch09-03-to-panic-or-not-to-panic.html#aまとめ":{"body":10,"breadcrumbs":0,"title":0},"ch09-03-to-panic-or-not-to-panic.html#aエラー処理のガイドライン":{"body":14,"breadcrumbs":0,"title":0},"ch09-03-to-panic-or-not-to-panic.html#aコンパイラよりもプログラマがより情報を持っている場合":{"body":22,"breadcrumbs":0,"title":0},"ch09-03-to-panic-or-not-to-panic.html#a例プロトタイプコードテスト":{"body":6,"breadcrumbs":0,"title":0},"ch09-03-to-panic-or-not-to-panic.html#a検証のために独自の型を作る":{"body":117,"breadcrumbs":0,"title":0},"ch09-03-to-panic-or-not-to-panic.html#panic-すべきかするまいか":{"body":8,"breadcrumbs":1,"title":1},"ch10-00-generics.html#aジェネリック型トレイトライフタイム":{"body":14,"breadcrumbs":0,"title":0},"ch10-00-generics.html#a関数を抽出することで重複を取り除く":{"body":154,"breadcrumbs":0,"title":0},"ch10-01-syntax.html#aジェネリクスを使用したコードのパフォーマンス":{"body":36,"breadcrumbs":0,"title":0},"ch10-01-syntax.html#aジェネリックなデータ型":{"body":1,"breadcrumbs":0,"title":0},"ch10-01-syntax.html#aメソッド定義では":{"body":180,"breadcrumbs":0,"title":0},"ch10-01-syntax.html#a構造体定義では":{"body":142,"breadcrumbs":0,"title":0},"ch10-01-syntax.html#a関数定義では":{"body":156,"breadcrumbs":0,"title":0},"ch10-01-syntax.html#enum定義では":{"body":43,"breadcrumbs":1,"title":1},"ch10-02-traits.html#aデフォルト実装":{"body":129,"breadcrumbs":0,"title":0},"ch10-02-traits.html#aトレイト-共通の振る舞いを定義する":{"body":1,"breadcrumbs":0,"title":0},"ch10-02-traits.html#aトレイトを型に実装する":{"body":140,"breadcrumbs":0,"title":0},"ch10-02-traits.html#aトレイトを定義する":{"body":27,"breadcrumbs":0,"title":0},"ch10-02-traits.html#aトレイト境界":{"body":64,"breadcrumbs":0,"title":0},"ch10-02-traits.html#aトレイト境界で-largest-関数を修正する":{"body":171,"breadcrumbs":1,"title":1},"ch10-02-traits.html#aトレイト境界を使用してメソッド実装を条件分けする":{"body":68,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#aまとめ":{"body":3,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#aジェネリックな型引数トレイト境界ライフタイムを一度に":{"body":28,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#aメソッド定義におけるライフタイム注釈":{"body":44,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#aライフタイムでダングリング参照を回避する":{"body":59,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#aライフタイムで参照を検証する":{"body":3,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#aライフタイムの観点で思考する":{"body":74,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#aライフタイム注釈記法":{"body":23,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#aライフタイム省略":{"body":96,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#a借用精査機":{"body":42,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#a構造体定義のライフタイム注釈":{"body":38,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#a関数のジェネリックなライフタイム":{"body":89,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#a関数シグニチャにおけるライフタイム注釈":{"body":147,"breadcrumbs":0,"title":0},"ch10-03-lifetime-syntax.html#a静的ライフタイム":{"body":12,"breadcrumbs":0,"title":0},"ch11-00-testing.html#a自動テストを書く":{"body":13,"breadcrumbs":0,"title":0},"ch11-01-writing-tests.html#assert-マクロで結果を確認する":{"body":243,"breadcrumbs":1,"title":1},"ch11-01-writing-tests.html#assert_eq-と-assert_ne-マクロで等値性をテストする":{"body":160,"breadcrumbs":2,"title":2},"ch11-01-writing-tests.html#aカスタムの失敗メッセージを追加する":{"body":95,"breadcrumbs":0,"title":0},"ch11-01-writing-tests.html#aテストの記述法":{"body":5,"breadcrumbs":0,"title":0},"ch11-01-writing-tests.html#aテスト関数の解剖":{"body":270,"breadcrumbs":0,"title":0},"ch11-01-writing-tests.html#should_panic-でパニックを確認する":{"body":293,"breadcrumbs":1,"title":1},"ch11-02-running-tests.html#aテストの実行され方を制御する":{"body":21,"breadcrumbs":0,"title":0},"ch11-02-running-tests.html#aテストを並行または連続して実行する":{"body":10,"breadcrumbs":0,"title":0},"ch11-02-running-tests.html#a名前でテストの一部を実行する":{"body":147,"breadcrumbs":0,"title":0},"ch11-02-running-tests.html#a特に要望のない限りテストを無視する":{"body":104,"breadcrumbs":0,"title":0},"ch11-02-running-tests.html#a関数の出力を表示する":{"body":143,"breadcrumbs":0,"title":0},"ch11-03-test-organization.html#aまとめ":{"body":3,"breadcrumbs":0,"title":0},"ch11-03-test-organization.html#aテストの体系化":{"body":3,"breadcrumbs":0,"title":0},"ch11-03-test-organization.html#a単体テスト":{"body":70,"breadcrumbs":0,"title":0},"ch11-03-test-organization.html#a結合テスト":{"body":326,"breadcrumbs":0,"title":0},"ch12-00-an-io-project.html#a入出力プロジェクト-コマンドラインプログラムを構築する":{"body":29,"breadcrumbs":0,"title":0},"ch12-01-accepting-command-line-arguments.html#args-関数と不正なユニコード":{"body":28,"breadcrumbs":1,"title":1},"ch12-01-accepting-command-line-arguments.html#aコマンドライン引数を受け付ける":{"body":15,"breadcrumbs":0,"title":0},"ch12-01-accepting-command-line-arguments.html#a引数の値を変数に保存する":{"body":53,"breadcrumbs":0,"title":0},"ch12-01-accepting-command-line-arguments.html#a引数の値を読み取る":{"body":33,"breadcrumbs":0,"title":0},"ch12-02-reading-a-file.html#aファイルを読み込む":{"body":137,"breadcrumbs":0,"title":0},"ch12-03-improving-error-handling-and-modularity.html#aエラー処理を修正する":{"body":238,"breadcrumbs":0,"title":0},"ch12-03-improving-error-handling-and-modularity.html#aコードをライブラリクレートに分割する":{"body":94,"breadcrumbs":0,"title":0},"ch12-03-improving-error-handling-and-modularity.html#aバイナリプロジェクトの責任の分離":{"body":135,"breadcrumbs":0,"title":0},"ch12-03-improving-error-handling-and-modularity.html#aリファクタリングしてモジュール性とエラー処理を向上させる":{"body":24,"breadcrumbs":0,"title":0},"ch12-03-improving-error-handling-and-modularity.html#clone-を使用する代償":{"body":82,"breadcrumbs":1,"title":1},"ch12-03-improving-error-handling-and-modularity.html#main-からロジックを抽出する":{"body":148,"breadcrumbs":1,"title":1},"ch12-04-testing-the-librarys-functionality.html#aテストを通過させるコードを書く":{"body":189,"breadcrumbs":0,"title":0},"ch12-04-testing-the-librarys-functionality.html#aテスト駆動開発でライブラリの機能を開発する":{"body":9,"breadcrumbs":0,"title":0},"ch12-04-testing-the-librarys-functionality.html#a失敗するテストを記述する":{"body":177,"breadcrumbs":0,"title":0},"ch12-05-working-with-environment-variables.html#a大文字小文字を区別しない-search-関数用に失敗するテストを書く":{"body":67,"breadcrumbs":1,"title":1},"ch12-05-working-with-environment-variables.html#a環境変数を取り扱う":{"body":2,"breadcrumbs":0,"title":0},"ch12-05-working-with-environment-variables.html#search_case_insensitive-関数を実装する":{"body":285,"breadcrumbs":1,"title":1},"ch12-06-writing-to-stderr-instead-of-stdout.html#aまとめ":{"body":3,"breadcrumbs":0,"title":0},"ch12-06-writing-to-stderr-instead-of-stdout.html#aエラーが書き込まれる場所を確認する":{"body":12,"breadcrumbs":0,"title":0},"ch12-06-writing-to-stderr-instead-of-stdout.html#aエラーを標準エラーに出力する":{"body":50,"breadcrumbs":0,"title":0},"ch12-06-writing-to-stderr-instead-of-stdout.html#a標準出力ではなく標準エラーにエラーメッセージを書き込む":{"body":5,"breadcrumbs":0,"title":0},"ch13-00-functional-features.html#a関数型言語の機能-イテレータとクロージャ":{"body":6,"breadcrumbs":0,"title":0},"ch13-01-closures.html#aクロージャ-環境をキャプチャできる匿名関数":{"body":1,"breadcrumbs":0,"title":0},"ch13-01-closures.html#aクロージャで動作の抽象化を行う":{"body":252,"breadcrumbs":0,"title":0},"ch13-01-closures.html#aクロージャで環境をキャプチャする":{"body":154,"breadcrumbs":0,"title":0},"ch13-01-closures.html#aクロージャの型推論と注釈":{"body":86,"breadcrumbs":0,"title":0},"ch13-01-closures.html#aジェネリック引数と-fn-トレイトを使用してクロージャを保存する":{"body":218,"breadcrumbs":1,"title":1},"ch13-01-closures.html#cacher-実装の限界":{"body":67,"breadcrumbs":1,"title":1},"ch13-02-iterators.html#aイテレータを消費するメソッド":{"body":35,"breadcrumbs":0,"title":0},"ch13-02-iterators.html#a一連の要素をイテレータで処理する":{"body":34,"breadcrumbs":0,"title":0},"ch13-02-iterators.html#a他のイテレータを生成するメソッド":{"body":69,"breadcrumbs":0,"title":0},"ch13-02-iterators.html#a環境をキャプチャするクロージャを使用する":{"body":78,"breadcrumbs":0,"title":0},"ch13-02-iterators.html#iterator-トレイトで独自のイテレータを作成する":{"body":223,"breadcrumbs":1,"title":1},"ch13-02-iterators.html#iterator-トレイトと-next-メソッド":{"body":67,"breadcrumbs":2,"title":2},"ch13-03-improving-our-io-project.html#aイテレータを使用して-clone-を取り除く":{"body":203,"breadcrumbs":1,"title":1},"ch13-03-improving-our-io-project.html#aイテレータアダプタでコードをより明確にする":{"body":61,"breadcrumbs":0,"title":0},"ch13-03-improving-our-io-project.html#a入出力プロジェクトを改善する":{"body":3,"breadcrumbs":0,"title":0},"ch13-04-performance.html#aまとめ":{"body":4,"breadcrumbs":0,"title":0},"ch13-04-performance.html#aパフォーマンス比較-ループvsイテレータ":{"body":74,"breadcrumbs":1,"title":1},"ch14-00-more-about-cargo.html#cargoとcratesioについてより詳しく":{"body":5,"breadcrumbs":1,"title":1},"ch14-01-release-profiles.html#aリリースプロファイルでビルドをカスタマイズする":{"body":80,"breadcrumbs":1,"title":0},"ch14-02-publishing-to-crates-io.html#a役に立つドキュメンテーションコメントを行う":{"body":137,"breadcrumbs":1,"title":0},"ch14-02-publishing-to-crates-io.html#a新しいクレートにメタデータを追加する":{"body":89,"breadcrumbs":1,"title":0},"ch14-02-publishing-to-crates-io.html#a既存のクレートの新バージョンを公開する":{"body":4,"breadcrumbs":1,"title":0},"ch14-02-publishing-to-crates-io.html#cargo-yank-でcratesioからバージョンを削除する":{"body":16,"breadcrumbs":4,"title":3},"ch14-02-publishing-to-crates-io.html#cratesioにクレートを公開する":{"body":3,"breadcrumbs":2,"title":1},"ch14-02-publishing-to-crates-io.html#cratesioに公開する":{"body":38,"breadcrumbs":2,"title":1},"ch14-02-publishing-to-crates-io.html#cratesioのアカウントをセットアップする":{"body":15,"breadcrumbs":2,"title":1},"ch14-02-publishing-to-crates-io.html#pub-use-で便利な公開apiをエクスポートする":{"body":210,"breadcrumbs":4,"title":3},"ch14-03-cargo-workspaces.html#aワークスペースを生成する":{"body":57,"breadcrumbs":1,"title":0},"ch14-03-cargo-workspaces.html#aワークスペース内に2番目のクレートを作成する":{"body":439,"breadcrumbs":2,"title":1},"ch14-03-cargo-workspaces.html#cargoのワークスペース":{"body":2,"breadcrumbs":2,"title":1},"ch14-04-installing-binaries.html#cargo-install-でcratesioからバイナリをインストールする":{"body":50,"breadcrumbs":4,"title":3},"ch14-05-extending-cargo.html#aまとめ":{"body":4,"breadcrumbs":1,"title":0},"ch14-05-extending-cargo.html#a独自のコマンドでcargoを拡張する":{"body":12,"breadcrumbs":2,"title":1},"ch15-00-smart-pointers.html#aスマートポインタ":{"body":22,"breadcrumbs":0,"title":0},"ch15-01-box.html#aヒープのデータを指す-boxt-を使用する":{"body":8,"breadcrumbs":1,"title":1},"ch15-01-box.html#aボックスで再帰的な型を可能にする":{"body":232,"breadcrumbs":0,"title":0},"ch15-01-box.html#boxt-を使ってヒープにデータを格納する":{"body":24,"breadcrumbs":1,"title":1},"ch15-02-deref.html#a参照外し型強制が可変性と相互作用する方法":{"body":29,"breadcrumbs":0,"title":0},"ch15-02-deref.html#a参照外し演算子で値までポインタを追いかける":{"body":54,"breadcrumbs":0,"title":0},"ch15-02-deref.html#a独自のスマートポインタを定義する":{"body":66,"breadcrumbs":0,"title":0},"ch15-02-deref.html#a関数やメソッドで暗黙的な参照外し型強制":{"body":131,"breadcrumbs":0,"title":0},"ch15-02-deref.html#boxt-を参照のように使う":{"body":27,"breadcrumbs":1,"title":1},"ch15-02-deref.html#deref-トレイトでスマートポインタを普通の参照のように扱う":{"body":5,"breadcrumbs":1,"title":1},"ch15-02-deref.html#deref-トレイトを実装して型を参照のように扱う":{"body":63,"breadcrumbs":1,"title":1},"ch15-03-drop.html#drop-トレイトで片付け時にコードを走らせる":{"body":88,"breadcrumbs":1,"title":1},"ch15-03-drop.html#stdmemdrop-で早期に値をドロップする":{"body":130,"breadcrumbs":1,"title":1},"ch15-04-rc.html#rct-でデータを共有する":{"body":150,"breadcrumbs":1,"title":1},"ch15-04-rc.html#rct-は参照カウント方式のスマートポインタ":{"body":11,"breadcrumbs":1,"title":1},"ch15-04-rc.html#rct-をクローンすると参照カウントが増える":{"body":94,"breadcrumbs":1,"title":1},"ch15-05-interior-mutability.html#a内部可変性-不変値への可変借用":{"body":384,"breadcrumbs":0,"title":0},"ch15-05-interior-mutability.html#rct-と-refcellt-を組み合わせることで可変なデータに複数の所有者を持たせる":{"body":107,"breadcrumbs":2,"title":2},"ch15-05-interior-mutability.html#refcellt-で実行時に借用規則を強制する":{"body":29,"breadcrumbs":1,"title":1},"ch15-05-interior-mutability.html#refcellt-と内部可変性パターン":{"body":7,"breadcrumbs":1,"title":1},"ch15-06-reference-cycles.html#aまとめ":{"body":10,"breadcrumbs":0,"title":0},"ch15-06-reference-cycles.html#a循環参照させる":{"body":236,"breadcrumbs":0,"title":0},"ch15-06-reference-cycles.html#a循環参照はメモリをリークすることもある":{"body":6,"breadcrumbs":0,"title":0},"ch15-06-reference-cycles.html#a循環参照を回避する--rct-を-weakt-に変換する":{"body":381,"breadcrumbs":2,"title":2},"ch16-00-concurrency.html#a恐れるな並行性":{"body":9,"breadcrumbs":0,"title":0},"ch16-01-threads.html#aスレッドで-move-クロージャを使用する":{"body":196,"breadcrumbs":1,"title":1},"ch16-01-threads.html#aスレッドを使用してコードを同時に走らせる":{"body":21,"breadcrumbs":0,"title":0},"ch16-01-threads.html#join-ハンドルで全スレッドの終了を待つ":{"body":199,"breadcrumbs":1,"title":1},"ch16-01-threads.html#spawn-で新規スレッドを生成する":{"body":76,"breadcrumbs":1,"title":1},"ch16-02-message-passing.html#aチャンネルと所有権の転送":{"body":60,"breadcrumbs":0,"title":0},"ch16-02-message-passing.html#aメッセージ受け渡しを使ってスレッド間でデータを転送する":{"body":109,"breadcrumbs":0,"title":0},"ch16-02-message-passing.html#a複数の値を送信し受信側が待機するのを確かめる":{"body":46,"breadcrumbs":0,"title":0},"ch16-02-message-passing.html#a転送機をクローンして複数の生成器を作成する":{"body":63,"breadcrumbs":0,"title":0},"ch16-03-shared-state.html#aミューテックスを使用して一度に1つのスレッドからデータにアクセスすることを許可する":{"body":445,"breadcrumbs":1,"title":1},"ch16-03-shared-state.html#a状態共有並行性":{"body":3,"breadcrumbs":0,"title":0},"ch16-03-shared-state.html#refcellt--rct-と-mutext--arct-の類似性":{"body":23,"breadcrumbs":4,"title":4},"ch16-04-extensible-concurrency-sync-and-send.html#aまとめ":{"body":7,"breadcrumbs":0,"title":0},"ch16-04-extensible-concurrency-sync-and-send.html#send-でスレッド間の所有権の転送を許可する":{"body":22,"breadcrumbs":1,"title":1},"ch16-04-extensible-concurrency-sync-and-send.html#send-と-sync-を手動で実装するのは非安全である":{"body":10,"breadcrumbs":2,"title":2},"ch16-04-extensible-concurrency-sync-and-send.html#sync-で複数のスレッドからのアクセスを許可する":{"body":22,"breadcrumbs":1,"title":1},"ch16-04-extensible-concurrency-sync-and-send.html#sync-と-send-トレイトで拡張可能な並行性":{"body":5,"breadcrumbs":2,"title":2},"ch17-00-oop.html#rustのオブジェクト指向プログラミング機能":{"body":8,"breadcrumbs":1,"title":1},"ch17-01-what-is-oo.html#aオブジェクトはデータと振る舞いを含む":{"body":18,"breadcrumbs":1,"title":0},"ch17-01-what-is-oo.html#aオブジェクト指向言語の特徴":{"body":4,"breadcrumbs":1,"title":0},"ch17-01-what-is-oo.html#aカプセル化は実装詳細を隠蔽する":{"body":112,"breadcrumbs":1,"title":0},"ch17-01-what-is-oo.html#a型システムおよびコード共有としての継承":{"body":13,"breadcrumbs":1,"title":0},"ch17-01-what-is-oo.html#a多相性":{"body":7,"breadcrumbs":1,"title":0},"ch17-02-trait-objects.html#aトレイトを実装する":{"body":198,"breadcrumbs":1,"title":0},"ch17-02-trait-objects.html#aトレイトオブジェクトで異なる型の値を許容する":{"body":31,"breadcrumbs":1,"title":0},"ch17-02-trait-objects.html#aトレイトオブジェクトにはオブジェクト安全性が必要":{"body":60,"breadcrumbs":1,"title":0},"ch17-02-trait-objects.html#aトレイトオブジェクトはダイナミックディスパッチを行う":{"body":5,"breadcrumbs":1,"title":0},"ch17-02-trait-objects.html#a一般的な振る舞いにトレイトを定義する":{"body":120,"breadcrumbs":1,"title":0},"ch17-03-oo-design-patterns.html#aまとめ":{"body":5,"breadcrumbs":1,"title":0},"ch17-03-oo-design-patterns.html#aオブジェクト指向デザインパターンを実装する":{"body":50,"breadcrumbs":1,"title":0},"ch17-03-oo-design-patterns.html#aステートパターンの代償":{"body":245,"breadcrumbs":1,"title":0},"ch17-03-oo-design-patterns.html#a草稿の記事の内容は空であることを保証する":{"body":27,"breadcrumbs":1,"title":0},"ch17-03-oo-design-patterns.html#a記事の内容のテキストを格納する":{"body":43,"breadcrumbs":1,"title":0},"ch17-03-oo-design-patterns.html#a記事の査読を要求すると状態が変化する":{"body":110,"breadcrumbs":1,"title":0},"ch17-03-oo-design-patterns.html#content-の振る舞いを変化させる-approve-メソッドを追加する":{"body":213,"breadcrumbs":3,"title":2},"ch17-03-oo-design-patterns.html#post-を定義し草稿状態で新しいインスタンスを生成する":{"body":62,"breadcrumbs":2,"title":1},"ch18-00-patterns.html#aパターンとマッチング":{"body":5,"breadcrumbs":0,"title":0},"ch18-01-all-the-places-for-patterns.html#aパターンが使用されることのある箇所全部":{"body":1,"breadcrumbs":0,"title":0},"ch18-01-all-the-places-for-patterns.html#a条件分岐-if-let-式":{"body":69,"breadcrumbs":0,"title":0},"ch18-01-all-the-places-for-patterns.html#a関数の引数":{"body":49,"breadcrumbs":0,"title":0},"ch18-01-all-the-places-for-patterns.html#for-ループ":{"body":37,"breadcrumbs":0,"title":0},"ch18-01-all-the-places-for-patterns.html#let-文":{"body":80,"breadcrumbs":0,"title":0},"ch18-01-all-the-places-for-patterns.html#match-アーム":{"body":20,"breadcrumbs":1,"title":1},"ch18-01-all-the-places-for-patterns.html#while-let-条件分岐ループ":{"body":25,"breadcrumbs":0,"title":0},"ch18-02-refutability.html#a論駁可能性-パターンが合致しないかどうか":{"body":69,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#a-で値の範囲に合致させる":{"body":46,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#a-束縛":{"body":57,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#aまとめ":{"body":3,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#aパターンの値を無視する":{"body":263,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#aパターン記法":{"body":0,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#aマッチガードで追加の条件式":{"body":144,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#aリテラルにマッチする":{"body":19,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#a分配して値を分解する":{"body":348,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#a名前付き変数にマッチする":{"body":86,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#a複数のパターン":{"body":19,"breadcrumbs":0,"title":0},"ch18-03-pattern-syntax.html#ref-と-ref-mut-でパターンに参照を生成する":{"body":84,"breadcrumbs":3,"title":3},"ch19-00-advanced-features.html#a高度な機能":{"body":8,"breadcrumbs":0,"title":0},"ch19-01-unsafe-rust.html#aいつunsafeコードを使用するべきか":{"body":5,"breadcrumbs":1,"title":1},"ch19-01-unsafe-rust.html#a可変で静的な変数にアクセスしたり変更する":{"body":57,"breadcrumbs":0,"title":0},"ch19-01-unsafe-rust.html#a生ポインタを参照外しする":{"body":76,"breadcrumbs":0,"title":0},"ch19-01-unsafe-rust.html#unsafe-rust":{"body":15,"breadcrumbs":2,"title":2},"ch19-01-unsafe-rust.html#unsafeなトレイトを実装する":{"body":41,"breadcrumbs":1,"title":1},"ch19-01-unsafe-rust.html#unsafeな関数やメソッドを呼ぶ":{"body":301,"breadcrumbs":1,"title":1},"ch19-01-unsafe-rust.html#unsafeの強大な力superpower":{"body":30,"breadcrumbs":1,"title":1},"ch19-02-advanced-lifetimes.html#aジェネリックな型への参照に対するライフタイム境界":{"body":104,"breadcrumbs":0,"title":0},"ch19-02-advanced-lifetimes.html#aトレイトオブジェクトライフタイムの推論":{"body":42,"breadcrumbs":0,"title":0},"ch19-02-advanced-lifetimes.html#aライフタイムサブタイピングによりあるライフタイムが他よりも長生きすることを保証する":{"body":375,"breadcrumbs":0,"title":0},"ch19-02-advanced-lifetimes.html#a高度なライフタイム":{"body":2,"breadcrumbs":0,"title":0},"ch19-03-advanced-traits.html#aスーパートレイトを使用して別のトレイト内であるトレイトの機能を必要とする":{"body":132,"breadcrumbs":0,"title":0},"ch19-03-advanced-traits.html#aデフォルトのジェネリック型引数と演算子オーバーロード":{"body":146,"breadcrumbs":0,"title":0},"ch19-03-advanced-traits.html#aニュータイプパターンを使用して外部の型に外部のトレイトを実装する":{"body":65,"breadcrumbs":0,"title":0},"ch19-03-advanced-traits.html#a明確化のためのフルパス記法-同じ名前のメソッドを呼ぶ":{"body":320,"breadcrumbs":0,"title":0},"ch19-03-advanced-traits.html#a関連型でトレイト定義においてプレースホルダーの型を指定する":{"body":79,"breadcrumbs":0,"title":0},"ch19-03-advanced-traits.html#a高度なトレイト":{"body":2,"breadcrumbs":0,"title":0},"ch19-04-advanced-types.html#a動的サイズ付け型と-sized-トレイト":{"body":70,"breadcrumbs":1,"title":1},"ch19-04-advanced-types.html#a型エイリアスで型同義語を生成する":{"body":179,"breadcrumbs":0,"title":0},"ch19-04-advanced-types.html#a型安全性と抽象化を求めてニュータイプパターンを使用する":{"body":19,"breadcrumbs":0,"title":0},"ch19-04-advanced-types.html#a高度な型":{"body":1,"breadcrumbs":0,"title":0},"ch19-04-advanced-types.html#never型は絶対に返らない":{"body":100,"breadcrumbs":1,"title":1},"ch19-05-advanced-functions-and-closures.html#aまとめ":{"body":2,"breadcrumbs":0,"title":0},"ch19-05-advanced-functions-and-closures.html#aクロージャを返却する":{"body":52,"breadcrumbs":0,"title":0},"ch19-05-advanced-functions-and-closures.html#a関数ポインタ":{"body":85,"breadcrumbs":0,"title":0},"ch19-05-advanced-functions-and-closures.html#a高度な関数とクロージャ":{"body":0,"breadcrumbs":0,"title":0},"ch20-00-final-project-a-web-server.html#a最後のプロジェクト-マルチスレッドのwebサーバを構築する":{"body":17,"breadcrumbs":1,"title":1},"ch20-01-single-threaded.html#aシングルスレッドのwebサーバを構築する":{"body":14,"breadcrumbs":2,"title":1},"ch20-01-single-threaded.html#aリクエストにバリデーションをかけ選択的にレスポンスを返す":{"body":129,"breadcrumbs":1,"title":0},"ch20-01-single-threaded.html#aリクエストを読み取る":{"body":109,"breadcrumbs":1,"title":0},"ch20-01-single-threaded.html#aリファクタリングの触り":{"body":65,"breadcrumbs":1,"title":0},"ch20-01-single-threaded.html#aレスポンスを記述する":{"body":60,"breadcrumbs":1,"title":0},"ch20-01-single-threaded.html#a本物のhtmlを返す":{"body":80,"breadcrumbs":2,"title":1},"ch20-01-single-threaded.html#httpリクエストを詳しく見る":{"body":43,"breadcrumbs":2,"title":1},"ch20-01-single-threaded.html#tcp接続をリッスンする":{"body":83,"breadcrumbs":2,"title":1},"ch20-02-multithreaded.html#aシングルスレッドサーバをマルチスレッド化する":{"body":1,"breadcrumbs":1,"title":0},"ch20-02-multithreaded.html#aスレッドプールでスループットを向上させる":{"body":1220,"breadcrumbs":1,"title":0},"ch20-02-multithreaded.html#a現在のサーバの実装で遅いリクエストをシミュレーションする":{"body":70,"breadcrumbs":1,"title":0},"ch20-03-graceful-shutdown-and-cleanup.html#aまとめ":{"body":3,"breadcrumbs":1,"title":0},"ch20-03-graceful-shutdown-and-cleanup.html#aスレッドに仕事をリッスンするのを止めるよう通知する":{"body":493,"breadcrumbs":1,"title":0},"ch20-03-graceful-shutdown-and-cleanup.html#a正常なシャットダウンと片付け":{"body":10,"breadcrumbs":1,"title":0},"ch20-03-graceful-shutdown-and-cleanup.html#threadpool-に-drop-トレイトを実装する":{"body":154,"breadcrumbs":3,"title":2},"foreword.html#aまえがき":{"body":17,"breadcrumbs":0,"title":0}},"docs":{"appendix-00.html#a付録":{"body":"以下の節は、Rustの旅で役に立つと思えるかもしれない参考資料を含んでいます。","breadcrumbs":"付録","id":"appendix-00.html#a付録","title":"付録"},"appendix-01-keywords.html#a付録a-キーワード":{"body":"以下のリストは、現在、あるいは将来Rust言語により使用されるために予約されているキーワードを含んでいます。 そのため、識別子として使用することはできません。識別子の例は、関数名、変数名、引数名、構造体のフィールド名、 モジュール名、クレート名、定数名、マクロ名、静的な値の名前、属性名、型名、トレイト名、ライフタイム名です。","breadcrumbs":"付録 » 付録A: キーワード","id":"appendix-01-keywords.html#a付録a-キーワード","title":"付録A: キーワード"},"appendix-01-keywords.html#a将来的な使用のために予約されているキーワード":{"body":"以下のキーワードには機能が何もないものの、将来的に使用される可能性があるので、Rustにより予約されています。 abstract alignof become box do final macro offsetof override priv proc pure sizeof typeof unsized virtual yield","breadcrumbs":"付録 » 将来的な使用のために予約されているキーワード","id":"appendix-01-keywords.html#a将来的な使用のために予約されているキーワード","title":"将来的な使用のために予約されているキーワード"},"appendix-01-keywords.html#a現在使用されているキーワード":{"body":"以下のキーワードは、解説された通りの機能が現状あります。 as - 基礎的なキャストの実行、要素を含む特定のトレイトの明確化、 use や extern crate 文の要素名を変更する break - 即座にループを抜ける const - 定数要素か定数の生ポインタを定義する continue - 次のループの繰り返しに継続する crate - 外部のクレートかマクロが定義されているクレートを表すマクロ変数をリンクする else - if と if let フロー制御構文の規定 enum - 列挙型を定義する extern - 外部のクレート、関数、変数をリンクする false - bool型のfalseリテラル fn - 関数か関数ポインタ型を定義する for - イテレータの要素を繰り返す、トレイトの実装、高階ライフタイムの指定 if - 条件式の結果によって条件分岐 impl - 固有の機能やトレイトの機能を実装する in - for ループ記法の一部 let - 変数を束縛する loop - 無条件にループする match - 値をパターンとマッチさせる mod - モジュールを定義する move - クロージャにキャプチャした変数全ての所有権を奪わせる mut - 参照、生ポインタ、パターン束縛で可変性に言及する pub - 構造体フィールド、 impl ブロック、モジュールで公開性について言及する ref - 参照で束縛する return - 関数から帰る Self - トレイトを実装する型の型エイリアス self - メソッドの主題、または現在のモジュール static - グローバル変数、またはプログラム全体に渡るライフタイム struct - 構造体を定義する super - 現在のモジュールの親モジュール trait - トレイトを定義する true - bool型のtrueリテラル type - 型エイリアスか関連型を定義する unsafe - unsafeなコード、関数、トレイト、実装に言及する use - スコープにシンボルをインポートする where - 型を制限する節に言及する while - 式の結果に基づいて条件的にループする","breadcrumbs":"付録 » 現在使用されているキーワード","id":"appendix-01-keywords.html#a現在使用されているキーワード","title":"現在使用されているキーワード"},"appendix-02-operators.html#a付録b-演算子と記号":{"body":"この付録は、演算子や、単独で現れたり、パス、ジェネリクス、トレイト境界、マクロ、属性、コメント、タプル、 かっこの文脈で現れる他の記号を含むRustの記法の用語集を含んでいます。","breadcrumbs":"付録 » 付録B: 演算子と記号","id":"appendix-02-operators.html#a付録b-演算子と記号","title":"付録B: 演算子と記号"},"appendix-02-operators.html#a演算子":{"body":"表B-1は、Rustの演算子、演算子が文脈で現れる例、短い説明、その演算子がオーバーロード可能かどうかを含んでいます。 演算子がオーバーロード可能ならば、オーバーロードするのに使用する関係のあるトレイトも列挙されています。 表B-1: 演算子 演算子 例 説明 オーバーロードできる? ! ident!(...) , ident!{...} , ident![...] マクロ展開 ! !expr ビット反転、または論理反転 Not != var != expr 非等価比較 PartialEq % expr % expr 余り演算 Rem %= var %= expr 余り演算後に代入 RemAssign & &expr , &mut expr 借用 & &type , &mut type , &'a type , &'a mut type 借用されたポインタ型 & expr & expr ビットAND BitAnd &= var &= expr ビットAND後に代入 BitAndAssign && expr && expr 論理AND * expr * expr 掛け算 Mul * *expr 参照外し * *const type , *mut type 生ポインタ *= var *= expr 掛け算後に代入 MulAssign + trait + trait , 'a + trait 型制限の複合化 + expr + expr 足し算 Add += var += expr 足し算後に代入 AddAssign , expr, expr 引数と要素の区別 - - expr 算術否定 Neg - expr - expr 引き算 Sub -= var -= expr 引き算後に代入 SubAssign -> fn(...) -> type , |...| -> type 関数とクロージャの戻り値型 . expr.ident メンバーアクセス .. .. , expr.. , ..expr , expr..expr 未満範囲リテラル .. ..expr 構造体リテラル更新記法 .. variant(x, ..) , struct_type { x, .. } 「残り全部」パターン束縛 ... expr...expr パターンで: 以下範囲パターン / expr / expr 割り算 Div /= var /= expr 割り算後に代入 DivAssign : pat: type , ident: type 型制約 : ident: expr 構造体フィールド初期化子 : 'a: loop {...} ループラベル ; expr; 文、要素終端子 ; [...; len] 固定長配列記法の一部 << expr << expr 左シフト Shl <<= var <<= expr 左シフト後に代入 ShlAssign < expr < expr 未満比較 PartialOrd <= expr <= expr 以下比較 PartialOrd = var = expr , ident = type 代入/等価 == expr == expr 等価比較 PartialEq => pat => expr matchアーム記法の一部 > expr > expr より大きい比較 PartialOrd >= expr >= expr 以上比較 PartialOrd >> expr >> expr 右シフト Shr >>= var >>= expr 右シフト後に代入 ShrAssign @ ident @ pat パターン束縛 ^ expr ^ expr ビットXOR BitXor ^= var ^= expr ビットXOR後に代入 BitXorAssign | pat | pat パターンOR | |…| expr クロージャ | expr | expr ビットOR BitOr |= var |= expr ビットOR後に代入 BitOrAssign || expr || expr 論理OR ? expr? エラー委譲","breadcrumbs":"付録 » 演算子","id":"appendix-02-operators.html#a演算子","title":"演算子"},"appendix-02-operators.html#a演算子以外のシンボル":{"body":"以下のリストは、演算子として機能しない記号全部を含んでいます; つまり、関数やメソッド呼び出しのようには、 振る舞わないということです。 表B-2は、単独で出現し、いろんな箇所で合法になる記号を示しています。 表B-2: スタンドアローン記法 シンボル 説明 'ident 名前付きのライフタイム、あるいはループラベル ...u8 , ...i32 , ...f64 , ...usize など 特定の型の数値リテラル \"...\" 文字列リテラル r\"...\" , r#\"...\"# , r##\"...\"## など 生文字列リテラル、エスケープ文字は処理されません b\"...\" バイト文字列リテラル、文字列の代わりに [u8] を構築します br\"...\" , br#\"...\"# , br##\"...\"## など 生バイト文字列リテラル、生文字列とバイト文字列の組み合わせ '...' 文字リテラル b'...' ASCIIバイトリテラル |...| expr クロージャ ! 常に発散関数の空のボトム型 _ 「無視」パターン束縛: 整数リテラルを見やすくするのにも使われる 表B-3は、要素へのモジュール階層を通したパスの文脈で出現する記号を示しています。 表B-3: パス関連記法 シンボル 説明 ident::ident 名前空間パス ::path クレートルートに相対的なパス(すなわち、明示的な絶対パス) self::path 現在のモジュールに相対的なパス(すなわち、明示的な相対パス) super::path 現在のモジュールの親モジュールに相対的なパス type::ident , ::ident 関連定数、関数、型 ::... 直接名前付けできない型の関連要素(例, <&T>::... , <[T]>::... など) trait::method(...) 定義したトレイトを名指ししてメソッド呼び出しを明確化する type::method(...) 定義されている型を名指ししてメソッド呼び出しを明確化する ::method(...) トレイト と 型を名指ししてメソッド呼び出しを明確化する 表B-4は、ジェネリックな型引数の文脈で出現する記号を示しています。 表B-4: ジェネリクス シンボル 説明 path<...> 型の内部のジェネリック型への引数を指定する(例、 Vec ) path::<...> , method::<...> 式中のジェネリックな型、関数、メソッドへの引数を指定する。しばしばターボ・フィッシュ(turbofish)と称される。(例、 \"42\".parse::() ) fn ident<...> ... ジェネリックな関数を定義する struct ident<...> ... ジェネリックな構造体を定義する enum ident<...> ... ジェネリックな列挙型を定義する impl<...> ... ジェネリックな実装を定義する for<...> type 高階ライフタイム境界 type 1つ以上の関連型に代入されたジェネリックな型(例、 Iterator ) 表B-5は、ジェネリック型引数をトレイト境界で制約する文脈で出現する記号を示しています。 表B-5: トレイト境界制約 シンボル 説明 T: U U を実装する型に制約されるジェネリック引数 T T: 'a ライフタイム 'a よりも長生きしなければならないジェネリック型 T (型がライフタイムより長生きするとは、 'a よりも短いライフタイムの参照を何も遷移的に含められないことを意味する) T : 'static ジェネリック型 T が 'static なもの以外の借用された参照を何も含まない 'b: 'a ジェネリックなライフタイム 'b がライフタイム 'a より長生きしなければならない T: ?Sized ジェネリック型引数が動的サイズ付け型であることを許容する 'a + trait , trait + trait 複合型制約 表B-6は、マクロの呼び出しや定義、要素に属性を指定する文脈で出現する記号を示しています。 表B-6: マクロと属性 シンボル 説明 #[meta] 外部属性 #![meta] 内部属性 $ident マクロ代用 $ident:kind マクロキャプチャ $(…)… マクロの繰り返し 表B-7は、コメントを生成する記号を示しています。 表B-7: コメント シンボル 説明 // 行コメント //! 内部行docコメント /// 外部行docコメント /*...*/ ブロックコメント /*!...*/ 内部ブロックdocコメント /**...*/ 外部ブロックdocコメント タプル 表B-8は、タプルの文脈で出現する記号を示しています。 表B-8: タプル シンボル 説明 () 空のタプル (ユニットとしても知られる)、リテラル、型両方 (expr) 括弧付きの式 (expr,) 1要素タプル式 (type,) 1要素タプル型 (expr, ...) タプル式 (type, ...) タプル型 expr(expr, ...) 関数呼び出し式; タプル struct やタプル enum 列挙子を初期化するのにも使用される ident!(...) , ident!{...} , ident![...] マクロ呼び出し expr.0 , expr.1 , など タプル添え字アクセス 表B-9は、波括弧が使用される文脈を表示しています。 表B-9: 波括弧 文脈 説明 {...} ブロック式 Type {...} struct リテラル 表B-10は、角括弧が使用される文脈を表示しています。 表B-10: 角括弧 文脈 説明 [...] 配列リテラル [expr; len] len 個 expr を含む配列リテラル [type; len] len 個の type のインスタンスを含む配列型 expr[expr] コレクション添え字アクセス。オーバーロード可能 ( Index , IndexMut ) expr[..] , expr[a..] , expr[..b] , expr[a..b] Range 、 RangeFrom 、 RangeTo 、 RangeFull を「添え字」として使用してコレクション・スライシングの振りをするコレクション添え字アクセス","breadcrumbs":"付録 » 演算子以外のシンボル","id":"appendix-02-operators.html#a演算子以外のシンボル","title":"演算子以外のシンボル"},"appendix-03-derivable-traits.html#aプログラマ用の出力の-debug":{"body":"Debug トレイトにより、フォーマット文字列でのデバッグ整形が可能になり、 {} プレースホルダー内に :? を追記することで表します。 Debug トレイトにより、デバッグ目的で型のインスタンスを出力できるようになるので、あなたや型を使用する他のプログラマが、 プログラムの実行の特定の箇所でインスタンスを調べられます。 Debug トレイトは、例えば、 assert_eq! マクロを使用する際などに必要になります。 このマクロは、プログラマがどうして2つのインスタンスが等価でなかったのか確認できるように、 等価アサートが失敗したら、引数として与えられたインスタンスの値を出力します。","breadcrumbs":"付録 » プログラマ用の出力の Debug","id":"appendix-03-derivable-traits.html#aプログラマ用の出力の-debug","title":"プログラマ用の出力の Debug"},"appendix-03-derivable-traits.html#a付録c-導出可能なトレイト":{"body":"本のいろんな箇所で derive 属性について議論しました。これは構造体や、enum定義に適用できます。 derive 属性は、 derive 記法で注釈した型に対して独自の既定の実装でトレイトを実装するコードを生成します。 この付録では、標準ライブラリの derive と共に使用できる全トレイトの参照を提供します。各節は以下を講義します: このトレイトを導出する演算子やメソッドで可能になること derive が提供するトレイトの実装がすること トレイトを実装することが型についてどれほど重要か そのトレイトを実装できたりできなかったりする条件 そのトレイトが必要になる処理の例 derive 属性が提供する以外の異なる振る舞いが欲しいなら、それらを手動で実装する方法の詳細について、 各トレイトの標準ライブラリのドキュメンテーションを調べてください。 標準ライブラリで定義されている残りのトレイトは、 derive で自分の型に実装することはできません。 これらのトレイトには知覚できるほどの既定の振る舞いはないので、自分が達成しようしていることに対して、 道理が通る方法でそれらを実装するのはあなた次第です。 導出できないトレイトの例は Display で、これはエンドユーザ向けのフォーマットを扱います。常に、エンドユーザ向けに型を表示する適切な方法について、 考慮すべきです。型のどの部分をエンドユーザは見ることができるべきでしょうか?どの部分を関係があると考えるでしょうか? どんな形式のデータがエンドユーザにとって最も関係があるでしょうか?Rustコンパイラには、 この見識がないため、適切な既定動作を提供してくれないのです。 この付録で提供される導出可能なトレイトのリストは、包括的ではありません: ライブラリは、自身のトレイトに derive を実装でき、 derive と共に使用できるトレイトのリストが実に限りのないものになってしまうのです。 derive の実装には、 プロシージャルなマクロが関連します。マクロについては、付録Dで講義します。","breadcrumbs":"付録 » 付録C: 導出可能なトレイト","id":"appendix-03-derivable-traits.html#a付録c-導出可能なトレイト","title":"付録C: 導出可能なトレイト"},"appendix-03-derivable-traits.html#a値を固定サイズの値にマップする-hash":{"body":"Hash トレイトにより、任意のサイズの型のインスタンスを取り、そのインスタンスをハッシュ関数で固定サイズの値にマップできます。 Hash を導出すると、 hash メソッドを実装します。 hash の導出された実装は、 型の各部品に対して呼び出した hash の結果を組み合わせます。つまり、 Hash を導出するには、 全フィールドと値も Hash を実装しなければならないということです。 Hash が必要になる例は、 HashMap にキーを格納し、データを効率的に格納することです。","breadcrumbs":"付録 » 値を固定サイズの値にマップする Hash","id":"appendix-03-derivable-traits.html#a値を固定サイズの値にマップする-hash","title":"値を固定サイズの値にマップする Hash"},"appendix-03-derivable-traits.html#a値を複製する-clone-と-copy":{"body":"Clone トレイトにより値のディープコピーを明示的に行うことができ、複製のプロセスは、任意のコードを実行し、 ヒープデータをコピーすることに関係がある可能性があります。 Clone について詳しくは、 第4章の「変数とデータの相互作用法: Clone」節を参照されたし。 Clone を導出すると、 clone メソッドを実装し、これは型全体に対して実装されると、 型の各部品に対して clone を呼び出します。要するに、 Clone を導出するには、 型のフィールドと値全部も Clone を実装していなければならないということです。 Clone が必要になる例は、スライスに対して to_vec メソッドを呼び出すことです。スライスは、 含んでいる型のインスタンスの所有権を持たないが、 to_vec で返されるベクタはそのインスタンスを所有する必要があるので、 to_vec は各要素に対して clone を呼び出します。故に、スライスに格納される型は、 Clone を実装しなければならないのです。 Copy トレイトにより、スタックに格納されたビットをコピーするだけで値を複製できます; 任意のコードは必要ありません。 Copy について詳しくは、第4章の「スタックのみのデータ: Copy」を参照されたし。 Copy トレイトは、プログラマがメソッドをオーバーロードし、任意のコードが実行されないという前提を侵害することを妨げるメソッドは何も定義しません。 そのため、全プログラマは、値のコピーは非常に高速であることを前提にすることができます。 部品すべてが Copy を実装する任意の型に対して Copy を導出することができます。 Clone も実装する型に対してのみ、 Copy トレイトを適用することができます。何故なら、 Copy を実装する型には、 Copy と同じ作業を行う Clone の瑣末(さまつ)な実装があるからです。 Copy トレイトは稀にしか必要になりません; Copy を実装する型では最適化が利用可能になります。 つまり、 clone を呼び出す必要がなくなり、コードがより簡潔になるということです。 Copy で可能なこと全てが Clone でも達成可能ですが、コードがより遅い可能性や、 clone を使用しなければならない箇所があったりします。","breadcrumbs":"付録 » 値を複製する Clone と Copy","id":"appendix-03-derivable-traits.html#a値を複製する-clone-と-copy","title":"値を複製する Clone と Copy"},"appendix-03-derivable-traits.html#a既定値のための-default":{"body":"Default トレイトにより、型に対して既定値を生成できます。 Default を導出すると、 default 関数を実装します。 default 関数の導出された実装は、型の各部品に対して default 関数を呼び出します。つまり、 Default を導出するには、型の全フィールドと値も Default を実装しなければならないということです。 Default::default 関数は、 第5章の「構造体更新記法で他のインスタンスからインスタンスを生成する」節で議論した構造体更新記法と組み合わせてよく使用されます。 構造体のいくつかのフィールドをカスタマイズし、それから ..Default::default() を使用して、 残りのフィールドに対して既定値をセットし使用することができます。 例えば、 Default トレイトは、 Option インスタンスに対してメソッド unwrap_or_default を使用する時に必要になります。 Option が None ならば、メソッド unwrap_or_default は、 Option に格納された型 T に対して Default::default の結果を返します。","breadcrumbs":"付録 » 既定値のための Default","id":"appendix-03-derivable-traits.html#a既定値のための-default","title":"既定値のための Default"},"appendix-03-derivable-traits.html#a等価比較のための-partialeq-と-eq":{"body":"PartialEq トレイトにより、型のインスタンスを比較して、等価性をチェックでき、 == と != 演算子の使用を可能にします。 PartialEq を導出すると、 eq メソッドを実装します。構造体に PartialEq を導出すると、 全 フィールドが等しい時のみ2つのインスタンスは等価になり、いずれかのフィールドが等価でなければ、 インスタンスは等価ではなくなります。enumに導出すると、各列挙子は、自身には等価ですが、他の列挙子には等価ではありません。 PartialEq トレイトは例えば、 assert_eq! マクロを使用する際に必要になります。 これは、等価性のためにとある型の2つのインスタンスを比較できる必要があります。 Eq トレイトにはメソッドはありません。その目的は、注釈された型の全値に対して、値が自身と等しいことを通知することです。 Eq トレイトは、 PartialEq を実装する全ての型が Eq を実装できるわけではないものの、 PartialEq も実装する型に対してのみ適用できます。これの一例は、浮動小数点数型です: 浮動小数点数の実装により、非数字( NaN )値の2つのインスタンスはお互いに等価ではないことが宣言されます。 Eq が必要になる一例が、 HashMap のキーで、 HashMap が、2つのキーが同じであると判定できます。","breadcrumbs":"付録 » 等価比較のための PartialEq と Eq","id":"appendix-03-derivable-traits.html#a等価比較のための-partialeq-と-eq","title":"等価比較のための PartialEq と Eq"},"appendix-03-derivable-traits.html#a順序付き比較のための-partialord-と-ord":{"body":"PartialOrd トレイトにより、ソートする目的で型のインスタンスを比較できます。 PartialOrd を実装する型は、 < 、 > 、 <= 、 >= 演算子を使用することができます。 PartialEq も実装する型に対してのみ、 PartialOrd トレイトを適用できます。 PartialOrd を導出すると、 partial_cmp メソッドを実装し、これは、与えられた値が順序付けられない時に None になる Option を返します。 その型のほとんどの値は比較できるものの、順序付けできない値の例として、非数字( NaN )浮動小数点値が挙げられます。 partial_cmp をあらゆる浮動小数点数と NaN 浮動小数点数で呼び出すと、 None が返るでしょう。 構造体に導出すると、フィールドが構造体定義で現れる順番で各フィールドの値を比較することで2つのインスタンスを比較します。 enumに導出すると、enum定義で先に定義された列挙子が、後に列挙された列挙子よりも小さいと考えられます。 PartialOrd トレイトが必要になる例には、低い値と高い値で指定される範囲の乱数を生成する rand クレートの gen_range メソッドが挙げられます。 Ord トレイトにより、注釈した型のあらゆる2つの値に対して、合法な順序付けが行えることがわかります。 Ord トレイトは cmp メソッドを実装し、これは、常に合法な順序付けが可能なので、 Option ではなく、 Ordering を返します。 PartialOrd と Eq ( Eq は PartialEq も必要とします)も実装している型にしか、 Ord トレイトを適用することはできません。構造体とenumで導出したら、 PartialOrd で、 partial_cmp の導出した実装と同じように cmp は振る舞います。 Ord が必要になる例は、 BTreeSet に値を格納する時です。 これは、値のソート順に基づいてデータを格納するデータ構造です。","breadcrumbs":"付録 » 順序付き比較のための PartialOrd と Ord","id":"appendix-03-derivable-traits.html#a順序付き比較のための-partialord-と-ord","title":"順序付き比較のための PartialOrd と Ord"},"appendix-04-macros.html#aマクロと関数の違い":{"body":"基本的に、マクロは、他のコードを記述するコードを書く術であり、これは メタプログラミング として知られています。 付録Cで、 derive 属性を議論し、これは、色々なトレイトの実装を生成してくれるのでした。 また、本を通して println! や vec! マクロを使用してきました。これらのマクロは全て、 展開 され、 手で書いたよりも多くのコードを生成します。 メタプログラミングは、書いて管理しなければならないコード量を減らすのに有用で、これは、関数の役目の一つでもあります。 ですが、マクロには関数にはない追加の力があります。 関数シグニチャは、関数の引数の数と型を宣言しなければなりません。一方、マクロは可変長の引数を取れます: println!(\"hello\") のように1引数で呼んだり、 println!(\"hello {}\", name) のように2引数で呼んだりできるのです。 また、マクロは、コンパイラがコードの意味を解釈する前に展開されるので、例えば、 与えられた型にトレイトを実装できます。関数ではできません。何故なら、関数は実行時に呼ばれ、 トレイトはコンパイル時に実装される必要があるからです。 関数ではなくマクロを実装する欠点は、Rustコードを記述するRustコードを書いているので、 関数定義よりもマクロ定義は複雑になることです。この間接性のために、マクロ定義は一般的に、 関数定義よりも、読みにくく、わかりにくく、管理しづらいです。 マクロと関数の別の違いは、マクロ定義は、関数定義のようには、モジュール内で名前空間分けされないことです。 外部クレートを使用する際に予期しない名前衝突を回避するために、 #[macro_use] 注釈を使用して、 外部クレートをスコープに導入するのと同時に、自分のプロジェクトのスコープにマクロを明示的に導入しなければなりません。 以下の例は、 serde クレートに定義されているマクロ全部を現在のクレートのスコープに導入するでしょう: #[macro_use]\nextern crate serde; この明示的注釈なしに extern crate が既定でスコープにマクロを導入できたら、偶然同じ名前のマクロを定義している2つのクレートを使用できなくなるでしょう。 現実的には、この衝突はあまり起きませんが、使用するクレートが増えるほど、可能性は高まります。 マクロと関数にはもう一つ、重要な違いがあります: ファイル内で呼び出す 前 にマクロはスコープに導入しなければなりませんが、 一方で関数はどこにでも定義でき、どこでも呼び出せます。","breadcrumbs":"付録 » マクロと関数の違い","id":"appendix-04-macros.html#aマクロと関数の違い","title":"マクロと関数の違い"},"appendix-04-macros.html#aマクロの未来":{"body":"将来的にRustは、宣言的マクロとプロシージャルマクロを拡張するでしょう。 macro キーワードでより良い宣言的マクロシステムを使用し、 derive だけよりもよりパワフルな作業のより多くの種類のプロシージャルマクロを追加するでしょう。 この本の出版時点ではこれらのシステムはまだ開発中です; 最新の情報は、オンラインのRustドキュメンテーションをお調べください。","breadcrumbs":"付録 » マクロの未来","id":"appendix-04-macros.html#aマクロの未来","title":"マクロの未来"},"appendix-04-macros.html#a一般的なメタプログラミングのために-macro_rules-で宣言的なマクロ":{"body":"Rustにおいて、最もよく使用される形態のマクロは、 宣言的マクロ です。これらは時として、 例によるマクロ 、 macro_rules! マクロ 、あるいはただ単に マクロ とも称されます。 核となるのは、宣言的マクロは、Rustの match 式に似た何かを書けるということです。第6章で議論したように、 match 式は、式を取り、式の結果の値をパターンと比較し、それからマッチしたパターンに紐づいたコードを実行する制御構造です。 マクロも自身に紐づいたコードがあるパターンと値を比較します; この場面で値とは、 マクロに渡されたリテラルのRustのソースコードそのもの、パターンは、そのソースコードの構造と比較され、 各パターンに紐づいたコードは、マクロに渡されたコードを置き換えるコードです。これは全て、コンパイル時に起きます。 マクロを定義するには、 macro_rules! 構文を使用します。 vec! マクロが定義されている方法を見て、 macro_rules! を使用する方法を探究しましょう。 vec! マクロを使用して特定の値で新しいベクタを生成する方法は、 第8章で講義しました。例えば、以下のマクロは、3つの整数を中身にする新しいベクタを生成します: let v: Vec = vec![1, 2, 3]; また、 vec! マクロを使用して2整数のベクタや、5つの文字列スライスのベクタなども生成できます。 同じことを関数を使って行うことはできません。予め、値の数や型がわかっていないからです。 リストD-1で些(いささ)か簡略化された vec! マクロの定義を見かけましょう。 #[macro_export]\nmacro_rules! vec { ( $( $x:expr ),* ) => { { let mut temp_vec = Vec::new(); $( temp_vec.push($x); )* temp_vec } };\n} リストD-1: vec! マクロ定義の簡略化されたバージョン 標準ライブラリの vec! マクロの実際の定義は、予め正確なメモリ量を確保するコードを含みます。 そのコードは、ここでは簡略化のために含まない最適化です。 #[macro_export] 注釈は、マクロを定義しているクレートがインポートされる度にこのマクロが利用可能になるべきということを示しています。 この注釈がなければ、このクレートに依存する誰かが #[macro_use] 注釈を使用していても、 このマクロはスコープに導入されないでしょう。 それから、 macro_rules! でマクロ定義と定義しているマクロの名前をビックリマーク なしで 始めています。 名前はこの場合 vec であり、マクロ定義の本体を意味する波括弧が続いています。 vec! 本体の構造は、 match 式の構造に類似しています。ここではパターン ( $( $x:expr ),* ) の1つのアーム、 => とこのパターンに紐づくコードのブロックが続きます。パターンが合致すれば、紐づいたコードのブロックが発されます。 これがこのマクロの唯一のパターンであることを踏まえると、合致する合法的な方法は一つしかありません; それ以外は、全部エラーになるでしょう。より複雑なマクロには、2つ以上のアームがあるでしょう。 マクロ定義で合法なパターン記法は、第18章で講義したパターン記法とは異なります。というのも、 マクロのパターンは値ではなく、Rustコードの構造に対してマッチされるからです。リストD-1のパターンの部品がどんな意味か見ていきましょう; マクロパターン記法全ては 参考文献 をご覧ください。 まず、1組のカッコがパターン全体を囲んでいます。次にドル記号( $ )、そして1組のカッコが続き、 このかっこは、置き換えるコードで使用するためにかっこ内でパターンにマッチする値をキャプチャします。 $() の内部には、 $x:expr があり、これは任意のRust式にマッチし、その式に $x という名前を与えます。 $() に続くカンマは、 $() にキャプチャされるコードにマッチするコードの後に、区別を意味するリテラルのカンマ文字が現れるという選択肢もあることを示唆しています。 カンマに続く * は、パターンが * の前にあるもの0個以上にマッチすることを指定しています。 このマクロを vec![1, 2, 3]; と呼び出すと、 $x パターンは、3つの式 1 、 2 、 3 で3回マッチします。 さて、このアームに紐づくコードの本体のパターンに目を向けましょう: $()* 部分内部の temp_vec.push() コードは、 パターンがマッチした回数に応じて0回以上パターン内で $() にマッチする箇所ごとに生成されます。 $x はマッチした式それぞれに置き換えられます。このマクロを vec![1, 2, 3]; と呼び出すと、 このマクロ呼び出しを置き換え、生成されるコードは以下のようになるでしょう: let mut temp_vec = Vec::new();\ntemp_vec.push(1);\ntemp_vec.push(2);\ntemp_vec.push(3);\ntemp_vec 任意の型のあらゆる数の引数を取り、指定した要素を含むベクタを生成するコードを生成できるマクロを定義しました。 多くのRustプログラマは、マクロを 書く よりも 使う 方が多いことを踏まえて、これ以上 macro_rules! を議論しません。 マクロの書き方をもっと学ぶには、オンラインドキュメンテーションか他のリソース、 “The Little Book of Rust Macros ( 訳注 : Rustのマクロの小さな本)などを調べてください。","breadcrumbs":"付録 » 一般的なメタプログラミングのために macro_rules! で宣言的なマクロ","id":"appendix-04-macros.html#a一般的なメタプログラミングのために-macro_rules-で宣言的なマクロ","title":"一般的なメタプログラミングのために macro_rules! で宣言的なマクロ"},"appendix-04-macros.html#a付録d-マクロ":{"body":"本全体で println! のようなマクロを使用してきましたが、マクロがなんなのかや、 どう動いているのかということは完全には探究していません。この付録は、マクロを以下のように説明します: マクロとはなんなのかと関数とどう違うのか 宣言的なマクロを定義してメタプログラミングをする方法 プロシージャルなマクロを定義して独自の derive トレイトを生成する方法 マクロは今でも、Rustにおいては発展中なので、付録でマクロの詳細を講義します。マクロは変わってきましたし、 近い将来、Rust1.0からの言語の他の機能や標準ライブラリに比べて速いスピードで変化するので、 この節は、本の残りの部分よりも時代遅れになる可能性が高いです。Rustの安定性保証により、 ここで示したコードは、将来のバージョンでも動き続けますが、この本の出版時点では利用可能ではないマクロを書くための追加の能力や、 より簡単な方法があるかもしれません。この付録から何かを実装しようとする場合には、そのことを肝に銘じておいてください。","breadcrumbs":"付録 » 付録D: マクロ","id":"appendix-04-macros.html#a付録d-マクロ","title":"付録D: マクロ"},"appendix-04-macros.html#a独自の-derive-のためのプロシージャルマクロ":{"body":"2番目の形態のマクロは、より関数(1種の手続きです)に似ているので、 プロシージャル・マクロ (procedural macro; 訳注 : 手続きマクロ)と呼ばれます。プロシージャルマクロは、宣言的マクロのようにパターンにマッチさせ、 そのコードを他のコードと置き換えるのではなく、入力として何らかのRustコードを受け付け、そのコードを処理し、 出力として何らかのRustコードを生成します。これを執筆している時点では、 derive 注釈にトレイト名を指定することで、 型に自分のトレイトを実装できるプロシージャルマクロを定義できるだけです。 hello_macro という関連関数が1つある HelloMacro というトレイトを定義する hello_macro というクレートを作成します。 クレートの使用者に使用者の型に HelloMacro トレイトを実装することを強制するのではなく、 使用者が型を #[derive(HelloMacro)] で注釈して hello_macro 関数の既定の実装を得られるように、 プロシージャルマクロを提供します。既定の実装は、 Hello, Macro! My name is TypeName! ( 訳注 : こんにちは、マクロ!僕の名前はTypeNameだよ!)と出力し、 ここで TypeName はこのトレイトが定義されている型の名前です。言い換えると、他のプログラマに我々のクレートを使用して、 リストD-2のようなコードを書けるようにするクレートを記述します。 ファイル名: src/main.rs extern crate hello_macro;\n#[macro_use]\nextern crate hello_macro_derive; use hello_macro::HelloMacro; #[derive(HelloMacro)]\nstruct Pancakes; fn main() { Pancakes::hello_macro();\n} リストD-2: 我々のプロシージャルマクロを使用した時にクレートの使用者が書けるようになるコード このコードは完成したら、 Hello, Macro! My name is Pancakes! ( Pancakes : ホットケーキ)と出力します。最初の手順は、 新しいライブラリクレートを作成することです。このように: $ cargo new hello_macro --lib 次に HelloMacro トレイトと関連関数を定義します: ファイル名: src/lib.rs pub trait HelloMacro { fn hello_macro();\n} トレイトと関数があります。この時点でクレートの使用者は、以下のように、 このトレイトを実装して所望の機能を達成できるでしょう。 extern crate hello_macro; use hello_macro::HelloMacro; struct Pancakes; impl HelloMacro for Pancakes { fn hello_macro() { println!(\"Hello, Macro! My name is Pancakes!\"); }\n} fn main() { Pancakes::hello_macro();\n} しかしながら、使用者は、 hello_macro を使用したい型それぞれに実装ブロックを記述する必要があります; この作業をしなくても済むようにしたいです。 さらに、まだトレイトが実装されている型の名前を出力する hello_macro 関数に既定の実装を提供することはできません: Rustにはリフレクションの能力がないので、型の名前を実行時に検索することができないのです。 コンパイル時にコード生成するマクロが必要です。 注釈: リフレクションとは、実行時に型名や関数の中身などを取得する機能のことです。 言語によって提供されていたりいなかったりしますが、実行時にメタデータがないと取得できないので、 RustやC++のようなアセンブリコードに翻訳され、パフォーマンスを要求される高級言語では、提供されないのが一般的と思われます。 次の手順は、プロシージャルマクロを定義することです。これを執筆している時点では、プロシージャルマクロは、 独自のクレートに存在する必要があります。最終的には、この制限は持ち上げられる可能性があります。 クレートとマクロクレートを構成する慣習は以下の通りです: foo というクレートに対して、 独自のderiveプロシージャルマクロクレートは foo_derive と呼ばれます。 hello_macro プロジェクト内に、 hello_macro_derive と呼ばれる新しいクレートを開始しましょう: $ cargo new hello_macro_derive --lib 2つのクレートは緊密に関係しているので、 hello_macro クレートのディレクトリ内にプロシージャルマクロクレートを作成しています。 hello_macro のトレイト定義を変更したら、 hello_macro_derive のプロシージャルマクロの実装も変更しなければならないでしょう。 2つのクレートは個別に公開される必要があり、これらのクレートを使用するプログラマは、 両方を依存に追加し、スコープに導入する必要があるでしょう。代わりに、 hello_macro クレートに依存として、 hello_macro_derive を使用させ、プロシージャルマクロのコードを再エクスポートすることもできるでしょう。 プロジェクトの構造によっては、プログラマが derive 機能を使用したくなくても、 hello_macro を使用することが可能になります。 hello_macro_derive クレートをプロシージャルマクロクレートとして宣言する必要があります。 また、すぐにわかるように、 syn と quote クレートの機能も必要になるので、依存として追加する必要があります。 以下を hello_macro_derive の Cargo.toml ファイルに追加してください: ファイル名: hello_macro_derive/Cargo.toml [lib]\nproc-macro = true [dependencies]\nsyn = \"0.11.11\"\nquote = \"0.3.15\" プロシージャルマクロの定義を開始するために、 hello_macro_derive クレートの src/lib.rs ファイルにリストD-3のコードを配置してください。 impl_hello_macro 関数の定義を追加するまでこのコードはコンパイルできないことに注意してください。 ファイル名: hello_macro_derive/src/lib.rs extern crate proc_macro;\nextern crate syn;\n#[macro_use]\nextern crate quote; use proc_macro::TokenStream; #[proc_macro_derive(HelloMacro)]\npub fn hello_macro_derive(input: TokenStream) -> TokenStream { // 型定義の文字列表現を構築する // Construct a string representation of the type definition let s = input.to_string(); // 文字列表現を構文解析する // Parse the string representation let ast = syn::parse_derive_input(&s).unwrap(); // implを構築する // Build the impl let gen = impl_hello_macro(&ast); // 生成されたimplを返す // Return the generated impl gen.parse().unwrap()\n} リストD-3: Rustコードを処理するためにほとんどのプロシージャルマクロクレートに必要になるコード D-3での関数の分け方に注目してください; これは、目撃あるいは作成するほとんどのプロシージャルマクロクレートで同じになるでしょう。 プロシージャルマクロを書くのが便利になるからです。 impl_hello_macro 関数が呼ばれる箇所で行うことを選ぶものは、 プロシージャルマクロの目的によって異なるでしょう。 3つの新しいクレートを導入しました: proc_macro 、 syn 、 quote です。 proc_macro クレートは、 Rustに付随してくるので、 Cargo.toml の依存に追加する必要はありませんでした。 proc_macro クレートにより、 RustコードをRustコードを含む文字列に変換できます。 syn クレートは、文字列からRustコードを構文解析し、 処理を行えるデータ構造にします。 quote クレートは、 syn データ構造を取り、Rustコードに変換し直します。 これらのクレートにより、扱いたい可能性のあるあらゆる種類のRustコードを構文解析するのがはるかに単純になります: Rustコードの完全なパーサを書くのは、単純な作業ではないのです。 hello_macro_derive 関数は、ライブラリの使用者が型に #[derive(HelloMacro)] を指定した時に呼び出されます。 その理由は、ここで hello_macro_derive 関数を proc_macro_derive で注釈し、トレイト名に一致する HelloMacro を指定したからです; これは、ほとんどのプロシージャルマクロが倣う慣習です。 この関数はまず、 TokenStream からの input を to_string を呼び出して String に変換します。 この String は、 HelloMacro を導出しているRustコードの文字列表現になります。 リストD-2の例で、 s は struct Pancakes; という String 値になります。 それが #[derive(HelloMacro)] 注釈を追加したRustコードだからです。 注釈: これを執筆している時点では、 TokenStream は文字列にしか変換できません。 将来的にはよりリッチなAPIになるでしょう。 さて、Rustコードの String をそれから解釈して処理を実行できるデータ構造に構文解析する必要があります。 ここで syn が登場します。 syn の parse_derive_input 関数は、 String を取り、 構文解析されたRustコードを表す DeriveInput 構造体を返します。以下のコードは、 文字列 struct Pancakes; を構文解析して得られる DeriveInput 構造体の関係のある部分を表示しています: DeriveInput { // --snip-- ident: Ident( \"Pancakes\" ), body: Struct( Unit )\n} この構造体のフィールドは、構文解析したRustコードが Pancakes という ident (識別子、つまり名前)のユニット構造体であることを示しています。 この構造体にはRustコードのあらゆる部分を記述するフィールドがもっと多くあります; DeriveInput の syn ドキュメンテーション で詳細を確認してください。 この時点では、含みたい新しいRustコードを構築する impl_hello_macro 関数を定義していません。 でもその前に、この hello_macro_derive 関数の最後の部分で quote クレートの parse 関数を使用して、 impl_hello_macro 関数の出力を TokenStream に変換し直していることに注目してください。 返された TokenStream をクレートの使用者が書いたコードに追加しているので、クレートをコンパイルすると、 我々が提供している追加の機能を得られます。 parse_derive_input か parse 関数がここで失敗したら、 unwrap を呼び出してパニックしていることにお気付きかもしれません。 エラー時にパニックするのは、プロシージャルマクロコードでは必要なことです。何故なら、 proc_macro_derive 関数は、プロシージャルマクロAPIに従うように Result ではなく、 TokenStream を返さなければならないからです。 unwrap を使用してこの例を簡略化することを選択しました; プロダクションコードでは、 panic! か expect を使用して何が間違っていたのかより具体的なエラーメッセージを提供すべきです。 今や、 TokenStream からの注釈されたRustコードを String と DeriveInput インスタンスに変換するコードができたので、 注釈された型に HelloMacro トレイトを実装するコードを生成しましょう: ファイル名: hello_macro_derive/src/lib.rs fn impl_hello_macro(ast: &syn::DeriveInput) -> quote::Tokens { let name = &ast.ident; quote! { impl HelloMacro for #name { fn hello_macro() { println!(\"Hello, Macro! My name is {}\", stringify!(#name)); } } }\n} ast.ident で注釈された型の名前(識別子)を含む Ident 構造体インスタンスを得ています。 リストD-2のコードは、 name が Ident(\"Pancakes\") になることを指定しています。 quote! マクロは、返却し quote::Tokens に変換したいRustコードを書かせてくれます。このマクロはまた、 非常にかっこいいテンプレート機構も提供してくれます; #name と書け、 quote! は、 それを name という変数の値と置き換えます。普通のマクロが動作するのと似た繰り返しさえ行えます。 完全なイントロダクションは、 quote クレートのdoc をご確認ください。 プロシージャルマクロに使用者が注釈した型に対して HelloMacro トレイトの実装を生成してほしく、 これは #name を使用することで得られます。トレイトの実装には1つの関数 hello_macro があり、 この本体に提供したい機能が含まれています: Hello, Macro! My name is 、そして、注釈した型の名前を出力する機能です。 ここで使用した stringify! マクロは、言語に埋め込まれています。 1 + 2 などのようなRustの式を取り、 コンパイル時に \"1 + 2\" のような文字列リテラルにその式を変換します。これは、 format! や println! とは異なります。 こちらは、式を評価し、そしてその結果を String に変換します。 #name 入力が文字通り出力される式という可能性もあるので、 stringify! を使用しています。 stringify! を使用すると、コンパイル時に #name を文字列リテラルに変換することで、 メモリ確保しなくても済みます。 この時点で、 cargo build は hello_macro と hello_macro_derive の両方で成功するはずです。 これらのクレートをリストD-2のコードにフックして、プロシージャルマクロが動くところを確認しましょう! cargo new --bin pancakes で projects ディレクトリに新しいバイナリプロジェクトを作成してください。 hello_macro と hello_macro_derive を依存として pancakes クレートの Cargo.toml に追加する必要があります。 自分のバージョンの hello_macro と hello_macro_derive を https://crates.io/ に公開するつもりなら、 普通の依存になるでしょう; そうでなければ、以下のように path 依存として指定できます: [dependencies]\nhello_macro = { path = \"../hello_macro\" }\nhello_macro_derive = { path = \"../hello_macro/hello_macro_derive\" } リストD-2のコードを src/main.rs に配置し、 cargo run を実行してください: Hello, Macro! My name is Pancakes と出力するはずです。 プロシージャルマクロの HelloMacro トレイトの実装は、 pancakes クレートが実装する必要なく、包含されました; #[derive(HelloMacro)] がトレイトの実装を追加したのです。","breadcrumbs":"付録 » 独自の derive のためのプロシージャルマクロ","id":"appendix-04-macros.html#a独自の-derive-のためのプロシージャルマクロ","title":"独自の derive のためのプロシージャルマクロ"},"appendix-05-translation.html#a付録e-本の翻訳":{"body":"英語以外の言語のリソースです。ほとんどは翻訳中です; Translationsラベル を確認して、 新しい翻訳の手助けや開始したことをお知らせください!","breadcrumbs":"付録 » 付録E: 本の翻訳","id":"appendix-05-translation.html#a付録e-本の翻訳","title":"付録E: 本の翻訳"},"appendix-06-newest-features.html#a128ビット整数":{"body":"Rust1.26.0で128ビットの整数基本型が追加されました: u128 : 範囲[0, 2^128 - 1]の128ビットの非負整数 i128 : 範囲[-(2^127), 2^127 - 1]の128ビットの符号付き整数 これらの基本型は、LLVMサポート経由で効率的に実装されています。ネイティブに128ビット整数をサポートしないプラットフォームですら利用可能で、 他の整数型のように使用できます。 これらの基本型は、特定の暗号化アルゴリズムなど、非常に大きな整数を効率的に使用する必要のあるアルゴリズムで、とても有用です。","breadcrumbs":"付録 » 128ビット整数","id":"appendix-06-newest-features.html#a128ビット整数","title":"128ビット整数"},"appendix-06-newest-features.html#aフィールド初期化省略":{"body":"fieldname を fieldname: fieldname の省略として記述することでデータ構造(構造体、enum、ユニオン)を名前付きのフィールドで、 初期化することができます。これにより、重複を減らし、コンパクトな記法の初期化が許容されます。 #[derive(Debug)]\nstruct Person { name: String, age: u8,\n} fn main() { // ピーター let name = String::from(\"Peter\"); let age = 27; // フル記法: // Using full syntax: let peter = Person { name: name, age: age }; // ポーティア let name = String::from(\"Portia\"); let age = 27; // フィールド初期化省略: // Using field init shorthand: let portia = Person { name, age }; println!(\"{:?}\", portia);\n}","breadcrumbs":"付録 » フィールド初期化省略","id":"appendix-06-newest-features.html#aフィールド初期化省略","title":"フィールド初期化省略"},"appendix-06-newest-features.html#aループから戻る":{"body":"loop の1つの使用法は、スレッドが仕事を終えたか確認するなど、失敗する可能性のあることを知っている処理を再試行することです。 ですが、その処理の結果を残りのコードに渡す必要がある可能性があります。それをループを停止させるために使用する break 式に追加したら、 breakしたループから返ってきます。 fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; assert_eq!(result, 20);\n}","breadcrumbs":"付録 » ループから戻る","id":"appendix-06-newest-features.html#aループから戻る","title":"ループから戻る"},"appendix-06-newest-features.html#a付録f-最新の機能":{"body":"この付録は、本の主な部分が完成してから安定版Rustに追加された機能をドキュメント化しています。","breadcrumbs":"付録 » 付録F: 最新の機能","id":"appendix-06-newest-features.html#a付録f-最新の機能","title":"付録F: 最新の機能"},"appendix-06-newest-features.html#a境界を含む範囲":{"body":"以前は、範囲を式として使用する際、 .. でなければならず、これは上限を含まない一方、パターンは ... を使用しなければならず、 これは、上限を含みます。現在では、 ..= が式と範囲の文脈両方で上限を含む範囲の記法として受け付けられます。 fn main() { for i in 0 ..= 10 { match i { 0 ..= 5 => println!(\"{}: low\", i), 6 ..= 10 => println!(\"{}: high\", i), _ => println!(\"{}: out of range\", i), } }\n} ... 記法はそれでも、matchでは受け付けられますが、式では受け付けられません。 ..= を使用すべきです。","breadcrumbs":"付録 » 境界を含む範囲","id":"appendix-06-newest-features.html#a境界を含む範囲","title":"境界を含む範囲"},"appendix-06-newest-features.html#use-宣言のネストされたグループ":{"body":"多くの異なるサブモジュールがある複雑なモジュール木があり、それぞれからいくつかの要素をインポートする必要があるなら、 同じ宣言の全インポートをグループ化し、コードを綺麗に保ち、ベースモジュールの名前を繰り返すのを回避するのが有用になる可能性があります。 use 宣言は、単純なインポートとグロブを使用したもの両方に対して、そのような場合に手助けになるネストをサポートしています。 例を挙げれば、このコード片は、 bar 、 Foo 、 baz の全要素、 Bar をインポートします。 # #![allow(unused_imports, dead_code)]\n#\n# mod foo {\n# pub mod bar {\n# pub type Foo = ();\n# }\n# pub mod baz {\n# pub mod quux {\n# pub type Bar = ();\n# }\n# }\n# }\n#\nuse foo::{ bar::{self, Foo}, baz::{*, quux::Bar},\n};\n#\n# fn main() {}","breadcrumbs":"付録 » use 宣言のネストされたグループ","id":"appendix-06-newest-features.html#use-宣言のネストされたグループ","title":"use 宣言のネストされたグループ"},"appendix-07-nightly-rust.html#aシュポシュポリリースチャンネルと列車に乗ること":{"body":"Rust開発は、 電車のダイヤ に合わせて処理されます。つまり、全開発はRustリポジトリの master ブランチで行われます。 リリースはソフトウェアのリリーストレインモデル(software release train model)に従い、これはCisco IOSや他のソフトウェアプロジェクトで活用されています。 Rustには リリースチャンネル が3つあります: 注釈: software release train modelとは、あるバージョンのソフトウェアリリースの順番を列車に見立て、 列車のダイヤのように、決まった間隔でリリースに持って行く手法のことの模様。一つの列車は、Rustの場合、 ナイトリー、ベータ、安定版の順に「駅」に停車していくものと思われる。 ナイトリー ベータ 安定版 多くのRust開発者は主に安定版チャンネルを使用しますが、新しい実験的な機能を試したい方は、 ナイトリーやベータを使用するかもしれません。 こちらが、開発とリリースプロセスの動き方の例です: RustチームがRust1.5のリリースに取り掛かっていると想定しましょう。 そのリリースは、2015年の11月に発生しましたが、現実的なバージョンナンバーを与えてくれるでしょう。 新しい機能がRustに追加されます: 新しいコミットが master ブランチに着地します。毎晩、新しいナイトリ版のRustが生成されます。 毎日がリリース日で、これらのリリースは、リリースインフラにより自動で作成されます。故に、 時間が経てばリリースは、毎晩1回、以下のような見た目になります: nightly: * - - * - - * 6週間ごとに、新しいリリースを準備するタイミングになります!Rustリポジトリの beta ブランチが、 ナイトリで使用される master ブランチから枝分かれします。さて、リリースが二つになりました: nightly: * - - * - - * |\nbeta: * ほとんどのRustユーザはベータリリースを積極的には使用しませんが、自身のCIシステム内でベータに対してテストを行い、 Rustが不具合の可能性を発見するのを手伝います。その間も、やはりナイトリリリースは毎晩あります: 注釈: CIはContinuous Integration(継続統合といったところか)のことと思われる。開発者のコードを1日に何度も、 メインのブランチに統合することらしい。 nightly: * - - * - - * - - * - - * |\nbeta: * 不具合が見つかったとしましょう。よいことに、不具合が安定版のリリースにこっそり持ち込まれる前にベータリリースをテストする時間がありました! 修正が master に適用されるので、ナイトリは修正され、それから修正が beta ブランチにバックポートされ、 ベータの新しいリリースが生成されます: nightly: * - - * - - * - - * - - * - - * |\nbeta: * - - - - - - - - * 最初のベータが作成されてから6週間後、安定版のリリースの時間です! stable ブランチが beta ブランチから生成されます: nightly: * - - * - - * - - * - - * - - * - * - * |\nbeta: * - - - - - - - - * |\nstable: * やりました!Rust1.5が完了しました!ですが、1つ忘れていることがあります: 6週間が経過したので、 次 のバージョンのRust(1.6)の新しいベータも必要です。従って、 stable が beta から枝分かれした後に、 次のバージョンの beta が nightly から再度枝分かれします: nightly: * - - * - - * - - * - - * - - * - * - * | |\nbeta: * - - - - - - - - * * |\nstable: * これが「トレイン・モデル」と呼ばれます。6週間ごとにリリースが「駅を出発する」からですが、 安定版リリースとして到着する前にベータチャンネルの旅をそれでもしなければなりません。 Rustは6週間ごとに時計仕掛けのようにリリースされます。あるRustリリースの日付を知っていれば、 次のリリースの日付もわかります: 6週間後です。6週間ごとにリリースを組むことのいい側面は、次の列車がすぐにやってくることです。 ある機能が偶然、特定のリリースを逃しても、心配する必要はありません: 別のリリースがすぐに起きます! これにより、リリースの締め切りが近い洗練されていない可能性のある機能をこっそり持ち込むプレッシャーが減る助けになるのです。 このプロセスのおかげで、Rustの次のビルドを常に確認し、アップグレードするのが容易であると自身に対して確かめることができます: ベータリリースが予想した通りに動かなければ、チームに報告して、次の安定版のリリースが起きる前に直してもらうことができるのです! ベータリリースでの破損はどちらかといえば稀ですが、 rustc もソフトウェアの一種であり、バグは確実に存在します。","breadcrumbs":"付録 » シュポシュポ!リリースチャンネルと列車に乗ること","id":"appendix-07-nightly-rust.html#aシュポシュポリリースチャンネルと列車に乗ること","title":"シュポシュポ!リリースチャンネルと列車に乗ること"},"appendix-07-nightly-rust.html#a付録g-rustの作られ方とnightly-rust":{"body":"この付録は、Rustのでき方と、それがRust開発者としてあなたにどう影響するかについてです。 この本の出力は安定版Rust 1.21.0で生成されていますが、コンパイルできるいかなる例も、 それより新しいRustのどんな安定版でもコンパイルでき続けられるはずということに触れました。 この節は、これが本当のことであると保証する方法を説明します!","breadcrumbs":"付録 » 付録G: Rustの作られ方と“Nightly Rust”","id":"appendix-07-nightly-rust.html#a付録g-rustの作られ方とnightly-rust","title":"付録G: Rustの作られ方と“Nightly Rust”"},"appendix-07-nightly-rust.html#a停滞なしの安定性":{"body":"言語として、Rustはコードの安定性について 大い に注意しています。Rustには、その上に建築できる岩のように硬い基礎であってほしく、 物事が定期的に変わっていたら、それは実現できません。同時に新しい機能で実験できなければ、もはや何も変更できないリリースの時まで、 重大な瑕疵(かし)を発見できなくなるかもしれません。 この問題に対する我々の解決策は「停滞なしの安定性」と呼ばれるもので、ガイドの原則は以下の通りです: 安定版Rustの新しいバージョンにアップグレードするのを恐れる必要は何もないはずです。各アップグレートは痛みのないもののはずですが、 新しい機能、より少ないバグ、高速なコンパイル時間も齎すべきです。","breadcrumbs":"付録 » 停滞なしの安定性","id":"appendix-07-nightly-rust.html#a停滞なしの安定性","title":"停滞なしの安定性"},"appendix-07-nightly-rust.html#a安定しない機能":{"body":"このリリースモデルにはもう一つ掴み所があります: 安定しない機能です。Rustは「機能フラグ」と呼ばれるテクニックを使用して、 あるリリースで有効にする機能を決定します。新しい機能が活発に開発中なら、 master に着地し、 故にナイトリーでは 機能フラグ の背後に存在します。ユーザとして、絶賛作業中の機能を試したいとお望みならば、 可能ですが、ナイトリリリースのRustを使用し、ソースコードに適切なフラグを注釈して同意しなければなりません。 ベータか安定リリースのRustを使用しているなら、機能フラグは使用できません。これが、永遠に安定であると宣言する前に、 新しい機能を実用に供することができる鍵になっています。最先端を選択するのをお望みの方はそうすることができ、 岩のように硬い経験をお望みの方は、安定版に執着し自分のコードが壊れることはないとわかります。停滞なしの安定性です。 この本は安定な機能についての情報のみ含んでいます。現在進行形の機能は、変化中であり、 確実にこの本が執筆された時と安定版ビルドで有効化された時で異なるからです。ナイトリ限定の機能についてのドキュメンテーションは、 オンラインで発見できます。","breadcrumbs":"付録 » 安定しない機能","id":"appendix-07-nightly-rust.html#a安定しない機能","title":"安定しない機能"},"appendix-07-nightly-rust.html#rfcプロセスとチーム":{"body":"では、これらの新しい機能をどう習うのでしょうか?Rustの開発モデルは、 Request For Comments (RFC; コメントの要求)プロセス に従っています。 Rustに改善を行いたければ、RFCと呼ばれる提案を書き上げます。 誰もがRFCを書いてRustを改善でき、提案はRustチームにより査読され議論され、このチームは多くの話題のサブチームから構成されています。 RustのWebサイト にはチームの完全なリストがあり、 プロジェクトの各分野のチームも含みます: 言語設計、コンパイラ実装、インフラ、ドキュメンテーションなどです。 適切なチームが提案とコメントを読み、自身のコメントを書き、最終的にその機能を受け入れるか拒否するかの同意があります。 機能が受け入れられれば、Rustリポジトリでissueが開かれ、誰かがそれを実装します。うまく実装できる人は、 そもそもその機能を提案した人ではないかもしれません!実装の準備ができたら、 「安定しない機能」節で議論したように、機能ゲートの背後の master に着地します。 時間経過後、一旦ナイトリリリースを使用するRust開発者が新しい機能を試すことができたら、チームのメンバーがその機能と、 ナイトリでどう機能しているかについて議論し、安定版のRustに導入すべきかどうか決定します。 決定が進行させることだったら、機能ゲートは取り除かれ、その機能はもう安定と考えられます! Rustの新しい安定版リリースまで、列車に乗っているのです。","breadcrumbs":"付録 » RFCプロセスとチーム","id":"appendix-07-nightly-rust.html#rfcプロセスとチーム","title":"RFCプロセスとチーム"},"appendix-07-nightly-rust.html#rustupとrustナイトリの役目":{"body":"rustupは、グローバルかプロジェクトごとにRustのリリースチャンネルを変更しやすくしてくれます。 標準では、安定版のRustがインストールされます。例えば、ナイトリをインストールするには: $ rustup install nightly rustup でインストールした全ツールチェーン(Rustのリリースと関連するコンポーネント)も確認できます。 こちらは、著者の一人のWindowsコンピュータの例です: > rustup toolchain list\nstable-x86_64-pc-windows-msvc (default)\nbeta-x86_64-pc-windows-msvc\nnightly-x86_64-pc-windows-msvc おわかりのように、安定版のツールチェーンが標準です。ほとんどのRustユーザは、ほとんどの場合、安定版を使用します。 あなたもほとんどの場合安定版を使用したい可能性がありますが、最前線の機能が気になるので、特定のプロジェクトではナイトリを使用したいかもしれません。 そうするためには、そのプロジェクトのディレクトリで rustup override を使用して、そのディレクトリにいる時に、 rustup が使用するべきツールチェーンとしてナイトリ版のものをセットします。 $ cd ~/projects/needs-nightly\n$ rustup override add nightly これで ~/projects/needs-nightly 内で rustc や cargo を呼び出す度に、 rustup は既定の安定版のRustではなく、 ナイトリRustを使用していることを確かめます。Rustプロジェクトが大量にある時には、重宝します。","breadcrumbs":"付録 » RustupとRustナイトリの役目","id":"appendix-07-nightly-rust.html#rustupとrustナイトリの役目","title":"RustupとRustナイトリの役目"},"ch00-00-introduction.html#aこの本の使い方":{"body":"一般的に、この本は、順番に読み進めていくことを前提にしています。後の章は、前の章の概念の上に成り立ち、 前の章では、ある話題にさほど深入りしない可能性があります; 典型的に後ほどの章で同じ話題を再度しています。 この本には2種類の章があるとわかるでしょう: 概念の章とプロジェクトの章です。概念の章では、 Rustの一面を学ぶでしょう。プロジェクトの章では、それまでに学んだことを適用して一緒に小さなプログラムを構築します。 2、12、20章がプロジェクトの章です。つまり、残りは概念の章です。 第1章はRustのインストール方法、Hello, world!プログラムの書き方、Rustのパッケージマネージャ兼、 ビルドツールのCargoの使用方法を説明します。第2章は、Rust言語への実践的な導入です。概念を高度に講義し、後ほどの章で追加の詳細を提供します。 今すぐRustの世界に飛び込みたいなら、第2章こそがそのためのものです。第3章は他のプログラミング言語の機能に似たRustの機能を講義していますが、 最初その3章すら飛ばして、まっすぐに第4章に向かい、Rustの所有権システムについて学びたくなる可能性があります。 しかしながら、あなたが次に進む前に全ての詳細を学ぶことを好む特別に几帳面な学習者なら、 第2章を飛ばして真っ先に第3章に行き、学んだ詳細を適用するプロジェクトに取り組みたくなった時に第2章に戻りたくなる可能性があります。 第5章は、構造体とメソッドについて議論し、第6章はenum、 match 式、 if let フロー制御構文を講義します。 構造体とenumを使用してRustにおいて独自の型を作成します。 第7章では、Rustのモジュールシステムと自分のコードとその公開されたAPI(Application Programming Interface)を体系化するプライバシー規則について学びます。 第8章では、ベクタ、文字列、ハッシュマップなどの標準ライブラリが提供する一般的なコレクションデータ構造の一部を議論します。 第9章では、Rustのエラー処理哲学とテクニックを探究します。 第10章ではジェネリクス、トレイト、ライフタイムについて深入りし、これらは複数の型に適用されるコードを定義する力をくれます。 第11章は、完全にテストに関してで、Rustの安全性保証があってさえ、プログラムのロジックが正しいことを保証するために、 必要になります。第12章では、ファイル内のテキストを検索する grep コマンドラインツールの一部の機能を自身で構築します。 このために、以前の章で議論した多くの概念を使用します。 第13章はクロージャとイテレータを探究します。これらは、関数型プログラミング言語由来のRustの機能です。 第14章では、Cargoをより詳しく調査し、他人と自分のライブラリを共有する最善の策について語ります。 第15章では、標準ライブラリが提供するスマートポインタとその機能を可能にするトレイトを議論します。 第16章では、並行プログラミングの異なるモデルを見ていき、Rustが恐れなしに複数のスレッドでプログラムする手助けをする方法を語ります。 第17章では、馴染み深い可能性のあるオブジェクト指向プログラミングの原則とRustのイディオムがどう比較されるかに目を向けます。 第18章は、パターンとパターンマッチングのリファレンスであり、これらはRustプログラムを通して、 考えを表現する強力な方法になります。第19章は、unsafe Rustやライフタイム、トレイト、型、関数、クロージャの詳細を含む、 興味のある高度な話題のスモーガスボード( 訳注 : 日本でいうバイキングのこと)を含みます。 第20章では、低レベルなマルチスレッドのWebサーバを実装するプロジェクトを完成させます! 最後に、言語についての有用な情報をよりリファレンスのような形式で含む付録があります。 付録AはRustのキーワードを講義し、付録Bは、Rustの演算子と記号、付録Cは、 標準ライブラリが提供する導出可能なトレイト、付録Dはマクロを講義します。 この本を読む間違った方法なんてありません: 飛ばしたければ、どうぞご自由に! 混乱したら、前の章に戻らなければならない可能性もあります。ですが、自分に合った方法でどうぞ。 Rustを学ぶ過程で重要な部分は、コンパイラが表示するエラーメッセージを読む方法を学ぶことです: それは動くコードへと導いてくれます。そのため、各場面でコンパイラが表示するエラーメッセージとともに、 コンパイルできないコードの例を多く提供します。適当に例を選んで走らせたら、コンパイルできないかもしれないことを知ってください! 周りのテキストを読んで実行しようとしている例がエラーになることを意図しているのか確認することを確かめてください。 ほとんどの場合、コンパイルできないあらゆるコードの正しいバージョンへと導きます。","breadcrumbs":"この本の使い方","id":"ch00-00-introduction.html#aこの本の使い方","title":"この本の使い方"},"ch00-00-introduction.html#aこの本は誰のためのものなの":{"body":"この本は、あなたが他のプログラミング言語でコードを書いたことがあることを想定していますが、 具体的にどの言語かという想定はしません。私たちは、幅広い分野のプログラミング背景からの人にとってこの資料を広くアクセスできるようにしようとしてきました。 プログラミングとはなん なのか やそれについて考える方法について多くを語るつもりはありません。 もし、完全なプログラミング初心者であれば、プログラミング入門を特に行う本を読むことでよりよく役に立つでしょう。","breadcrumbs":"この本は誰のためのものなの","id":"ch00-00-introduction.html#aこの本は誰のためのものなの","title":"この本は誰のためのものなの"},"ch00-00-introduction.html#aオープンソース開発者":{"body":"Rustは、Rustプログラミング言語やコミュニティ、開発者ツール、ライブラリを開発したい方向けです。 あなたがRust言語に貢献されることを心よりお待ちしております。","breadcrumbs":"オープンソース開発者","id":"ch00-00-introduction.html#aオープンソース開発者","title":"オープンソース開発者"},"ch00-00-introduction.html#aスピードと安定性に価値を見出す方":{"body":"Rustは、スピードと安定性を言語に渇望する方向けです。ここでいうスピードとは、 Rustで作れるプログラムのスピードとソースコードを書くスピードのことです。Rustコンパイラのチェックにより、 機能の追加とリファクタリングを通して安定性を保証してくれます。これはこのようなチェックがない言語の脆いレガシーコードとは対照的で、 その場合開発者はしばしば、変更するのを恐れてしまいます。ゼロコスト抽象化を志向し、 手で書いたコードと同等の速度を誇る低レベルコードにコンパイルされる高レベル機能により、 Rustは安全なコードを高速なコードにもしようと努力しています。 Rust言語は他の多くのユーザのサポートも望んでいます; ここで名前を出した方は、 ただの最大の出資者の一部です。総合すると、Rustの最大の野望は、プログラマが数十年間受け入れてきた代償を排除することです: つまり、安全性 と 生産性、スピード と エルゴノミクスです。Rustを試してみて、その選択が自分に合っているか確かめてください。","breadcrumbs":"スピードと安定性に価値を見出す方","id":"ch00-00-introduction.html#aスピードと安定性に価値を見出す方","title":"スピードと安定性に価値を見出す方"},"ch00-00-introduction.html#aソースコード":{"body":"この本が生成されるソースファイルは、 GitHub で見つかります。 訳注: 日本語版は こちら です。","breadcrumbs":"ソースコード","id":"ch00-00-introduction.html#aソースコード","title":"ソースコード"},"ch00-00-introduction.html#a企業":{"body":"数百の企業が、大企業、中小企業を問わず、様々なタスクにプロダクションでRustを使用しています。 そのタスクには、コマンドラインツール、Webサービス、DevOpsツール、組み込みデバイス、 オーディオとビデオの解析および変換、暗号通貨、生物情報学、サーチエンジン、IoTアプリケーション、 機械学習、Firefoxウェブブラウザの主要部分さえ含まれます。","breadcrumbs":"企業","id":"ch00-00-introduction.html#a企業","title":"企業"},"ch00-00-introduction.html#a学生":{"body":"Rustは、学生やシステムの概念を学ぶことに興味のある方向けです。Rustを使用して、 多くの人がOS開発などの話題を学んできました。コミュニティはとても暖かく、喜んで学生の質問に答えてくれます。 この本のような努力を通じて、Rustチームはシステムの概念を多くの人、特にプログラミング初心者にとってアクセス可能にしたいと考えています。","breadcrumbs":"学生","id":"ch00-00-introduction.html#a学生","title":"学生"},"ch00-00-introduction.html#a導入":{"body":"注釈: この本のこの版は、本として利用可能な The Rust Programming Language と、 No Starch Press のebook形式と同じです。 The Rust Programming Language へようこそ。Rustに関する入門書です。 Rustプログラミング言語は、高速で信頼できるソフトウェアを書く手助けをしてくれます。 高レベルのエルゴノミクス( 訳注 : ergonomicsとは、人間工学的という意味。砕いて言えば、人間に優しいということ)と低レベルの制御は、 しばしばプログラミング言語の設計においてトレードオフの関係になります; Rustは、その衝突に挑戦しています。バランスのとれた強力な技術の許容量と素晴らしい開発者経験を通して、 Rustは伝統的にそれらの制御と紐付いていた困難全てなしに低レベルの詳細(メモリ使用など)を制御する選択肢を与えてくれます。","breadcrumbs":"導入","id":"ch00-00-introduction.html#a導入","title":"導入"},"ch00-00-introduction.html#a開発者チーム":{"body":"Rustは、いろんなレベルのシステムプログラミングの知識を持つ開発者の巨大なチームとコラボするのに生産的なツールであると証明してきています。 低レベルコードは様々な種類の微細なバグを抱える傾向があり、そのようなバグは他の言語だと広範なテストと、 経験豊富な開発者による注意深いコードレビューによってのみ捕捉されるものです。Rustにおいては、 コンパイラが並行性のバグも含めたこのようなとらえどころのないバグのあるコードをコンパイルするのを拒むことで、 門番の役割を担います。コンパイラとともに取り組むことで、チームはバグを追いかけるよりもプログラムのロジックに集中することに、 時間を費やせるのです。 Rustはまた、現代的な開発ツールをシステムプログラミング世界に導入します。 Cargoは、付属の依存マネージャ兼ビルドツールで、依存を追加、コンパイル、管理することを楽かつ、 Rustエコシステムを通じて矛盾させません。 Rustfmtは開発者の間で矛盾のないコーディングスタイルを保証します。 Rust Language ServerはIDE(Intefrated Development Environment)にコード補完とインラインのエラーメッセージの統合の源となります。 これらや他のツールをRustのエコシステムで使用することで、開発者はシステムレベルのコードを記述しつつ、 生産的になれます。","breadcrumbs":"開発者チーム","id":"ch00-00-introduction.html#a開発者チーム","title":"開発者チーム"},"ch00-00-introduction.html#rustは誰のためのものなの":{"body":"Rustは、様々な理由により多くの人にとって理想的です。いくつか最も重要なグループを見ていきましょう。","breadcrumbs":"Rustは誰のためのものなの","id":"ch00-00-introduction.html#rustは誰のためのものなの","title":"Rustは誰のためのものなの"},"ch01-00-getting-started.html#a事始め":{"body":"Rustの旅を始めましょう!学ぶべきことはたくさんありますが、いかなる旅もどこかから始まります。 この章では、以下のことを議論します: RustをLinux、macOS、Windowsにインストールする Hello, world! と出力するプログラムを書く cargo というRustのパッケージマネージャ兼ビルドシステムを使用する","breadcrumbs":"事始め","id":"ch01-00-getting-started.html#a事始め","title":"事始め"},"ch01-01-installation.html#aインストール":{"body":"最初の手順は、Rustをインストールすることです。Rustは、 rustup というRustのバージョンと関連するツールを管理するコマンドラインツールを使用して、 ダウンロードします。ダウンロードするには、インターネット接続が必要でしょう。 注釈: なんらかの理由で rustup を使用しないことを好むのなら、 Rustインストールページ で、 他の選択肢をご覧になってください。 以下の手順で最新の安定版のRustコンパイラをインストールします。この本の例と出力は全て、安定版のRust1.21.0を使用しています。 Rustの安定性保証により、現在この本の例でコンパイルできるものは、新しいバージョンになってもコンパイルでき続けることを保証します。 出力は、バージョンによって多少異なる可能性があります。Rustは頻繁にエラーメッセージと警告を改善しているからです。 言い換えると、どんな新しいバージョンでもこの手順に従ってインストールした安定版なら、 この本の内容で想定通りに動くはずです。","breadcrumbs":"事始め » インストール","id":"ch01-01-installation.html#aインストール","title":"インストール"},"ch01-01-installation.html#aコマンドライン表記":{"body":"この章及び、本を通して、端末で使用するなんらかのコマンドを示すことがあります。読者が入力するべき行は、 全て $ で始まります。 $ 文字を入れる必要はありません; 各コマンドの開始を示しているだけです。 $ で始まらない行は、典型的には直前のコマンドの出力を示します。また、PowerShell限定の例は、 $ ではなく、 > を使用します。","breadcrumbs":"事始め » コマンドライン表記","id":"ch01-01-installation.html#aコマンドライン表記","title":"コマンドライン表記"},"ch01-01-installation.html#aトラブルシューティング":{"body":"Rustが正常にインストールされているか確かめるには、シェルを開いて以下の行を入力してください: $ rustc --version バージョンナンバー、コミットハッシュ、最新の安定版がリリースされたコミット日時が以下のフォーマットで表示されるのを目撃するはずです。 rustc x.y.z (abcabcabc yyyy-mm-dd) この情報が見れたら、Rustのインストールに成功しました!この情報が出ず、Windowsを使っているなら、 Rustが %PATH% システム環境変数にあることを確認してください。全て正常で、それでもRustが動かないなら、 助力を得られる場所はたくさんあります。最も簡単なのが irc.mozilla.orgの#rust IRCチャンネル で、 Mibbit を通してアクセスできます。そのアドレスで、助けてくれる他のRustacean(自分たちを呼ぶバカなニックネーム)とチャットできます。 他の素晴らしいリソースには、 ユーザ・フォーラム と Stack Overflow が含まれます。 Rustacean: いらないかもしれない補足です。Rustaceanは公式にcrustaceans(甲殻類)から 来て いるそうです。 そのため、Rustのマスコットは非公式らしいですが、 カニ 。上の会話でCの欠点を削ぎ落としているからcを省いてるの?みたいなことを聞いていますが、 違うそうです。検索したら、堅牢性が高いから甲殻類という意見もありますが、真偽は不明です。 明日使えるかもしれないトリビアでした。","breadcrumbs":"事始め » トラブルシューティング","id":"ch01-01-installation.html#aトラブルシューティング","title":"トラブルシューティング"},"ch01-01-installation.html#aローカルのドキュメンテーション":{"body":"インストーラは、ドキュメンテーションの複製もローカルに含んでいるので、オフラインで閲覧することができます。 ブラウザでローカルのドキュメンテーションを開くには、 rustup doc を実行してください。 標準ライブラリにより型や関数が提供され、それがなんなのかや使用方法に確信が持てない度に、APIドキュメンテーションを使用して探してください!","breadcrumbs":"事始め » ローカルのドキュメンテーション","id":"ch01-01-installation.html#aローカルのドキュメンテーション","title":"ローカルのドキュメンテーション"},"ch01-01-installation.html#a更新及びアンインストール":{"body":"rustup 経由でRustをインストールしたら、最新版への更新は、簡単になります。シェルから、 以下の更新スクリプトを実行してください: $ rustup update Rustと rustup をアンインストールするには、シェルから以下のアンインストールスクリプトを実行してください: $ rustup self uninstall","breadcrumbs":"事始め » 更新及びアンインストール","id":"ch01-01-installation.html#a更新及びアンインストール","title":"更新及びアンインストール"},"ch01-01-installation.html#linuxとmacosに-rustup-をインストールする":{"body":"LinuxかmacOSを使用しているなら、端末を開き、以下のコマンドを入力してください: $ curl https://sh.rustup.rs -sSf | sh このコマンドはスクリプトをダウンロードし、 rustup ツールのインストールを開始し、Rustの最新の安定版をインストールします。 パスワードを求められる可能性があります。インストールがうまく行けば、以下の行が出現するでしょう: Rust is installed now. Great! お好みでご自由にスクリプトをダウンロードし、実行前に調査することもできます。 インストールスクリプトは、次回のログイン後にRustをシステムのPATHに自動的に追加します。端末を再起動するのではなく、 いますぐにRustを使用し始めたいのなら、シェルで以下のコマンドを実行してRustをシステムのPATHに手動で追加します: $ source $HOME/.cargo/env また、以下の行を ~/.bash_profile に追加することもできます: $ export PATH=\"$HOME/.cargo/bin:$PATH\" さらに、なんらかの類のリンカが必要になるでしょう。既にインストールされている可能性が高いものの、 Rustプログラムのコンパイルを試みて、リンカが実行できないというエラーが出たら、 システムにリンカがインストールされていないということなので、手動でインストールする必要があるでしょう。 Cコンパイラは通常正しいリンカとセットになっています。 自分のプラットフォームのドキュメンテーションを見てCコンパイラのインストール方法を確認してください。 一般的なRustパッケージの中には、Cコードに依存し、Cコンパイラが必要になるものもあります。 故に今インストールする価値はあるかもしれません。","breadcrumbs":"事始め » LinuxとmacOSに rustup をインストールする","id":"ch01-01-installation.html#linuxとmacosに-rustup-をインストールする","title":"LinuxとmacOSに rustup をインストールする"},"ch01-01-installation.html#windowsで-rustup-をインストールする":{"body":"Windowsでは、 https://www.rust-lang.org/install.html に行き、手順に従ってRustをインストールしてください。 インストールの途中で、Visual Studio2013以降用のC++ビルドツールも必要になるという旨のメッセージが出るでしょう。 ビルドツールを取得する最も簡単な方法は、 Visual Studio 2017用のビルドツール をインストールすることです。 ツールは、他のツール及びフレームワークのセクションにあります。 これ以降、 cmd.exe とPowerShellの両方で動くコマンドを使用します。 特定の違いがあったら、どちらを使用すべきか説明します。","breadcrumbs":"事始め » Windowsで rustup をインストールする","id":"ch01-01-installation.html#windowsで-rustup-をインストールする","title":"Windowsで rustup をインストールする"},"ch01-02-hello-world.html#aコンパイルと実行は個別のステップ":{"body":"新しく作成したプログラムをちょうど実行したので、その途中の手順を調査しましょう。 Rustプログラムを実行する前に、以下のように、 rustc コマンドを入力し、ソースファイルの名前を渡すことで、 Rustコンパイラを使用してコンパイルしなければなりません。 $ rustc main.rs あなたにCやC++の背景があるなら、これは gcc や clang と似ていると気付くでしょう。コンパイルに成功後、 Rustはバイナリの実行可能ファイルを出力します。 Linux、macOS、WindowsのPowerShellなら、シェルで以下のように ls コマンドを入力することで実行可能ファイルを見られます: $ ls\nmain main.rs WindowsのCMDなら、以下のように入力するでしょう: > dir /B %= the /B option says to only show the file names =% %= /Bオプションは、ファイル名だけを表示することを宣言する =%\nmain.exe\nmain.pdb\nmain.rs これは、 .rs 拡張子のソースコードファイル、実行可能ファイル(Windowsなら main.exe 、他のプラットフォームでは、 main )、 そして、CMDを使用しているなら、 .pdb 拡張子のデバッグ情報を含むファイルを表示します。ここから、 main か main.exe を走らせます。このように: $ ./main # or .\\main.exe on Windows # または、Widnowsなら.\\main.exe main.rs がHello, world!プログラムなら、この行は Hello, world! と端末に出力するでしょう。 RubyやPython、JavaScriptなどの動的言語により造詣が深いなら、プログラムのコンパイルと実行を個別の手順で行うことに慣れていない可能性があります。 Rustは AOTコンパイル (ahead-of-time; 訳注 : 予め)言語です。つまり、プログラムをコンパイルし、 実行可能ファイルを誰かにあげ、あげた人がRustをインストールしていなくても実行できるわけです。 誰かに .rb 、 .py 、 .js ファイルをあげたら、それぞれRuby、Python、JavaScriptの実装がインストールされている必要があります。 ですが、そのような言語では、プログラムをコンパイルし実行するには、1コマンドしか必要ないのです。 全ては言語設計においてトレードオフなのです。 簡単なプログラムなら rustc でコンパイルするだけでも十分ですが、プロジェクトが肥大化してくると、 オプションを全て管理し、自分のコードを簡単に共有したくなるでしょう。次は、Cargoツールを紹介します。 これは、現実世界のRustプログラムを書く手助けをしてくれるでしょう。","breadcrumbs":"事始め » コンパイルと実行は個別のステップ","id":"ch01-02-hello-world.html#aコンパイルと実行は個別のステップ","title":"コンパイルと実行は個別のステップ"},"ch01-02-hello-world.html#aプロジェクトのディレクトリを作成する":{"body":"Rustコードを格納するディレクトリを作ることから始めましょう。Rustにとって、コードがどこにあるかは問題ではありませんが、 この本の練習とプロジェクトのために、ホームディレクトリに projects ディレクトリを作成してプロジェクトを全てそこに保管することを推奨します。 端末を開いて以下のコマンドを入力し、 projects ディレクトリと、 projects ディレクトリ内にHello, world!プロジェクトのディレクトリを作成してください。 LinuxとmacOSなら、こう入力してください: $ mkdir ~/projects\n$ cd ~/projects\n$ mkdir hello_world\n$ cd hello_world Windowsのcmdなら、こう: > mkdir \"%USERPROFILE%\\projects\"\n> cd /d \"%USERPROFILE%\\projects\"\n> mkdir hello_world\n> cd hello_world WindowsのPowerShellなら、こう: > mkdir $env:USERPROFILE\\projects\n> cd $env:USERPROFILE\\projects\n> mkdir hello_world\n> cd hello_world","breadcrumbs":"事始め » プロジェクトのディレクトリを作成する","id":"ch01-02-hello-world.html#aプロジェクトのディレクトリを作成する","title":"プロジェクトのディレクトリを作成する"},"ch01-02-hello-world.html#hello-world":{"body":"Rustをインストールしたので、最初のRustプログラムを書きましょう。新しい言語を学ぶ際に、 Hello, world! というテキストを画面に出力する小さなプログラムを書くことは伝統的なことなので、 ここでも同じようにしましょう! 注釈: この本は、コマンドラインに基礎的な馴染みがあることを前提にしています。Rustは、編集やツール、 どこにコードがあるかについて特定の要求をしないので、コマンドラインではなくIDEを使用することを好むのなら、 どうぞご自由にお気に入りのIDEを使用してください。今では、多くのIDEがなんらかの形でRustをサポートしています; 詳しくは、IDEのドキュメンテーションをご覧ください。最近、Rustチームは優れたIDEサポートを有効にすることに注力し、 その前線で急激に成果があがっています!","breadcrumbs":"事始め » Hello, World!","id":"ch01-02-hello-world.html#hello-world","title":"Hello, World!"},"ch01-02-hello-world.html#rustプログラムの解剖":{"body":"Hello, world!プログラムでちょうど何が起こったのか詳しく確認しましょう。 こちらがパズルの最初のピースです: fn main() { } これらの行でRustで関数を定義しています。 main 関数は特別です: 常に全ての実行可能なRustプログラムで走る最初のコードになります。 1行目は、引数がなく、何も返さない main という関数を宣言しています。引数があるなら、かっこ( () )の内部に入ります。 また、関数の本体が波括弧( {} )に包まれていることにも注目してください。Rustでは、全ての関数本体の周りにこれらが必要になります。 スペースを1つあけて、開き波括弧を関数宣言と同じ行に配置するのがいいスタイルです。 これを執筆している時点では、 rustfmt と呼ばれる自動整形ツールは開発中です。複数のRustプロジェクトに渡って、 標準的なスタイルに固執したいなら、 rustfmt は特定のスタイルにコードを整形してくれます。Rustチームは、 最終的に rustc のように標準的なRustの配布にこのツールを含むことを計画しています。従って、この本を読んだ時期によっては、 既にコンピュータにインストールされている可能性もあります!詳細は、オンラインのドキュメンテーションを確認してください。 main 関数内には、こんなコードがあります: println!(\"Hello, world!\"); この行が、この小さなプログラムの全作業をしています: テキストを画面に出力するのです。 ここで気付くべき重要な詳細が4つあります。まず、Rustのスタイルは、タブではなく、4スペースでインデントするということです。 2番目に println! はRustのマクロを呼び出すということです。代わりに関数を呼んでいたら、 println ( ! なし)と入力されているでしょう。Rustのマクロについて詳しくは、付録Dで議論します。 とりあえず、 ! を使用すると、普通の関数ではなくマクロを呼んでいるのだということを知っておくだけでいいでしょう。 3番目に、 \"Hello, world!\" 文字列が見えます。この文字列を引数として println! に渡し、 この文字列が画面に表示されているのです。 4番目にこの行をセミコロン( ; )で終え、この式が終わり、次の式の準備ができていると示唆していることです。 Rustコードのほとんどの行は、セミコロンで終わります。","breadcrumbs":"事始め » Rustプログラムの解剖","id":"ch01-02-hello-world.html#rustプログラムの解剖","title":"Rustプログラムの解剖"},"ch01-02-hello-world.html#rustプログラムを書いて走らせる":{"body":"次にソースファイルを作り、 main.rs と呼んでください。Rustのファイルは常に .rs という拡張子で終わります。 ファイル名に2単語以上使っているなら、アンダースコアで区切ってください。例えば、 helloworld.rs ではなく、 hello_world.rs を使用してください。 さて、作ったばかりの main.rs ファイルを開き、リスト1-1のコードを入力してください。 ファイル名: main.rs fn main() { // 世界よ、こんにちは println!(\"Hello, world!\");\n} リスト1-1: Hello, world! と出力するプログラム ファイルを保存し、端末ウィンドウに戻ってください。LinuxかmacOSなら、以下のコマンドを打ってファイルをコンパイルし、 実行してください: $ rustc main.rs\n$ ./main\nHello, world! Windowsなら、 ./main の代わりに .\\main.exe と打ちます: > rustc main.rs\n> .\\main.exe\nHello, world! OSに関わらず、 Hello, world! という文字列が端末に出力されるはずです。この出力が見れないなら、 「トラブルシューティング」節に立ち戻って、助けを得る方法を参照してください。 Hello, world! が確かに出力されたら、おめでとうございます!正式にRustプログラムを書きました。 Rustプログラマになったのです!ようこそ!","breadcrumbs":"事始め » Rustプログラムを書いて走らせる","id":"ch01-02-hello-world.html#rustプログラムを書いて走らせる","title":"Rustプログラムを書いて走らせる"},"ch01-03-hello-cargo.html#aまとめ":{"body":"既にRustの旅の素晴らしいスタートを切っています!この章では、以下の方法を学びました: rustup で最新の安定版のRustをインストールする方法 新しいRustのバージョンに更新する方法 ローカルにインストールされたドキュメンテーションを開く方法 直接 rustc を使用してHello, world!プログラムを書き、実行する方法 Cargoの慣習を使用して新しいプロジェクトを作成し、実行する方法 より中身のあるプログラムをビルドし、Rustコードの読み書きに慣れるいいタイミングです。故に、第2章では、 数当てゲームを構築します。むしろ一般的なプログラミングの概念がRustでどう動くのか学ぶことから始めたいのであれば、 第3章を見て、それから第2章に戻ってください。","breadcrumbs":"事始め » まとめ","id":"ch01-03-hello-cargo.html#aまとめ","title":"まとめ"},"ch01-03-hello-cargo.html#aリリースビルドを行う":{"body":"プロジェクトを最終的にリリースする準備ができたら、 cargo build --release を使用して、 最適化を行なってコンパイルすることができます。このコマンドは、 target/debug ではなく、 target/release に実行可能ファイルを作成します。最適化は、Rustコードの実行を速くしてくれますが、 オンにするとプログラムをコンパイルする時間が延びます。このため、2つの異なるプロファイルがあるのです: 頻繁に再ビルドをかけたい開発用と、繰り返し再ビルドすることはなく、できるだけ高速に動いてユーザにあげる最終的なプログラムをビルドする用です。 コードの実行時間をベンチマークするなら、 cargo build --release を確実に実行し、 target/release の実行可能ファイルでベンチマークしてください。","breadcrumbs":"事始め » リリースビルドを行う","id":"ch01-03-hello-cargo.html#aリリースビルドを行う","title":"リリースビルドを行う"},"ch01-03-hello-cargo.html#a習慣としてのcargo":{"body":"単純なプロジェクトでは、Cargoは単に rustc を使用する以上の価値を生みませんが、プログラムが複雑になるにつれて、 その価値を証明するでしょう。複数のクレートからなる複雑なプロジェクトでは、Cargoにビルドを調整してもらうのが遥かに簡単です。 hello_cargo プロジェクトは単純ではありますが、今では、Rustのキャリアを通じて使用するであろう本物のツールを多く使用するようになりました。 事実、既存のどんなプロジェクトに取り組むにも、以下のコマンドを使用して、Gitでコードをチェックアウトし、 そのプロジェクトのディレクトリに移動し、ビルドできます: $ git clone someurl.com/someproject\n$ cd someproject\n$ cargo build Cargoについてより詳しく知るには、 ドキュメンテーション を確認してください。","breadcrumbs":"事始め » 習慣としてのCargo","id":"ch01-03-hello-cargo.html#a習慣としてのcargo","title":"習慣としてのCargo"},"ch01-03-hello-cargo.html#cargoでプロジェクトを作成する":{"body":"Cargoを使用して新しいプロジェクトを作成し、元のHello, world!プロジェクトとどう違うかを見ましょう。 projects ディレクトリ(あるいはコードを格納すると決めた場所)に戻ってください。それから、 OSに関わらず、以下を実行してください: $ cargo new hello_cargo --bin\n$ cd hello_cargo 最初のコマンドは、 hello_cargo という新しいバイナリの実行可能ファイルを作成します。 cargo new に渡した --bin 引数が、 ライブラリとは対照的に実行可能なアプリケーション(よく単に バイナリ と呼ばれる)を作成します。プロジェクトを hello_cargo と名付け、 Cargoは、そのファイルを同名のディレクトリに作成します。 hello_cargo ディレクトリに行き、ファイルを列挙してください。Cargoが2つのファイルと1つのディレクトリを生成してくれたことがわかるでしょう: Cargo.toml ファイルと、中に main.rs ファイルがある src ディレクトリです。また、 .gitignore ファイルと共に、新しいGitリポジトリも初期化しています。 注釈: Gitは一般的なバージョンコントロールシステムです。 cargo new を変更して、異なるバージョンコントロールシステムを使用したり、 --vcs フラグを使用して何もバージョンコントロールシステムを使用しないようにもできます。 cargo new --help を走らせて、利用可能なオプションを確認してください。 お好きなテキストエディタで Cargo.toml を開いてください。リスト1-2のコードのような見た目のはずです。 ファイル名: Cargo.toml [package]\nname = \"hello_cargo\"\nversion = \"0.1.0\"\nauthors = [\"Your Name \"] [dependencies] リスト1-2: cargo new で生成される Cargo.toml の中身 このファイルは TOML ( Tom's Obvious, Minimal Language ; 直訳 : トムの明確な最小限の言語)フォーマットで、 Cargoの設定フォーマットです。 最初の行の [package] は、後の文がパッケージを設定していることを示すセクションヘッダーです。もっと情報を追加するにつれて、 別のセクションも追加するでしょう。 その後の3行が、Cargoがプログラムをコンパイルするのに必要な設定情報をセットします: 名前、バージョン、誰が書いたかです。 Cargoは名前とEメールの情報を環境から取得するので、その情報が正しくなければ、 今修正してそれから保存してください。 最後の行の [dependencies] は、プロジェクトの依存を列挙するためのセクションの始まりです。 Rustでは、パッケージのコードは クレート として参照されます。このプロジェクトでは何も他のクレートは必要ありませんが、 第2章の最初のプロジェクトでは必要なので、その時にはこの依存セクションを使用するでしょう。 では、 src/main.rs を開いて覗いてみてください: ファイル名: src/main.rs fn main() { println!(\"Hello, world!\");\n} ちょうどリスト1-1で書いたように、CargoはHello, world!プログラムを生成してくれています。ここまでで、 前のプロジェクトとCargoが生成したプロジェクトの違いは、Cargoが src ディレクトリにコードを配置し、 最上位のディレクトリに Cargo.toml 設定ファイルがあることです。 Cargoは、ソースファイルが src ディレクトリにあることを期待します。プロジェクトの最上位のディレクトリは、 READMEファイル、ライセンス情報、設定ファイル、あるいは、他のコードに関連しないもののためのものです。 Cargoを使用すると、プロジェクトを体系化する手助けをしてくれます。適材適所であり、 全てがその場所にあるのです。 Hello, world!プロジェクトのように、Cargoを使用しないプロジェクトを開始したら、 実際にCargoを使用するプロジェクトに変換することができます。プロジェクトのコードを src ディレクトリに移動し、 適切な Cargo.toml ファイルを作成してください。","breadcrumbs":"事始め » Cargoでプロジェクトを作成する","id":"ch01-03-hello-cargo.html#cargoでプロジェクトを作成する","title":"Cargoでプロジェクトを作成する"},"ch01-03-hello-cargo.html#cargoプロジェクトをビルドし実行する":{"body":"さて、CargoでHello, world!プログラムをビルドし、実行する時の違いに目を向けましょう! hello_cargo ディレクトリから、 以下のコマンドを入力してプロジェクトをビルドしてください: $ cargo build Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo) Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs このコマンドは、カレントディレクトリではなく、 target/debug/hello_cargo (あるいはWindowsなら、 target/debug/hello_cargo.exe )に実行可能ファイルを作成します。以下のコマンドで実行可能ファイルを実行できます: $ ./target/debug/hello_cargo # or .\\target\\debug\\hello_cargo.exe on Windows # あるいは、Windowsなら、.\\target\\debug\\hello_cargo.exe\nHello, world! 全てがうまくいけば、 Hello, world! が端末に出力されるはずです。初めて cargo build を実行すると、 Cargoが最上位に新しいファイルも作成します: Cargo.lock です。このファイルは、自分のプロジェクトの依存の正確なバージョンを追いかけます。 このプロジェクトには依存がないので、ファイルはやや空っぽです。絶対にこのファイルを手動で変更する必要はないでしょう; Cargoが中身を管理してくれるのです。 cargo build でプロジェクトをビルドし、 ./target/debug/hello_cargo で実行したばかりですが、 cargo run を使用して、コードをコンパイルし、それから吐かれた実行可能ファイルを全部1コマンドで実行することもできます: $ cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/hello_cargo`\nHello, world! 今回は、Cargoが hello_cargo をコンパイルしていることを示唆する出力がないことに注目してください。 Cargoはファイルが変更されていないことを推察したので、単純にバイナリを実行したのです。 ソースコードを変更していたら、Cargoは実行前にプロジェクトを再ビルドし、こんな出力を目の当たりにしたでしょう: $ cargo run Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo) Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs Running `target/debug/hello_cargo`\nHello, world! Cargoは cargo check というコマンドも提供しています。このコマンドは、迅速にコードを確認し、 コンパイルできることを確かめますが、実行可能ファイルは生成しません: $ cargo check Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo) Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs 何故、実行可能ファイルが欲しくないのでしょうか?しばしば、 cargo check は、 cargo build よりも遥かに速くなります。 実行可能ファイルを生成する手順を飛ばすからです。コードを書いている際に継続的に自分の作業を確認するのなら、 cargo check を使用すると、その過程が高速化されます!そのため、多くのRustaceanは、 プログラムを書く際にコンパイルできるか確かめるために定期的に cargo check を実行します。 そして、実行可能ファイルを使用できる状態になったら、 cargo build を走らせるのです。 ここまでにCargoについて学んだことをおさらいしましょう: cargo build か cargo check でプロジェクトをビルドできる。 プロジェクトのビルドと実行を1ステップ、 cargo run でできる。 ビルドの結果をコードと同じディレクトリに保存するのではなく、Cargoは target/debug ディレクトリに格納する。 Cargoを使用する追加の利点は、使用しているOSに関わらず、同じコマンドが使用できることです。 故にこの時点で、WindowsとLinux及びmacOSで特定の手順を提供することは最早なくなります。","breadcrumbs":"事始め » Cargoプロジェクトをビルドし、実行する","id":"ch01-03-hello-cargo.html#cargoプロジェクトをビルドし実行する","title":"Cargoプロジェクトをビルドし、実行する"},"ch01-03-hello-cargo.html#hello-cargo":{"body":"Cargoは、Rustのビルドシステム兼、パッケージマネージャです。ほとんどのRustaceanはこのツールを使用して、 Rustプロジェクトの管理をしています。Cargoは、コードのビルドやコードが依存しているライブラリのダウンロード、 それらのライブラリのビルド(コードが必要とするライブラリを我々は、 依存 と呼んでいます)などの多くの仕事を扱ってくれるからです。 今までに書いたような最も単純なRustプログラムは、依存がありません。従って、Hello, world!プロジェクトをCargoを使ってビルドしても、 Cargoのコードをビルドする部分しか使用しないでしょう。より複雑なRustプログラムを書くにつれて、 依存を追加し、Cargoでプロジェクトを開始したら、依存の追加は、遥かに簡単になるのです。 Rustプロジェクトの大多数がCargoを使用しているので、これ以降この本では、あなたもCargoを使用していることを想定します。 Cargoは、「インストール」節で議論した公式のインストーラを使用していれば、勝手にインストールされます。 Rustを他の何らかの手段でインストールした場合、以下のコマンドを端末に入れてCargoがインストールされているか確かめてください: $ cargo --version バージョンナンバーが見えたら、インストールされています! command not found などのエラーが見えたら、 自分のインストール方法をドキュメンテーションで確認して、Cargoを個別にインストールする方法を決定してください。","breadcrumbs":"事始め » Hello, Cargo!","id":"ch01-03-hello-cargo.html#hello-cargo","title":"Hello, Cargo!"},"ch02-00-guessing-game-tutorial.html#aまとめ":{"body":"ここまでで、数当てゲームの構築に成功しました。おめでとうございます! このプロジェクトは、たくさんの新しいRustの概念に触れる実践的な方法でした: let 、 match 、メソッド、関連関数、外部クレートの使用などなど。 以降の数章で、これらの概念についてより深く学ぶことになるでしょう。 第3章では、ほとんどのプログラミング言語に存在する、変数、データ型、関数などの概念について講義し、 それらのRustでの使用方法について示します。 第4章では、所有権について見ます。これにより、Rustは他の言語とかけ離れた存在になっています。 第5章では、構造体とメソッド記法について議論し、第6章ではenumの動作法を説明します。","breadcrumbs":"まとめ","id":"ch02-00-guessing-game-tutorial.html#aまとめ","title":"まとめ"},"ch02-00-guessing-game-tutorial.html#aクレートを使用して機能を追加する":{"body":"クレートはRustコードのパッケージであることを思い出してください。私たちがここまで作ってきたプロジェクトは、 バイナリクレート であり、これは実行可能形式になります。 rand クレートは ライブラリクレート であり、 他のプログラムで使用するためのコードが含まれています。 外部クレートを使用する部分は、Cargoがとても輝くところです。 rand を使ったコードを書ける前に、 Cargo.toml ファイルを編集して、 rand クレートを依存ファイルとして取り込む必要があります。 今このファイルを開いて、以下の行をCargoが自動生成した [dependencies] セクションヘッダの一番下に追記しましょう: ファイル名: Cargo.toml [dependencies] rand = \"0.3.14\" Cargo.toml ファイルにおいて、ヘッダに続くものは全て、他のセクションが始まるまで続くセクションの一部になります。 [dependecies] セクションは、プロジェクトが依存する外部クレートと必要とするバージョンを記述するところです。 ここでは、 rand クレートで、セマンティックバージョン指定子には 0.3.14 を指定します。Cargoは、 バージョンナンバー記述の標準規格である セマンティックバージョニング (時に SemVer と呼ばれる)を理解します。 0.3.14 という数字は、実際には ^0.3.14 の省略記法で、これは、「バージョン0.3.14と互換性のある公開APIを持つ任意のバージョン」を意味します。 さて、コードは一切変えずに、リスト2-2のようにプロジェクトをビルドしましょう。 $ cargo build Updating registry `https://github.com/rust-lang/crates.io-index` (レジストリを更新しています) Downloading rand v0.3.14 (rand v0.3.14をダウンロードしています) Downloading libc v0.2.14 (libc v0.2.14をダウンロードしています) Compiling libc v0.2.14 (libc v0.2.14をコンパイルしています) Compiling rand v0.3.14 (rand v0.3.14をコンパイルしています) Compiling guessing_game v0.1.0 (file:///projects/guessing_game) (guessing_game v0.1.0をコンパイルしています) Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs リスト2-2: randクレートを依存として追加した後の cargo build コマンドの出力 もしかしたら、バージョンナンバーは違うかもしれません(でも、互換性はあります、SemVerのおかげでね!)。 そして、行の出力順序も違うかもしれません。 今や、外部依存を持つようになったので、Cargoは レジストリ (registry、登録所)から最新バージョンを拾ってきます。 レジストリ とは、 Crates.io のデータのコピーです。Crates.ioとは、Rustのエコシステムにいる人間が、 他の人が使えるように自分のオープンソースのRustプロジェクトを投稿する場所です。 レジストリの更新後、Cargoは [dependencies] セクションをチェックし、まだ取得していないクレートを全部ダウンロードします。 今回の場合、 rand しか依存ファイルには列挙していませんが、Cargoは libc のコピーも拾ってきます。 rand クレートが libc に依存しているからですね。クレートのダウンロード完了後、コンパイラは依存ファイルをコンパイルし、 依存が利用可能な状態でプロジェクトをコンパイルします。 何も変更せず即座に cargo build コマンドを走らせたら、 Finished 行を除いて何も出力されないでしょう。 Cargoは、既に全ての依存をダウンロードしてコンパイル済みであることも、 あなたが Cargo.toml ファイルを弄ってないことも知っているからです。さらに、Cargoはプログラマがコードを変更していないことも検知するので、 再度コンパイルすることもありません。することがないので、ただ単に終了します。 src/main.rs ファイルを開き、些細な変更をし、保存して再度ビルドを行えば、2行だけ出力があるでしょう: $ cargo build Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs これらの行は、Cargoが src/main.rs ファイルへの取るに足らない変更に対して、ビルドを更新していることを示しています。 依存は変更していないので、Cargoは、既にダウンロードしてコンパイルまで済ませてある依存を使用できると検知します。 自分で書いたコードのみ再ビルドをかけるわけです。 Cargo.lock ファイルで再現可能なビルドを保証する Cargoは、プログラマが自分のコードを更新するたびに同じ生成物を再構成することを保証してくれるメカニズムを備えています: Cargoは、プログラマが示唆するまで、指定したバージョンの依存のみを使用します。 例として、 rand クレートの次週のバージョン0.3.15が登場し、重要なバグ修正がなされているけれども、 自分のコードを破壊してしまう互換性破壊があった場合はどうなるでしょう? この問題に対する回答は、 Cargo.lock ファイルであり、このファイルは、初めて cargo build コマンドを 走らせた時に生成され、現在 guessing_game ディレクトリに存在しています。プロジェクトを初めてビルドする際に、 Cargoは判断基準(criteria)に合致するよう全ての依存のバージョンを計算し、 Cargo.lock ファイルに記述します。 次にプロジェクトをビルドする際には、Cargoは Cargo.lock ファイルが存在することを確かめ、 再度バージョンの計算の作業を行うのではなく、そこに指定されているバージョンを使用します。 このことにより、自動的に再現可能なビルドを構成できるのです。つまり、明示的にアップグレードしない限り、 プロジェクトが使用するバージョンは 0.3.14 に保たれるのです。 Cargo.lock ファイルのおかげでね。 クレートを更新して新バージョンを取得する クレートを 本当に アップグレードする必要が出てきたら、Cargoは別のコマンド( update )を提供します。 これは、 Cargo.lock ファイルを無視して、 Cargo.toml ファイル内の全ての指定に合致する最新バージョンを計算します。 それがうまくいったら、Cargoはそれらのバージョンを Cargo.lock ファイルに記述します。 しかし標準でCargoは、 0.3.0 より大きく、 0.4.0 未満のバージョンのみを検索します。 rand クレートの新バージョンが2つリリースされていたら( 0.3.15 と 0.4.0 だとします)、 cargo update コマンドを走らせた時に以下のようなメッセージを目の当たりにするでしょう: $ cargo update Updating registry `https://github.com/rust-lang/crates.io-index` (レジストリ`https://github.com/rust-lang/crates-io-index`を更新しています) Updating rand v0.3.14 -> v0.3.15 (randクレートをv0.3.14 -> v0.3.15に更新しています) この時点で、 Cargo.lock ファイルに書かれている現在使用している rand クレートのバージョンが、 0.3.15 になっていることにも気付くでしょう。 rand のバージョン 0.4.0 または、 0.4.x シリーズのどれかを使用したかったら、 代わりに Cargo.toml ファイルを以下のように更新しなければならないでしょう: [dependencies] rand = \"0.4.0\" 次回、 cargo build コマンドを走らせたら、Cargoは利用可能なクレートのレジストリを更新し、 rand クレートの必要条件を指定した新しいバージョンに従って再評価します。 まだ第14章で議論する Cargo と そのエコシステム については述べたいことが山ほどありますが、 とりあえずは、これで知っておくべきことは全てです。 Cargoのおかげでライブラリはとても簡単に再利用ができるので、 Rustaceanは数多くのパッケージから構成された小規模のプロジェクトを書くことができるのです。","breadcrumbs":"クレートを使用して機能を追加する","id":"ch02-00-guessing-game-tutorial.html#aクレートを使用して機能を追加する","title":"クレートを使用して機能を追加する"},"ch02-00-guessing-game-tutorial.html#aループで複数回の予想を可能にする":{"body":"loop キーワードは、無限ループを作り出します。これを追加して、ユーザが何回も予想できるようにしましょう: ファイル名: src/main.rs // --snip-- println!(\"The secret number is: {}\", secret_number); loop { println!(\"Please input your guess.\"); // --snip-- match guess.cmp(&secret_number) { Ordering::Less => println!(\"Too small!\"), Ordering::Greater => println!(\"Too big!\"), Ordering::Equal => println!(\"You win!\"), } }\n} 見てわかる通り、予想入力部分以降をループに入れ込みました。ループ内の行にインデントを追加するのを忘れないようにして、 またプログラムを走らせてみましょう。新たな問題が発生したことに注目してください。 プログラムが教えた通りに動作しているからですね: 永遠に予想入力を求めるわけです! これでは、ユーザが終了できないようです! ユーザは、ctrl-cというキーボードショートカットを使って、いつでもプログラムを強制終了させられます。 しかし、「予想と秘密の数字を比較する」節の parse メソッドに関する議論で触れたように、 この貪欲なモンスターを回避する別の方法があります: ユーザが数字以外の答えを入力すれば、プログラムはクラッシュするのです。 ユーザは、その利点を活かして、終了することができます。以下のようにですね: $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs Running `target/debug/guessing_game`\nGuess the number!\nThe secret number is: 59\nPlease input your guess.\n45\nYou guessed: 45\nToo small!\nPlease input your guess.\n60\nYou guessed: 60\nToo big!\nPlease input your guess.\n59\nYou guessed: 59\nYou win!\nPlease input your guess.\nquit\nthread 'main' panicked at 'Please type a number!: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:785\n(スレッド'main'は'数字を入力してください!: ParseIntError { kind: InvalidDigit }', src/libcore/result.rs:785でパニックしました)\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\n(注釈: `RUST_BACKTRACE=1`で走らせるとバックトレースを見れます)\nerror: Process didn't exit successfully: `target/debug/guess` (exit code: 101)\n(エラー: プロセスは予期なく終了しました) quit と入力すれば、実際にゲームを終了できるわけですが、別に他の数字以外の入力でもそうなります。 しかしながら、これは最低限度と言えるでしょう。正しい数字が予想されたら、自動的にゲームが停止してほしいわけです。","breadcrumbs":"ループで複数回の予想を可能にする","id":"ch02-00-guessing-game-tutorial.html#aループで複数回の予想を可能にする","title":"ループで複数回の予想を可能にする"},"ch02-00-guessing-game-tutorial.html#a不正な入力を処理する":{"body":"さらにゲームの振る舞いを改善するために、ユーザが数値以外を入力した時にプログラムをクラッシュさせるのではなく、 非数値を無視してユーザが数当てを続けられるようにしましょう!これは、 guess が String 型から u32 型に変換される行を改変することで達成できます。リスト2-5のようにですね。 ファイル名: src/main.rs // --snip-- io::stdin().read_line(&mut guess) .expect(\"Failed to read line\"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue,\n}; println!(\"You guessed: {}\", guess); // --snip-- リスト2-5: 非数値の予想を無視し、プログラムをクラッシュさせるのではなく、もう1回予想してもらう expect メソッドの呼び出しから match 式に切り替えることは、 エラーでクラッシュする動作からエラー処理を行う処理に変更する一般的な手段になります。 parse メソッドは、 Result 型を返し、 Result は Ok か Err の列挙子を取りうる列挙型であることを思い出してください。 ここでは match 式を使っています。 cmp メソッドの Ordering という結果のような感じですね。 parse メソッドは、文字列から数値への変換に成功したら、結果の数値を保持する Ok 値を返します。 この Ok 値は、最初のアームのパターンにマッチし、この match 式は parse メソッドが生成し、 Ok 値に格納した num の値を返すだけです。その数値が最終的に、生成している新しい guess 変数として欲しい場所に存在します。 parse メソッドは、文字列から数値への変換に 失敗 したら、エラーに関する情報を多く含む Err 値を返します。 この Err 値は、最初の match アームの Ok(num) というパターンにはマッチしないものの、 2番目のアームの Err(_) というパターンにはマッチするわけです。この _ は、包括値です; この例では、 保持している情報がどんなものでもいいから全ての Err 値にマッチさせたいと宣言しています。 従って、プログラムは2番目のアームのコードを実行し( continue ですね)、これは、 loop の次のステップに移り、再度予想入力を求めるようプログラムに指示します。故に実質的には、 プログラムは parse メソッドが遭遇しうる全てのエラーを無視するようになります! さて、プログラムの全てがうまく予想通りに動くはずです。試しましょう: $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Running `target/debug/guessing_game`\nGuess the number!\nThe secret number is: 61\nPlease input your guess.\n10\nYou guessed: 10\nToo small!\nPlease input your guess.\n99\nYou guessed: 99\nToo big!\nPlease input your guess.\nfoo\nPlease input your guess.\n61\nYou guessed: 61\nYou win! 素晴らしい!最後にひとつまみ変更を加えて、数当てゲームを完了にしましょう。 プログラムが未だに秘密の数字を出力していることを思い出してください。テスト中はうまく動くけど、 ゲームを台無しにしてしまいます。秘密の数字を出力する println! を削除しましょう。 リスト2-6が成果物のコードです: ファイル名: src/main.rs extern crate rand; use std::io;\nuse std::cmp::Ordering;\nuse rand::Rng; fn main() { println!(\"Guess the number!\"); let secret_number = rand::thread_rng().gen_range(1, 101); loop { println!(\"Please input your guess.\"); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect(\"Failed to read line\"); let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, }; println!(\"You guessed: {}\", guess); match guess.cmp(&secret_number) { Ordering::Less => println!(\"Too small!\"), Ordering::Greater => println!(\"Too big!\"), Ordering::Equal => { println!(\"You win!\"); break; } } }\n} リスト2-6: 数当てゲームの完全なコード","breadcrumbs":"不正な入力を処理する","id":"ch02-00-guessing-game-tutorial.html#a不正な入力を処理する","title":"不正な入力を処理する"},"ch02-00-guessing-game-tutorial.html#a乱数を生成する":{"body":"Cargo.toml に rand クレートを追加したので、 rand クレートを使用開始しましょう。 次のステップは、リスト2-3のように src/main.rs ファイルを更新することです。 ファイル名: src/main.rs extern crate rand; use std::io;\nuse rand::Rng; fn main() { println!(\"Guess the number!\"); let secret_number = rand::thread_rng().gen_range(1, 101); println!(\"The secret number is: {}\", secret_number); //秘密の数字は次の通り: {} println!(\"Please input your guess.\"); let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect(\"Failed to read line\"); println!(\"You guessed: {}\", guess);\n} リスト2-3: 乱数を生成するコードの追加 まず、コンパイラに rand クレートを外部依存として使用することを知らせる行を追加しています。 これにより、 use rand を呼ぶのと同じ効果が得られるので、 rand クレートのものを rand:: という接頭辞をつけて呼び出せるようになりました。 次に、別の use 行を追加しています: use rand::Rng ですね。 Rng トレイトは乱数生成器が実装するメソッドを定義していて、 このトレイトがスコープにないと、メソッドを使用できないのです。トレイトについて詳しくは、 第10章で解説します。 また、途中に2行追加もしています。 rand::thread_rng 関数は、これから使う特定の乱数生成器を返してくれます: この乱数生成器は、実行スレッドに固有で、OSにより、シード値を与えられています。 次に、この乱数生成器の gen_range メソッドを呼び出しています。このメソッドは、 use rand::Rng 文でスコープに導入した Rng トレイトで定義されています。 gen_range メソッドは二つの数字を引数に取り、 それらの間の乱数を生成してくれます。範囲は下限値を含み、上限値を含まないため、 1 と 101 と指定しないと1から100の範囲の数字は得られません。 注釈: 単純に使用すべきトレイトと、クレートからどのメソッドと関数を呼び出すか知っているわけではないでしょう。 クレートの使用方法は、各クレートのドキュメントにあります。Cargoの別の素晴らしい機能は、 cargo doc --open コマンドを走らせてローカルに存在する依存すべてのドキュメントをビルドし、ブラウザで閲覧できる機能です。 例えば、 rand クレートの他の機能に興味があるなら、 cargo doc --open コマンドを走らせて、 左側のサイドバーから rand をクリックしてください。 コードに追加した2行目は、秘密の数字を出力してくれます。これは、プログラムを開発中にはテストするのに役立ちますが、 最終版からは削除する予定です。プログラムがスタートと同時に答えを出力しちゃったら、ゲームになりませんからね! 試しに何回かプログラムを走らせてみてください: $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs Running `target/debug/guessing_game`\nGuess the number! (何回も出ているので、ここでは和訳は省略します)\nThe secret number is: 7\nPlease input your guess.\n4\nYou guessed: 4\n$ cargo run Running `target/debug/guessing_game`\nGuess the number!\nThe secret number is: 83\nPlease input your guess.\n5\nYou guessed: 5 毎回異なる乱数が出て、その数字はすべて1から100の範囲になるはずです。よくやりました!","breadcrumbs":"乱数を生成する","id":"ch02-00-guessing-game-tutorial.html#a乱数を生成する","title":"乱数を生成する"},"ch02-00-guessing-game-tutorial.html#a予想と秘密の数字を比較する":{"body":"今や、ユーザ入力と乱数生成ができるようになったので、比較することができますね。 このステップはリスト2-4に示されています。これから説明するように、このコードは現状ではコンパイルできないことに注意してください。 ファイル名: src/main.rs extern crate rand; use std::io;\nuse std::cmp::Ordering;\nuse rand::Rng; fn main() { // ---snip--- println!(\"You guessed: {}\", guess); match guess.cmp(&secret_number) { Ordering::Less => println!(\"Too small!\"), //小さすぎ! Ordering::Greater => println!(\"Too big!\"), //大きすぎ! Ordering::Equal => println!(\"You win!\"), //やったね! }\n} リスト2-4: 2値比較の可能性のある返り値を処理する 最初の新しい点は、別の use 文です。これで、 std::cmp::Ordering という型を標準ライブラリからスコープに導入しています。 Result と同じく Ordering もenumです。ただ、 Ordering の列挙子は、 Less 、 Greater 、 Equal です。これらは、2値比較した時に発生しうる3種類の結果です。 match guess.cmp(&secret_number) { Ordering::Less => println!(\"Too small!\"), Ordering::Greater => println!(\"Too big!\"), Ordering::Equal => println!(\"You win!\"),\n} それから、一番下に新しく5行追加して Ordering 型を使用しています。 cmp メソッドは、 2値を比較し、比較できるものに対してなら何に対しても呼び出せます。このメソッドは、 比較したいものへの参照を取ります: ここでは、 guess 変数と secret_number 変数を比較しています。 それからこのメソッドは use 文でスコープに導入した Ordering 列挙型の値を返します。 match 式を使用して、 guess 変数と secret_number を cmp に渡して返ってきた Ordering の列挙子に基づき、 次の動作を決定しています。 match 式は、複数の アーム (腕)からできています。一つのアームは、 パターンとそのパターンに match 式の冒頭で与えた値がマッチした時に走るコードから構成されています。Rustは、 match に与えられた値を取り、各アームのパターンを順番に照合していきます。 match 式とパターンは、 コードを書く際に出くわす様々なシチュエーションを表現させてくれ、 すべてのシチュエーションに対処していることを保証するのを手助けしてくれるRustの強力な機能です。 これらの機能は、それぞれ、第6章と第18章で詳しく講義することにします。 ここで使われている match 式でどんなことが起こるかの例をじっくり観察してみましょう!例えば、 ユーザは50と予想し、ランダム生成された秘密の数字は今回、38だったとしましょう。コードが50と38を比較すると、 cmp メソッドは Ordering::Greater を返します。50は38よりも大きいからですね。 match 式に Ordering::Greater が与えられ、各アームのパターンを吟味し始めます。まず、 最初のアームのパターンと照合します( Ordering::Less ですね)。しかし、 値の Ordering::Greater と Ordering::Less はマッチしないため、このアームのコードは無視され、 次のアームに移ります。次のアームのパターン、 Ordering::Greater は 見事に Ordering::Greater とマッチします! このアームに紐づけられたコードが実行され、画面に Too big! が表示されます。 これで match 式の実行は終わりになります。この筋書きでは、最後のアームと照合する必要はもうないからですね。 ところが、リスト2-4のコードは、まだコンパイルが通りません。試してみましょう: $ cargo build Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\nerror[E0308]: mismatched types (型が合いません) --> src/main.rs:23:21 |\n23 | match guess.cmp(&secret_number) { | ^^^^^^^^^^^^^^ expected struct `std::string::String`, found integral variable | (構造体`std::string::String`を予期したけど、整数型変数が見つかりました) | = note: expected type `&std::string::String` = note: found type `&{integer}` error: aborting due to previous error (先のエラーのため、処理を中断します)\nCould not compile `guessing_game`. (`guessing_game`をコンパイルできませんでした) このエラーの核は、 型の不一致 があると言っています。Rustには、強い静的型システムがあります。 しかし、型推論にも対応しています。 let guess = String::new() と書いた時、コンパイラは、 guess が String 型であるはずと推論してくれ、その型を明示させられることはありませんでした。 一方で、 secret_number 変数は、数値型です。1から100を表すことができる数値型はいくつかあります: i32 は32ビットの数字; u32 は32ビットの非負数字; i64 は64ビットの数字などです。 Rustでの標準は、 i32 型であり、型情報をどこかに追加して、コンパイラに異なる数値型だと推論させない限り、 secret_number の型はこれになります。エラーの原因は、Rustでは、文字列と数値型を比較できないことです。 究極的には、プログラムが入力として読み込む String 型を現実の数値型に変換し、 予想と数値として比較できるようにしたいわけです。これは、以下の2行を main 関数の本体に追記することでできます: ファイル名: src/main.rs // --snip-- let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect(\"Failed to read line\"); let guess: u32 = guess.trim().parse() .expect(\"Please type a number!\"); //数値を入力してください! println!(\"You guessed: {}\", guess); match guess.cmp(&secret_number) { Ordering::Less => println!(\"Too small!\"), Ordering::Greater => println!(\"Too big!\"), Ordering::Equal => println!(\"You win!\"), }\n} その2行とは: let guess: u32 = guess.trim().parse() .expect(\"Please type a number!\"); guess という名前の変数を生成しています。あれ、でも待って。もうプログラムには guess という名前の変数がありませんでしたっけ? 確かにありますが、Rustでは、新しい値で guess の値を 覆い隠す (shadow)ことが許されているのです。 この機能は、値を別の型に変換したいシチュエーションでよく使われます。 シャドーイング(shadowing)のおかげで別々の変数を2つ作らされることなく、 guess という変数名を再利用することができるのです。 guess_str と guess みたいなね(シャドーイングについては、第3章でもっと掘り下げます)。 guess を guess.trim().parse() という式に束縛しています。この式中の guess は、 入力が入った String 型の元々の guess を指しています。 String オブジェクトの trim メソッドは、 両端の空白をすべて除去します。 u32 型は、数字しか含むことができませんが、ユーザは、 read_line の処理を終えるためにエンターを押さなければなりません。 ユーザがエンターを押したら、改行文字が文字列に追加されます。 具体例として、ユーザが5を入力して、 エンターを押せば、 guess は次のようになります: 5\\n 。 この \\n が「改行」、つまりエンターキーを押した結果を表しているわけです。 trim メソッドは、 \\n を削除するので、ただの 5 になります。 文字列の parse メソッド は、文字列を解析して何らかの数値にします。 このメソッドは、いろんな数値型を解析できるので、 let guess: u32 としてコンパイラに私たちが求めている型をズバリ示唆する必要があるのです。 guess の後のコロン( : )がコンパイラに変数の型を注釈する合図になります。 Rustには、組み込みの数値型がいくつかあります; ここの u32 型は、32ビットの非負整数です。 u32 型は小さな非負整数のデフォルトの選択肢として丁度良いです。他の数値型については、第3章で学ぶでしょう。 付け加えると、このサンプルプログラムの u32 という注釈と secret_number 変数との比較は、 secret_number 変数も u32 型であるとコンパイラが推論することを意味します。 従って、今では比較が同じ型の2つの値で行われることになるわけです! parse メソッドの呼び出しは、エラーになりやすいです。例としては、文字列が A👍% を含んでいたら、 数値に変換できるわけがありません。失敗する可能性があるので、 parse メソッドは、 Result 型を返すわけです。ちょうど、(「Result型で失敗する可能性に対処する」節で先ほど議論した) read_line メソッドのようにというわけですね。 今回も、 expect メソッドを使用して Result 型を同じように扱います。この Result を expect メソッドを再度使用して、 同じように扱います。もし、文字列から数値を生成できなかったために、 parse メソッドが Result 型の Err 列挙子を返したら、 expect メソッドの呼び出しは、ゲームをクラッシュさせ、与えたメッセージを表示します。 もし、 parse メソッドが文字列の数値への変換に成功したら、 Result 型の Ok 列挙子を返し、 expect メソッドは、 Ok 値から必要な数値を返してくれます。 さあ、プログラムを走らせましょう! $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs Running `target/debug/guessing_game`\nGuess the number!\nThe secret number is: 58\nPlease input your guess. 76\nYou guessed: 76\nToo big! いいですね!予想の前にスペースを追加したにもかかわらず、プログラムはちゃんとユーザが76と予想したことを導き出しました。 プログラムを何回か走らせて、異なる入力の色々な振る舞いを確認してください: つまり、 数字を正しく言い当てたり、大きすぎる値を予想したり、小さすぎる数字を入力したりということです。 ここまでで大方ゲームはうまく動くようになりましたが、まだユーザは1回しか予想できません。 ループを追加して、その部分を変更しましょう!","breadcrumbs":"予想と秘密の数字を比較する","id":"ch02-00-guessing-game-tutorial.html#a予想と秘密の数字を比較する","title":"予想と秘密の数字を比較する"},"ch02-00-guessing-game-tutorial.html#a予想を処理する":{"body":"数当てプログラムの最初の部分は、ユーザに入力を求め、その入力を処理し、予期した形式になっていることを確認します。 手始めに、プレーヤーが予想を入力できるようにしましょう。 リスト2-1のコードを src/main.rs に入力してください。 ファイル名: src/main.rs use std::io; fn main() { println!(\"Guess the number!\"); // 数を当ててごらん println!(\"Please input your guess.\"); // ほら、予想を入力してね let mut guess = String::new(); io::stdin().read_line(&mut guess) .expect(\"Failed to read line\"); // 行の読み込みに失敗しました println!(\"You guessed: {}\", guess); // 次のように予想しました: {}\n} リスト2-1: ユーザに予想を入力してもらい、それを出力するコード 注釈: The programming language Rust第1版の翻訳者によると、 ソースコードのコメント中以外に日本語文字があるとコンパイルに失敗することがあるそうなので、文字列の英語は、コメントに和訳を載せます。 また、重複する内容の場合には、最初の1回だけ掲載するようにします。 このコードには、たくさんの情報が詰め込まれていますね。なので、行ごとに見ていきましょう。 ユーザ入力を受け付け、結果を出力するためには、 io (入/出力)ライブラリをスコープに導入する必要があります。 io ライブラリは、標準ライブラリ( std として知られています)に存在します: use std::io; デフォルトでは、 prelude に存在するいくつかの型のみ使えます。 もし、使用したい型がpreludeにない場合は、 use 文で明示的にその型をスコープに導入する必要があります。 std::io ライブラリを使用することで、ユーザ入力を受け付ける能力などの実用的な機能の多くを使用することができます。 第1章で見た通り、 main 関数がプログラムへのエントリーポイント( 脚注 : スタート地点)になります: fn main() { fn 構文が関数を新しく宣言し、かっこの () は引数がないことを示し、波括弧の { が関数本体のスタート地点になります。 また、第1章で学んだように、 println! は、文字列を画面に表示するマクロになります: println!(\"Guess the number!\"); println!(\"Please input your guess.\"); このコードは、このゲームが何かを出力し、ユーザに入力を求めています。","breadcrumbs":"予想を処理する","id":"ch02-00-guessing-game-tutorial.html#a予想を処理する","title":"予想を処理する"},"ch02-00-guessing-game-tutorial.html#a値を変数に保持する":{"body":"次に、ユーザ入力を保持する場所を作りましょう。こんな感じに: let mut guess = String::new(); さあ、プログラムが面白くなってきましたね。このたった1行でいろんなことが起きています。 これが let 文であることに注目してください。これを使用して 変数 を生成しています。 こちらは、別の例です: let foo = bar; この行では、 foo という名前の新しい変数を作成し、 bar の値に束縛しています。 Rustでは、変数は標準で不変(immutable)です。この概念について詳しくは、 第3章の「変数と可変性」節で議論します。以下の例には、 変数名の前に mut をつけて変数を可変にする方法が示されています: let foo = 5; // immutable\nlet mut bar = 5; // mutable 注釈: // という記法は、行末まで続くコメントを記述します。 コンパイラは、コメントを一切無視し、これについても第3章で詳しく議論します。 数当てゲームのプログラムに戻りましょう。さあ、 let mut guess が guess という名前の可変変数を導入するとわかりましたね。 イコール記号( = )の反対側には、変数 guess が束縛される値があります。この値は、 String::new 関数の呼び出し結果であり、この関数は、 String 型のオブジェクトを返します。 String 型は、標準ライブラリによって提供される文字列型で、 サイズ可変、UTF-8エンコードされたテキスト破片になります。 ::new 行にある :: という記法は、 new が String 型の 関連関数 であることを表しています。 関連関数とは、 String 型の特定のオブジェクトよりも型(この場合は String )に対して 実装された関数のことであり、 静的(スタティック)メソッド と呼ばれる言語もあります。 この new 関数は、新しく空の文字列を生成します。 new 関数は、いろんな型に見られます。 なぜなら、何らかの新規値を生成する関数にとってありふれた名前だからです。 まとめると、 let mut guess = String::new(); という行は、現在、新たに空の String オブジェクトに束縛されている 可変変数を作っているわけです。ふう! プログラムの1行目で、 use std::io として、標準ライブラリから入/出力機能を取り込んだことを思い出してください。 今度は、 io 型の stdin 関連関数を呼び出しましょう: io::stdin().read_line(&mut guess) .expect(\"Failed to read line\"); 仮に、プログラムの冒頭で use std::io としていなければ、この関数呼び出しは、 std::io::stdin と記述していたでしょう。 この stdin 関数は、 std::io::Stdin オブジェクトを返し、この型は、 ターミナルの標準入力へのハンドルを表す型になります。 その次のコード片、 .read_line(&mut guess) は、標準入力ハンドルの read_line メソッドを呼び出して、ユーザから入力を受け付けます。また、 read_line メソッドに対して、 &mut guess という引数を一つ渡していますね。 read_line メソッドの仕事は、ユーザが標準入力したものすべてを取り出し、文字列に格納することなので、 格納する文字列を引数として取ります。この文字列引数は、可変である必要があります。 メソッドがユーザ入力を追記して、文字列の中身を変えられるようにということですね。 & という記号は、この引数が 参照 であることを表し、これのおかげで、データを複数回メモリにコピーせずとも、 コードの複数箇所で同じデータにアクセスできるようになるわけです。参照は複雑な機能であり、 とても安全かつ簡単に参照を使うことができることは、Rustの主要な利点の一つでもあります。 そのような詳細を知らなくても、このプログラムを完成させることはできます。 現時点では、変数のように、参照も標準で不変であることを知っておけばいいでしょう。 故に、 &guess と書くのではなく、 &mut guess と書いて、可変にする必要があるのです。 (第4章で参照についてより詳細に説明します)","breadcrumbs":"値を変数に保持する","id":"ch02-00-guessing-game-tutorial.html#a値を変数に保持する","title":"値を変数に保持する"},"ch02-00-guessing-game-tutorial.html#a数当てゲームをプログラムする":{"body":"実物のプロジェクトに一緒に取り組むことで、Rustの世界へ飛び込みましょう! この章では、実際のプログラム内で使用しながらいくつかの一般的なRustの概念に触れます。 let 、 match 、メソッド、関連関数、外部クレートの使用などについて学ぶでしょう! 後ほどの章でこれらの概念について深く知ることになります。この章では、基礎部分だけにしましょう。 古典的な初心者向けのプログラミング問題を実装してみましょう: 数当てゲームです。 これは以下のように動作します: プログラムは1から100までの乱数整数を生成します。 そしてプレーヤーに予想を入力するよう促します。予想を入力したら、プログラムは、 その予想が小さすぎたか大きすぎたかを出力します。予想が当たっていれば、ゲームは祝福メッセージを表示し、 終了します。","breadcrumbs":"数当てゲームをプログラムする","id":"ch02-00-guessing-game-tutorial.html#a数当てゲームをプログラムする","title":"数当てゲームをプログラムする"},"ch02-00-guessing-game-tutorial.html#a新規プロジェクトの立ち上げ":{"body":"新規プロジェクトを立ち上げるには、第1章で作成した projects ディレクトリに行き、 Cargoを使って新規プロジェクトを作成します。以下のように: $ cargo new guessing_game --bin\n$ cd guessing_game 最初のコマンド cargo new は、プロジェクト名を第1引数に取ります( guessing_game ですね)。 --bin というフラグは、Cargoにバイナリ生成プロジェクトを作成させます。第1章のものと似ていますね。 2番目のコマンドで新規プロジェクトのディレクトリに移動します。 生成された Cargo.toml ファイルを見てください: ファイル名: Cargo.toml [package]\nname = \"guessing_game\"\nversion = \"0.1.0\"\nauthors = [\"名前 \"] [dependencies] もし、Cargoがあなたの環境から取得した作者情報が間違っていたら、 ファイルを編集して保存し直してください。 第1章でも見かけたように、 cargo new コマンドは、\"Hello, world!\"プログラムを生成してくれます。 src/main.rs ファイルをチェックしてみましょう: ファイル名: src/main.rs fn main() { println!(\"Hello, world!\");\n} さて、この\"Hello, world!\"プログラムをコンパイルし、 cargo run コマンドを使用して、 以前と同じように動かしてみましょう: $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs Running `target/debug/guessing_game`\nHello, world! run コマンドは、プロジェクトに迅速に段階を踏んで取り掛かる必要がある場合に有用であり、 次のステップに進む前に各段階を急速にテストして、このゲームではそれを行います。 再度 src/main.rs ファイルを開きましょう。ここにすべてのコードを書いていきます。","breadcrumbs":"新規プロジェクトの立ち上げ","id":"ch02-00-guessing-game-tutorial.html#a新規プロジェクトの立ち上げ","title":"新規プロジェクトの立ち上げ"},"ch02-00-guessing-game-tutorial.html#a最初の部分をテストする":{"body":"数当てゲームの最初の部分をテストしてみましょう。 cargo run でプログラムを走らせてください: $ cargo run Compiling guessing_game v0.1.0 (file:///projects/guessing_game) Finished dev [unoptimized + debuginfo] target(s) in 2.53 secs Running `target/debug/guessing_game`\nGuess the number!\nPlease input your guess.\n6\nYou guessed: 6 ここまでで、ゲームの最初の部分は完成になります: キーボードからの入力を受け付け、出力できています。","breadcrumbs":"最初の部分をテストする","id":"ch02-00-guessing-game-tutorial.html#a最初の部分をテストする","title":"最初の部分をテストする"},"ch02-00-guessing-game-tutorial.html#a正しい予想をした後に終了する":{"body":"break 文を追加して、ユーザが勝った時にゲームが終了するようにプログラムしましょう: ファイル名: src/main.rs // --snip-- match guess.cmp(&secret_number) { Ordering::Less => println!(\"Too small!\"), Ordering::Greater => println!(\"Too big!\"), Ordering::Equal => { println!(\"You win!\"); break; } } }\n} break 文の1行を You win! の後に追記することで、ユーザが秘密の数字を正確に予想した時に、 プログラムはループを抜けるようになりました。ついでに、ループを抜けることは、プログラムを終了することを意味します。 ループが main 関数の最後の部分だからですね。","breadcrumbs":"正しい予想をした後に終了する","id":"ch02-00-guessing-game-tutorial.html#a正しい予想をした後に終了する","title":"正しい予想をした後に終了する"},"ch02-00-guessing-game-tutorial.html#a秘密の数字を生成する":{"body":"次に、ユーザが数当てに挑戦する秘密の数字を生成する必要があります。毎回この秘密の数字は、変わるべきです。 ゲームが何回も楽しめるようにですね。ゲームが難しくなりすぎないように、1から100までの乱数を使用しましょう。 Rustの標準ライブラリには、乱数機能はまだ含まれていません。ですが、実は、 Rustの開発チームが rand クレート を用意してくれています。","breadcrumbs":"秘密の数字を生成する","id":"ch02-00-guessing-game-tutorial.html#a秘密の数字を生成する","title":"秘密の数字を生成する"},"ch02-00-guessing-game-tutorial.html#println-マクロのプレースホルダーで値を出力する":{"body":"閉じ波かっこを除けば、ここまでに追加されたコードのうち議論すべきものは、残り1行であり、それは以下の通りです: println!(\"You guessed: {}\", guess); この行は、ユーザ入力を保存した文字列の中身を出力します。1組の波括弧の {} は、プレースホルダーの役目を果たします: {} は値を所定の場所に保持する小さなカニのはさみと考えてください。波括弧を使って一つ以上の値を出力できます: 最初の波括弧の組は、フォーマット文字列の後に列挙された最初の値に対応し、 2組目は、2つ目の値、とそんな感じで続いていきます。1回の println! の呼び出しで複数の値を出力するコードは、 以下のような感じになります: let x = 5;\nlet y = 10; println!(\"x = {} and y = {}\", x, y); このコードは、 x = 5 and y = 10 と出力するでしょう.","breadcrumbs":"println! マクロのプレースホルダーで値を出力する","id":"ch02-00-guessing-game-tutorial.html#println-マクロのプレースホルダーで値を出力する","title":"println! マクロのプレースホルダーで値を出力する"},"ch02-00-guessing-game-tutorial.html#result-型で失敗の可能性を扱う":{"body":"まだ、この行は終わりではありませんよ。ここまでに議論したのはテキストでは1行ですが、コードとしての論理行としては、 まだ所詮最初の部分でしかないのです。2番目の部分はこのメソッドです: .expect(\"Failed to read line\"); .foo() という記法で、メソッドを呼び出す時、改行と空白で長い行を分割するのがしばしば賢明です。 今回の場合、こう書くこともできますよね: io::stdin().read_line(&mut guess).expect(\"Failed to read line\"); しかし、長い行は読みづらいものです。なので、分割しましょう: 2回のメソッド呼び出しに、2行です。 さて、この行が何をしているのかについて議論しましょうか。 以前にも述べたように、 read_line メソッドは、渡された文字列にユーザが入力したものを入れ込むだけでなく、 値も返します(今回は io::Result です)。 Rustには Result と名のついた型が、 標準ライブラリにたくさんあります: 汎用の Result の他、 io::Result などのサブモジュール用に特化したものまで。 この Result 型は、 列挙型 であり、普通、 enum (イーナム)と呼ばれます。 列挙型とは、固定された種類の値を持つ型のことであり、それらの値は、enumの 列挙子 (variant)と呼ばれます。 enumについては、第6章で詳しく解説します。 Result 型に関しては、列挙子は Ok か Err です。 Ok 列挙子は、処理が成功したことを表し、 中に生成された値を保持します。 Err 列挙子は、処理が失敗したことを意味し、 Err は、処理が失敗した過程や、 理由などの情報を保有します。 これら Result 型の目的は、エラー処理の情報をコード化することです。 Result 型の値も、他の型同様、 メソッドが定義されています。 io::Result オブジェクトには、呼び出し可能な expect メソッド があります。 この io::Result オブジェクトが Err 値の場合、 expect メソッドはプログラムをクラッシュさせ、 引数として渡されたメッセージを表示します。 read_line メソッドが Err を返したら、 恐らく根底にあるOSによるエラーに起因するのでしょう。 この io::Result オブジェクトが Ok 値の場合、 expect メソッドは、 Ok 列挙子が保持する 返り値を取り出して、ただその値を返すので、これを使用することができるでしょう。 今回の場合、その返り値とは、ユーザが標準入力に入力したデータのバイト数になります。 もし、 expect メソッドを呼び出さなかったら、コンパイルは通るものの、警告が出るでしょう: $ cargo build Compiling guessing_game v0.1.0 (file:///projects/guessing_game)\nwarning: unused `std::result::Result` which must be used\n(警告: 使用されなければならない`std::result::Result`が使用されていません) --> src/main.rs:10:5 |\n10 | io::stdin().read_line(&mut guess); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: #[warn(unused_must_use)] on by default コンパイラは、私たちが read_line メソッドから返ってきた Result 値を使用していないと警告してきており、 これは、プログラムがエラーの可能性に対処していないことを示します。 警告を抑制する正しい手段は、実際にエラー対処コードを書くことですが、今は、 問題が起きた時にプロラグムをクラッシュさせたいので、 expect を使用できるわけです。 エラーから復旧する方法については、第9章で学ぶでしょう。","breadcrumbs":"Result 型で失敗の可能性を扱う","id":"ch02-00-guessing-game-tutorial.html#result-型で失敗の可能性を扱う","title":"Result 型で失敗の可能性を扱う"},"ch03-00-common-programming-concepts.html#aキーワード":{"body":"Rust言語にも他の言語同様、キーワードが存在し、これらは言語だけが使用できるようになっています。 これらの単語は、変数や関数名には使えないことを弁えておいてください。ほとんどのキーワードは、特別な意味を持っており、 自らのRustプログラムにおいて、様々な作業をこなすために使用することができます; いくつかは、紐付けられた機能がないものの、将来Rustに追加されるかもしれない機能用に予約されています。 キーワードの一覧は、付録Aで確認できます。","breadcrumbs":"キーワード","id":"ch03-00-common-programming-concepts.html#aキーワード","title":"キーワード"},"ch03-00-common-programming-concepts.html#a一般的なプログラミングの概念":{"body":"この章では、ほとんど全てのプログラミング言語で見られる概念を講義し、それらがRustにおいて、 どう動作するかを見ていきます。多くのプログラミング言語は、その核心において、いろいろなものを共有しています。 この章で提示する概念は、全てRustに固有のものではありませんが、Rustの文脈で議論し、 これらの概念を使用することにまつわる仕様を説明します。 具体的には、変数、基本的な型、関数、コメント、そしてフロー制御について学びます。 これらの基礎は全てのRustプログラムに存在するものであり、それらを早期に学ぶことにより、強力な基礎を築くことになるでしょう。","breadcrumbs":"一般的なプログラミングの概念","id":"ch03-00-common-programming-concepts.html#a一般的なプログラミングの概念","title":"一般的なプログラミングの概念"},"ch03-01-variables-and-mutability.html#aシャドーイング":{"body":"第2章の数当てゲームのチュートリアル、「予想と秘密の数字を比較する」節で見たように、前に定義した変数と同じ名前の変数を新しく宣言でき、 新しい変数は、前の変数を覆い隠します。Rustaceanはこれを最初の変数は、 2番目の変数に 覆い隠さ れたと言い、この変数を使用した際に、2番目の変数の値が現れるということです。 以下のようにして、同じ変数名を用いて変数を覆い隠し、 let キーワードの使用を繰り返します: ファイル名: src/main.rs fn main() { let x = 5; let x = x + 1; let x = x * 2; println!(\"The value of x is: {}\", x);\n} このプログラムはまず、 x を 5 という値に束縛します。それから let x = を繰り返すことで x を覆い隠し、 元の値に 1 を加えることになるので、 x の値は 6 になります。 3番目の let 文も x を覆い隠し、以前の値に 2 をかけることになるので、 x の最終的な値は 12 になります。 このプログラムを走らせたら、以下のように出力するでしょう: $ cargo run Compiling variables v0.1.0 (file:///projects/variables) Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs Running `target/debug/variables`\nThe value of x is: 12 シャドーイングは、変数を mut にするのとは違います。なぜなら、 let キーワードを使わずに、 誤ってこの変数に再代入を試みようものなら、コンパイルエラーが出るからです。 let を使うことで、 値にちょっとした加工は行えますが、その加工が終わったら、変数は不変になるわけです。 mut と上書きのもう一つの違いは、再度 let キーワードを使用したら、実効的には新しい変数を生成していることになるので、 値の型を変えつつ、同じ変数名を使いまわせることです。例えば、 プログラムがユーザに何らかのテキストに対して空白文字を入力することで何個分のスペースを表示したいかを尋ねますが、 ただ、実際にはこの入力を数値として保持したいとしましょう: let spaces = \" \";\nlet spaces = spaces.len(); この文法要素は、容認されます。というのも、最初の spaces 変数は文字列型であり、2番目の spaces 変数は、 たまたま最初の変数と同じ名前になったまっさらな変数のわけですが、数値型になるからです。故に、シャドーイングのおかげで、 異なる名前を思いつく必要がなくなるわけです。 spaces_str と spaces_num などですね; 代わりに、 よりシンプルな spaces という名前を再利用できるわけです。一方で、この場合に mut を使おうとすると、 以下に示した通りですが、コンパイルエラーになるわけです: let mut spaces = \" \";\nspaces = spaces.len(); 変数の型を可変にすることは許されていないと言われているわけです: error[E0308]: mismatched types (型が合いません) --> src/main.rs:3:14 |\n3 | spaces = spaces.len(); | ^^^^^^^^^^^^ expected &str, found usize | (&str型を予期しましたが、usizeが見つかりました) | = note: expected type `&str` found type `usize` さあ、変数が動作する方法を見てきたので、今度は変数が取りうるデータ型について見ていきましょう。","breadcrumbs":"普遍的なプログラミング概念 » シャドーイング","id":"ch03-01-variables-and-mutability.html#aシャドーイング","title":"シャドーイング"},"ch03-01-variables-and-mutability.html#a変数と可変性":{"body":"第2章で触れた通り、変数は標準で不変になります。これは、 Rustが提供する安全性や簡便な並行性の利点を享受する形でコードを書くための選択の1つです。 ところが、まだ変数を可変にするという選択肢も残されています。 どのように、そしてなぜRustは不変性を推奨するのか、さらには、なぜそれとは違う道を選びたくなることがあるのか見ていきましょう。 変数が不変であると、値が一旦名前に束縛されたら、その値を変えることができません。 これを具体的に説明するために、 projects ディレクトリに cargo new --bin variables コマンドを使って、 variables という名前のプロジェクトを生成しましょう。 それから、新規作成した variables ディレクトリで、 src/main.rs ファイルを開き、 その中身を以下のコードに置き換えましょう。このコードはまだコンパイルできません: ファイル名: src/main.rs fn main() { let x = 5; println!(\"The value of x is: {}\", x); // xの値は{}です x = 6; println!(\"The value of x is: {}\", x);\n} これを保存し、 cargo run コマンドでプログラムを走らせてください。次の出力に示されているようなエラーメッセージを受け取るはずです: error[E0384]: cannot assgin twice immutable variable `x` (不変変数`x`に2回代入できません) --> src/main.rs:4:5 |\n2 | let x = 5; | - first assignment to `x` | (`x`への最初の代入)\n3 | println!(\"The value of x is: {}\", x);\n4 | x = 6; | ^^^^^ cannot assign twice to immutable variable この例では、コンパイラがプログラムに潜むエラーを見つけ出す手助けをしてくれることが示されています。 コンパイルエラーは、イライラすることもあるものですが、まだプログラムにしてほしいことを安全に行えていないだけということなのです; エラーが出るからといって、あなたがいいプログラマではないという意味ではあり ません ! 経験豊富なRustaceanでも、コンパイルエラーを出すことはあります。 このエラーは、エラーの原因が 不変変数xに2回代入できない であると示しています。不変な x という変数に第2段階の値を代入しようとしたからです。 以前に不変と指定された値を変えようとした時に、コンパイルエラーが出るのは重要なことです。 なぜなら、この状況はまさしく、バグに繋がるからです。コードのある部分は、 値が変わることはないという前提のもとに処理を行い、別の部分がその値を変更していたら、 最初の部分が目論見通りに動いていない可能性があるのです。このようなバグの発生は、 事実( 訳注 :実際にプログラムを走らせた結果のことと思われる)の後には追いかけづらいものです。 特に第2のコード片が、値を 時々 しか変えない場合、尚更です。 Rustでは、値が不変であると宣言したら、本当に変わらないことをコンパイラが担保してくれます。 つまり、コードを読み書きする際に、どこでどうやって値が変化しているかを追いかける必要がなくなります。 故にコードを通して正しいことを確認するのが簡単になるのです。 しかし、可変性は時として非常に有益なこともあります。変数は、標準でのみ、不変です。つまり、 第2章のように変数名の前に mut キーワードを付けることで、可変にできるわけです。この値が変化できるようにするとともに、 mut により、未来の読者に対してコードの別の部分がこの変数の値を変える可能性を示すことで、その意図を汲ませることができるのです。 例として、 src/main.rs ファイルを以下のように書き換えてください: ファイル名: src/main.rs fn main() { let mut x = 5; println!(\"The value of x is: {}\", x); x = 6; println!(\"The value of x is: {}\", x);\n} 今、このプログラムを走らせると、以下のような出力が得られます: $ cargo run Compiling variables v0.1.0 (file:///projects/variables) Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs Running `target/debug/variables`\nThe value of x is: 5 (xの値は5です)\nThe value of x is: 6 mut キーワードが使われると、 x が束縛している値を 5 から 6 に変更できます。 変数を可変にする方が、不変変数だけがあるよりも書きやすくなるので、変数を可変にしたくなることもあるでしょう。 考えるべきトレードオフはバグの予防以外にも、いくつかあります。例えば、大きなデータ構造を使う場合などです。 インスタンスを可変にして変更できるようにする方が、いちいちインスタンスをコピーして新しくメモリ割り当てされたインスタンスを返すよりも速くなります。 小規模なデータ構造なら、新規インスタンスを生成して、もっと関数型っぽいコードを書く方が通して考えやすくなるため、 低パフォーマンスは、その簡潔性を得るのに足りうるペナルティになるかもしれません。","breadcrumbs":"普遍的なプログラミング概念 » 変数と可変性","id":"ch03-01-variables-and-mutability.html#a変数と可変性","title":"変数と可変性"},"ch03-01-variables-and-mutability.html#a変数と定数constantsの違い":{"body":"変数の値を変更できないようにするといえば、他の多くの言語も持っている別のプログラミング概念を思い浮かべるかもしれません: 定数 です。不変変数のように、定数は名前に束縛され、変更することが叶わない値のことですが、 定数と変数の間にはいくつかの違いがあります。 まず、定数には mut キーワードは使えません: 定数は標準で不変であるだけでなく、常に不変なのです。 定数は let キーワードの代わりに、 const キーワードで宣言し、値の型は 必ず 注釈しなければなりません。 型と型注釈については次のセクション、「データ型」で講義しますので、その詳細について気にする必要はありません。 ただ単に型は常に注釈しなければならないのだと思っていてください。 定数はどんなスコープでも定義できます。グローバルスコープも含めてです。なので、 いろんなところで使用される可能性のある値を定義するのに役に立ちます。 最後の違いは、定数は定数式にしかセットできないことです。関数呼び出し結果や、実行時に評価される値にはセットできません。 定数の名前が MAX_POINTS で、値が100,000にセットされた定数定義の例をご覧ください。(Rustの定数の命名規則は、 全て大文字でアンダースコアで単語区切りすることです): const MAX_POINTS: u32 = 100_000; 定数は、プログラムが走る期間、定義されたスコープ内でずっと有効です。従って、 プログラムのいろんなところで使用される可能性のあるアプリケーション空間の値を定義するのに有益な選択肢になります。 例えば、ゲームでプレイヤーが取得可能なポイントの最高値や、光速度などですね。 プログラム中で使用されるハードコードされた値に対して、定数として名前付けすることは、 コードの将来的な管理者にとって値の意味を汲むのに役に立ちます。将来、ハードコードされた値を変える必要が出た時に、 たった1箇所を変更するだけで済むようにもしてくれます。","breadcrumbs":"普遍的なプログラミング概念 » 変数と定数(constants)の違い","id":"ch03-01-variables-and-mutability.html#a変数と定数constantsの違い","title":"変数と定数(constants)の違い"},"ch03-02-data-types.html#aスカラー型":{"body":"スカラー型は、単独の値を表します。Rustには主に4つのスカラー型があります: 整数、浮動小数点数、論理値、最後に文字です。他のプログラミング言語でも、これらの型を見かけたことはあるでしょう。 Rustでの動作方法に飛び込みましょう。 整数型 整数とは、小数部分のない数値のことです。第2章で一つの整数型を使用しましたね。 u32 型です。 この型定義は、紐付けられる値が、符号なし整数(符号付き整数は u ではなく、 i で始まります)になり、 これは、32ビット分のサイズを取ります。表3-1は、Rustの組み込み整数型を表示しています。 符号付きと符号なし欄の各バリアント(例: i16 )を使用して、整数値の型を宣言することができます。 表3-1: Rustの整数型 大きさ 符号付き 符号なし 8-bit i8 u8 16-bit i16 u16 32-bit i32 u32 64-bit i64 u64 arch isize usize 各バリアントは、符号付きか符号なしかを選べ、明示的なサイズを持ちます。 符号付き と 符号なし は、 数値が正負を持つかどうかを示します。つまり、数値が符号を持つ必要があるかどうか(符号付き)、または、 絶対に正数にしかならず符号なしで表現できるかどうか(符号なし)です。これは、数値を紙に書き下すのと似ています: 符号が問題になるなら、数値はプラス記号、またはマイナス記号とともに表示されます; しかしながら、 その数値が正数であると仮定することが安全なら、符号なしで表示できるわけです。符号付き数値は、 2の補数表現で保持されます(これが何なのか確信を持てないのであれば、ネットで検索することができます。 まあ要するに、この解説は、この本の範疇外というわけです)。 各符号付きバリアントは、-(2n - 1)以上2n - 1 - 1以下の数値を保持でき、 ここで n はこのバリアントが使用するビット数です。以上から、 i8 型は-(27)から27 - 1まで、 つまり、-128から127までを保持できます。符号なしバリアントは、0以上2n - 1以下を保持できるので、 u8 型は、0から28 - 1までの値、つまり、0から255までを保持できることになります。 加えて、 isize と usize 型は、プログラムが動作しているコンピュータの種類に依存します: 64ビットアーキテクチャなら、64ビットですし、32ビットアーキテクチャなら、32ビットになります。 整数リテラル( 訳注 : リテラルとは、見たままの値ということ)は、表3-2に示すどの形式でも記述することができます。 バイトリテラルを除く数値リテラルは全て、 型接尾辞(例えば、 57u8 )と _ を見た目の区切り記号(例えば、 1_000 )に付加することができます。 表3-2: Rustの整数リテラル 数値リテラル 例 10進数 98_222 16進数 0xff 8進数 0o77 2進数 0b1111_0000 バイト ( u8 だけ) b'A' では、どの整数型を使うべきかはどう把握すればいいのでしょうか?もし確信が持てないのならば、 Rustの基準型は一般的にいい選択肢になります。整数型の基準は i32 型です: 64ビットシステム上でも、 この型が普通最速になります。 isize と usize を使う主な状況は、何らかのコレクションにアクセスすることです。 浮動小数点型 Rustにはさらに、 浮動小数点数 に対しても、2種類の基本型があり、浮動小数点数とは数値に小数点がついたもののことです。 Rustの浮動小数点型は、 f32 と f64 で、それぞれ32ビットと64ビットサイズです。基準型は f64 です。 なぜなら、現代のCPUでは、 f32 とほぼ同スピードにもかかわらず、より精度が高くなるからです。 実際に動作している浮動小数点数の例をご覧ください: ファイル名: src/main.rs fn main() { let x = 2.0; // f64 let y: f32 = 3.0; // f32\n} 浮動小数点数は、IEEE-754規格に従って表現されています。 f32 が単精度浮動小数点数、 f64 が倍精度浮動小数点数です。 数値演算 Rustにも全数値型に期待されうる標準的な数学演算が用意されています: 足し算、引き算、掛け算、割り算、余りです。 以下の例では、 let 文での各演算の使用方法をご覧になれます: ファイル名: src/main.rs fn main() { // 足し算 let sum = 5 + 10; // 引き算 let difference = 95.5 - 4.3; // 掛け算 let product = 4 * 30; // 割り算 let quotient = 56.7 / 32.2; // 余り let remainder = 43 % 5;\n} これらの文の各式は、数学演算子を使用しており、一つの値に評価され、そして、変数に束縛されます。 付録BにRustで使える演算子の一覧が載っています。 論理値型 他の多くの言語同様、Rustの論理値型も取りうる値は二つしかありません: true と false です。 Rustの論理値型は、 bool と指定されます。 例です: ファイル名: src/main.rs fn main() { let t = true; let f: bool = false; // 明示的型注釈付きで\n} 論理値を使う主な手段は、条件式です。例えば、 if 式などですね。 if 式のRustでの動作方法については、 「制御フロー」節で講義します。 文字型 ここまで、数値型のみ扱ってきましたが、Rustには文字も用意されています。Rustの char 型は、 言語の最も基本的なアルファベット型であり、以下のコードでその使用方法の一例を見ることができます。 ( char は、ダブルクォーテーションマークを使用する文字列に対して、シングルクォートで指定されることに注意してください。) ファイル名: src/main.rs fn main() { let c = 'z'; let z = 'ℤ'; let heart_eyed_cat = '😻'; //ハート目の猫\n} Rustの char 型は、ユニコードのスカラー値を表します。これはつまり、アスキーよりもずっとたくさんのものを表せるということです。 アクセント文字; 中国語、日本語、韓国語文字; 絵文字; ゼロ幅スペースは、全てRustでは、有効な char 型になります。ユニコードスカラー値は、 U+0000 から U+D7FF までと U+E000 から U+10FFFF までの範囲になります。 ところが、「文字」は実はユニコードの概念ではないので、文字とは何かという人間としての直観は、 Rustにおける char 値が何かとは合致しない可能性があります。この話題については、第8章の「文字列」で詳しく議論しましょう。","breadcrumbs":"普遍的なプログラミング概念 » スカラー型","id":"ch03-02-data-types.html#aスカラー型","title":"スカラー型"},"ch03-02-data-types.html#aデータ型":{"body":"Rustにおける値は全て、何らかの データ型 になり、コンパイラがどんなデータが指定されているか知れるので、 そのデータの取り扱い方も把握できるというわけです。2種のデータ型のサブセットを見ましょう: スカラー型と複合型です。 Rustは 静的型付き 言語であることを弁えておいてください。つまり、 コンパイル時に全ての変数の型が判明している必要があるということです。コンパイラは通常、値と使用方法に基づいて、 使用したい型を推論してくれます。複数の型が推論される可能性がある場合、例えば、 第2章の「予想と秘密の数字を比較する」節で parse メソッドを使って String 型を数値型に変換した時のように、 複数の型が可能な場合には、型注釈をつけなければいけません。以下のようにですね: let guess: u32 = \"42\".parse().expect(\"Not a number!\"); // 数字ではありません! ここで型注釈を付けなければ、コンパイラは以下のエラーを表示し、これは可能性のある型のうち、 どの型を使用したいのかを知るのに、コンパイラがプログラマからもっと情報を得る必要があることを意味します: error[E0282]: type annotations needed (型注釈が必要です) --> src/main.rs:2:9 |\n2 | let guess = \"42\".parse().expect(\"Not a number!\"); | ^^^^^ cannot infer type for `_` | (`_`の型が推論できません) | = note: type annotations or generic parameter binding required (注釈: 型注釈、またはジェネリクス引数束縛が必要です) 他のデータ型についても、様々な型注釈を目にすることになるでしょう。","breadcrumbs":"普遍的なプログラミング概念 » データ型","id":"ch03-02-data-types.html#aデータ型","title":"データ型"},"ch03-02-data-types.html#a複合型":{"body":"複合型 により、複数の値を一つの型にまとめることができます。Rustには、 2種類の基本的な複合型があります: タプルと配列です。 タプル型 タプルは、複数の型の何らかの値を一つの複合型にまとめ上げる一般的な手段です。 タプルは、丸かっこの中にカンマ区切りの値リストを書くことで生成します。タプルの位置ごとに型があり、 タプル内の値はそれぞれ全てが同じ型である必要はありません。今回の例では、型注釈をあえて追加しました: ファイル名: src/main.rs fn main() { let tup: (i32, f64, u8) = (500, 6.4, 1);\n} 変数 tup は、タプル全体に束縛されています。なぜなら、タプルは、一つの複合要素と考えられるからです。 タプルから個々の値を取り出すには、パターンマッチングを使用して分解することができます。以下のように: ファイル名: src/main.rs fn main() { let tup = (500, 6.4, 1); let (x, y, z) = tup; println!(\"The value of y is: {}\", y);\n} このプログラムは、まずタプルを生成し、それを変数 tup に束縛しています。 それから let とパターンを使って tup 変数の中身を3つの個別の変数( x 、 y 、 z ですね)に変換しています。 この過程は、 分配 と呼ばれます。単独のタプルを破壊して三分割しているからです。最後に、 プログラムは y 変数の値を出力し、 6.4 と表示されます。 パターンマッチングを通しての分配の他にも、アクセスしたい値の番号をピリオド( . )に続けて書くことで、 タプルの要素に直接アクセスすることもできます。例です: ファイル名: src/main.rs fn main() { let x: (i32, f64, u8) = (500, 6.4, 1); let five_hundred = x.0; let six_point_four = x.1; let one = x.2;\n} このプログラムは、新しいタプル x を作成し、添え字アクセスで各要素に対して新しい変数も作成しています。 多くのプログラミング言語同様、タプルの最初の添え字は0です。 配列型 配列 によっても、複数の値のコレクションを得ることができます。タプルと異なり、配列の全要素は、 同じ型でなければなりません。Rustの配列は、他の言語と異なっています。Rustの配列は、 固定長なのです: 一度宣言されたら、サイズを伸ばすことも縮めることもできません。 Rustでは、配列に入れる要素は、角かっこ内にカンマ区切りリストとして記述します: ファイル名: src/main.rs fn main() { let a = [1, 2, 3, 4, 5];\n} 配列は、ヒープよりもスタック(スタックとヒープについては第4章で詳(つまび)らかに議論します)にデータのメモリを確保したい時、 または、常に固定長の要素があることを確認したい時に有効です。 ただ、配列は、ベクタ型ほど柔軟ではありません。ベクタは、標準ライブラリによって提供されている配列と似たようなコレクション型で、 こちらは、サイズを伸縮させることが できます 。配列とベクタ型、どちらを使うべきか確信が持てない時は、 おそらくベクタ型を使うべきです。第8章でベクタについて詳細に議論します。 ベクタ型よりも配列を使いたくなるかもしれない例は、1年の月の名前を扱うプログラムです。そのようなプログラムで、 月を追加したり削除したりすることまずないので、配列を使用できます。常に12個要素があることもわかってますからね: let months = [\"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"]; 配列の要素にアクセスする 配列は、スタック上に確保される一塊のメモリです。添え字によって、 配列の要素にこのようにアクセスすることができます: ファイル名: src/main.rs fn main() { let a = [1, 2, 3, 4, 5]; let first = a[0]; let second = a[1];\n} この例では、 first という名前の変数には 1 という値が格納されます。配列の [0] 番目にある値が、 それだからですね。 second という名前の変数には、配列の [1] 番目の値 2 が格納されます。 配列要素への無効なアクセス 配列の終端を越えて要素にアクセスしようとしたら、どうなるでしょうか? 先ほどの例を以下のように変えたとすると、コンパイルは通りますが、実行するとエラーで終了します: ファイル名: src/main.rs fn main() { let a = [1, 2, 3, 4, 5]; let index = 10; let element = a[index]; println!(\"The value of element is: {}\", element); // 要素の値は{}です\n} このコードを cargo run で走らせると、以下のような結果になります: $ cargo run Compiling arrays v0.1.0 (file:///projects/arrays) Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs Running `target/debug/arrays`\nthread '
    ' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:6\nスレッド'
    'は'範囲外アクセス: 長さは5ですが、添え字は10でした', src/main.rs:6\nでパニックしました\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. コンパイルでは何もエラーが出なかったものの、プログラムは 実行時 エラーに陥り、 正常終了しませんでした。要素に添え字アクセスを試みると、言語は、 指定されたその添え字が配列長よりも小さいかを確認してくれます。添え字が配列長よりも大きければ、言語は パニック します。 パニックとは、プログラムがエラーで終了したことを表すRust用語です。 これは、実際に稼働しているRustの安全機構の最初の例になります。低レベル言語の多くでは、 この種のチェックは行われないため、間違った添え字を与えると、無効なメモリにアクセスできてしまいます。 Rustでは、メモリアクセスを許可し、処理を継続する代わりに即座にプログラムを終了することで、 この種のエラーからプログラマを保護しています。Rustのエラー処理については、第9章でもっと議論します。","breadcrumbs":"普遍的なプログラミング概念 » 複合型","id":"ch03-02-data-types.html#a複合型","title":"複合型"},"ch03-03-how-functions-work.html#a戻り値のある関数":{"body":"関数は、それを呼び出したコードに値を返すことができます。戻り値に名前を付けはしませんが、 矢印( -> )の後に型を書いて確かに宣言します。Rustでは、関数の戻り値は、関数本体ブロックの最後の式の値と同義です。 return キーワードで関数から早期リターンし、値を指定することもできますが、多くの関数は最後の式を暗黙的に返します。 こちらが、値を返す関数の例です: ファイル名: src/main.rs fn five() -> i32 { 5\n} fn main() { let x = five(); println!(\"The value of x is: {}\", x);\n} five 関数内には、関数呼び出しもマクロ呼び出しも、 let 文でさえ存在しません。数字の5が単独であるだけです。 これは、Rustにおいて、完璧に問題ない関数です。関数の戻り値型が -> i32 と指定されていることにも注目してください。 このコードを実行してみましょう; 出力はこんな感じになるはずです: $ cargo run Compiling functions v0.1.0 (file:///projects/functions) Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs Running `target/debug/functions`\nThe value of x is: 5 five 内の 5 が関数の戻り値です。だから、戻り値型が i32 なのです。これについてもっと深く考察しましょう。 重要な箇所は2つあります: まず、 let x = five() という行は、関数の戻り値を使って変数を初期化していることを示しています。 関数 five は 5 を返すので、この行は以下のように書くのと同義です: let x = 5; 2番目に、 five 関数は仮引数をもたず、戻り値型を定義していますが、関数本体はセミコロンなしの 5 単独です。 なぜなら、これが返したい値になる式だからです。 もう一つ別の例を見ましょう: ファイル名: src/main.rs fn main() { let x = plus_one(5); println!(\"The value of x is: {}\", x);\n} fn plus_one(x: i32) -> i32 { x + 1\n} このコードを走らせると、 The value of x is: 6 と出力されるでしょう。しかし、 x + 1 を含む行の終端にセミコロンを付けて、式から文に変えたら、エラーになるでしょう: ファイル名: src/main.rs fn main() { let x = plus_one(5); println!(\"The value of x is: {}\", x);\n} fn plus_one(x: i32) -> i32 { x + 1;\n} このコードを実行すると、以下のようにエラーが出ます: error[E0308]: mismatched types (型が合いません) --> src/main.rs:7:28 |\n7 | fn plus_one(x: i32) -> i32 { | ____________________________^\n8 | | x + 1; | | - help: consider removing this semicolon\n9 | | } | |_^ expected i32, found () | (i32を予期したのに、()型が見つかりました) | = note: expected type `i32` found type `()` メインのエラーメッセージである「型が合いません」でこのコードの根本的な問題が明らかになるでしょう。 関数 plus_one の定義では、 i32 型を返すと言っているのに、文は値に評価されないからです。このことは、 () 、つまり空のタプルとして表現されています。それゆえに、何も戻り値がなく、これが関数定義と矛盾するので、 結果としてエラーになるわけです。この出力内で、コンパイラは問題を修正する手助けになりそうなメッセージも出していますね: セミコロンを削除するよう提言しています。そして、そうすれば、エラーは直るわけです。","breadcrumbs":"普遍的なプログラミング概念 » 戻り値のある関数","id":"ch03-03-how-functions-work.html#a戻り値のある関数","title":"戻り値のある関数"},"ch03-03-how-functions-work.html#a関数":{"body":"関数は、Rustのコードにおいてよく見かける存在です。既に、言語において最も重要な関数のうちの一つを目撃していますね: そう、 main 関数です。これは、多くのプログラムのエントリーポイント( 訳注 : プログラム実行時に最初に走る関数のこと)になります。 fn キーワードもすでに見かけましたね。これによって新しい関数を宣言することができます。 Rustの関数と変数の命名規則は、 スネークケース ( 訳注 : some_variableのような命名規則)を使うのが慣例です。 スネークケースとは、全文字を小文字にし、単語区切りにアンダースコアを使うことです。 以下のプログラムで、サンプルの関数定義をご覧ください: ファイル名: src/main.rs fn main() { println!(\"Hello, world!\"); another_function();\n} fn another_function() { println!(\"Another function.\"); // 別の関数\n} Rustにおいて関数定義は、 fn キーワードで始まり、関数名の後に丸かっこの組が続きます。 波かっこが、コンパイラに関数本体の開始と終了の位置を伝えます。 定義した関数は、名前に丸かっこの組を続けることで呼び出すことができます。 another_function 関数がプログラム内で定義されているので、 main 関数内から呼び出すことができるわけです。 ソースコード中で another_function を main 関数の 後 に定義していることに注目してください; 勿論、main関数の前に定義することもできます。コンパイラは、関数がどこで定義されているかは気にしません。 どこかで定義されていることのみ気にします。 functions という名前の新しいバイナリ生成プロジェクトを始めて、関数についてさらに深く探究していきましょう。 another_function の例を src/main.rs ファイルに配置して、走らせてください。 以下のような出力が得られるはずです: $ cargo run Compiling functions v0.1.0 (file:///projects/functions) Finished dev [unoptimized + debuginfo] target(s) in 0.28 secs Running `target/debug/functions`\nHello, world!\nAnother function. 行出力は、 main 関数内に書かれた順序で実行されています。最初に\"Hello, world\"メッセージが出て、 それから another_function が呼ばれて、こちらのメッセージが出力されています。","breadcrumbs":"普遍的なプログラミング概念 » 関数","id":"ch03-03-how-functions-work.html#a関数","title":"関数"},"ch03-03-how-functions-work.html#a関数の引数":{"body":"関数は、引数を持つようにも定義できます。引数とは、関数シグニチャの一部になる特別な変数のことです。 関数に引数があると、引数の位置に実際の値を与えることができます。技術的にはこの実際の値は 実引数 と呼ばれますが、普段の会話では、仮引数(\"parameter\")と実引数(\"argument\")を関数定義の変数と関数呼び出し時に渡す実際の値、 両方の意味に区別なく使います( 訳注 : 日本語では、特別区別する意図がない限り、どちらも単に引数と呼ぶことが多いでしょう)。 以下の書き直した another_function では、Rustの仮引数がどのようなものかを示しています: ファイル名: src/main.rs fn main() { another_function(5);\n} fn another_function(x: i32) { println!(\"The value of x is: {}\", x); // xの値は{}です\n} このプログラムを走らせてみてください; 以下のような出力が得られるはずです: $ cargo run Compiling functions v0.1.0 (file:///projects/functions) Finished dev [unoptimized + debuginfo] target(s) in 1.21 secs Running `target/debug/functions`\nThe value of x is: 5 another_function の宣言には、 x という名前の仮引数があります。 x の型は、 i32 と指定されています。値 5 が another_function に渡されると、 println! マクロにより、 フォーマット文字列中の1組の波かっこがあった位置に値 5 が出力されます。 関数シグニチャにおいて、各仮引数の型を宣言しなければ なりません 。これは、Rustの設計において、 意図的な判断です: 関数定義で型注釈が必要不可欠ということは、コンパイラがその意図するところを推し量るのに、 プログラマがコードの他の箇所で使用する必要がないということを意味します。 関数に複数の仮引数を持たせたいときは、仮引数定義をカンマで区切ってください。 こんな感じです: ファイル名: src/main.rs fn main() { another_function(5, 6);\n} fn another_function(x: i32, y: i32) { println!(\"The value of x is: {}\", x); println!(\"The value of y is: {}\", y);\n} この例では、2引数の関数を生成しています。そして、引数はどちらも i32 型です。それからこの関数は、 仮引数の値を両方出力します。関数引数は、全てが同じ型である必要はありません。今回は、 偶然同じになっただけです。 このコードを走らせてみましょう。今、 function プロジェクトの src/main.rs ファイルに記載されているプログラムを先ほどの例と置き換えて、 cargo run で走らせてください: $ cargo run Compiling functions v0.1.0 (file:///projects/functions) Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs Running `target/debug/functions`\nThe value of x is: 5\nThe value of y is: 6 x に対して値 5 、 y に対して値 6 を渡して関数を呼び出したので、この二つの文字列は、 この値で出力されました。","breadcrumbs":"普遍的なプログラミング概念 » 関数の引数","id":"ch03-03-how-functions-work.html#a関数の引数","title":"関数の引数"},"ch03-03-how-functions-work.html#a関数本体は文と式を含む":{"body":"関数本体は、文が並び、最後に式を置くか文を置くという形で形成されます。現在までには、 式で終わらない関数だけを見てきたわけですが、式が文の一部になっているものなら見かけましたね。Rustは、式指向言語なので、 これは理解しておくべき重要な差異になります。他の言語にこの差異はありませんので、文と式がなんなのかと、 その違いが関数本体にどんな影響を与えるかを見ていきましょう。 実のところ、もう文と式は使っています。 文 とは、なんらかの動作をして値を返さない命令です。 式 は結果値に評価されます。ちょっと例を眺めてみましょう。 let キーワードを使用して変数を生成し、値を代入することは文になります。 リスト3-1で let y = 6; は文です。 ファイル名: src/main.rs fn main() { let y = 6;\n} リスト3-1: 1文を含む main 関数宣言 関数定義も文になります。つまり、先の例は全体としても文になるわけです。 文は値を返しません。故に、 let 文を他の変数に代入することはできません。 以下のコードではそれを試みていますが、エラーになります: ファイル名: src/main.rs fn main() { let x = (let y = 6);\n} このプログラムを実行すると、以下のようなエラーが出るでしょう: $ cargo run Compiling functions v0.1.0 (file:///projects/functions)\nerror: expected expression, found statement (`let`)\n(エラー: 式を予期しましたが、文が見つかりました (`let`)) --> src/main.rs:2:14 |\n2 | let x = (let y = 6); | ^^^ | = note: variable declaration using `let` is a statement (注釈: `let`を使う変数宣言は、文です) この let y = 6 という文は値を返さないので、 x に束縛するものがないわけです。これは、 CやRubyなどの言語とは異なる動作です。CやRubyでは、代入は代入値を返します。これらの言語では、 x = y = 6 と書いて、 x も y も値6になるようにできるのですが、Rustにおいては、 そうは問屋が卸さないわけです。 式は何かに評価され、これからあなたが書くRustコードの多くを構成します。 簡単な数学演算( 5 + 6 など)を思い浮かべましょう。この例は、値 11 に評価される式です。式は文の一部になりえます: リスト3-1において、 let y = 6 という文の 6 は値 6 に評価される式です。関数呼び出しも式です。マクロ呼び出しも式です。 新しいスコープを作る際に使用するブロック( {} )も式です: ファイル名: src/main.rs fn main() { let x = 5; let y = { let x = 3; x + 1 }; println!(\"The value of y is: {}\", y);\n} 以下の式: { let x = 3; x + 1\n} は今回の場合、 4 に評価されるブロックです。その値が、 let 文の一部として y に束縛されます。 今まで見かけてきた行と異なり、文末にセミコロンがついていない x + 1 の行に気をつけてください。 式は終端にセミコロンを含みません。式の終端にセミコロンを付けたら、文に変えてしまいます。そして、文は値を返しません。 次に関数の戻り値や式を見ていく際にこのことを肝に銘じておいてください。","breadcrumbs":"普遍的なプログラミング概念 » 関数本体は、文と式を含む","id":"ch03-03-how-functions-work.html#a関数本体は文と式を含む","title":"関数本体は、文と式を含む"},"ch03-04-comments.html#aコメント":{"body":"全プログラマは、自分のコードがわかりやすくなるよう努めますが、時として追加の説明が許されることもあります。 このような場合、プログラマは注釈または コメント をソースコードに残し、コメントをコンパイラは無視しますが、 ソースコードを読む人間には有益なものと思えるでしょう。 こちらが単純なコメントです: // hello, world Rustでは、コメントは2連スラッシュで始め、行の終わりまで続きます。コメントが複数行にまたがる場合、 各行に // を含める必要があります。こんな感じに: // So we’re doing something complicated here, long enough that we need\n// multiple lines of comments to do it! Whew! Hopefully, this comment will\n// explain what’s going on.\n// ここで何か複雑なことをしていて、長すぎるから複数行のコメントが必要なんだ。\n// ふう!願わくば、このコメントで何が起きているか説明されていると嬉しい。 コメントは、コードが書かれた行の末尾にも配置することができます: Filename: src/main.rs fn main() { let lucky_number = 7; // I’m feeling lucky today(今日はラッキーな気がするよ)\n} しかし、こちらの形式のコメントの方が見かける機会は多いでしょう。注釈しようとしているコードの1行上に書く形式です: ファイル名: src/main.rs fn main() { // I’m feeling lucky today // 今日はラッキーな気がするよ let lucky_number = 7;\n} Rustには他の種類のコメント、ドキュメントコメントもあり、それについては第14章で議論します。","breadcrumbs":"普遍的なプログラミング概念 » コメント","id":"ch03-04-comments.html#aコメント","title":"コメント"},"ch03-05-control-flow.html#aまとめ":{"body":"やりましたね!結構長い章でした: 変数、スカラー値と複合データ型、関数、コメント、 if 式、そして、ループについて学びました! この章で議論した概念について経験を積みたいのであれば、以下のことをするプログラムを組んでみてください: 温度を華氏と摂氏で変換する。 フィボナッチ数列のn番目を生成する。 クリスマスキャロルの定番、\"The Twelve Days of Christmas\"の歌詞を、 曲の反復性を利用して出力する。 次に進む準備ができたら、他の言語にはあまり存在 しない Rustの概念について話しましょう: 所有権です。","breadcrumbs":"普遍的なプログラミング概念 » まとめ","id":"ch03-05-control-flow.html#aまとめ","title":"まとめ"},"ch03-05-control-flow.html#aフロー制御":{"body":"条件が真かどうかによってコードを走らせるかどうかを決定したり、 条件が真の間繰り返しコードを走らせるか決定したりすることは、多くのプログラミング言語において、基本的な構成ブロックです。 Rustコードの実行フローを制御する最も一般的な文法要素は、 if 式とループです。","breadcrumbs":"普遍的なプログラミング概念 » フロー制御","id":"ch03-05-control-flow.html#aフロー制御","title":"フロー制御"},"ch03-05-control-flow.html#aループでの繰り返し":{"body":"一連のコードを1回以上実行できると、しばしば役に立ちます。この作業用に、 Rustにはいくつかの ループ が用意されています。ループは、本体内のコードを最後まで実行し、 直後にまた最初から処理を開始します。 ループを試してみるのに、 loops という名の新プロジェクトを作りましょう。 Rustには3種類のループが存在します: loop と while と for です。それぞれ試してみましょう。 loop でコードを繰り返す loop キーワードを使用すると、同じコードを何回も何回も永遠に、明示的にやめさせるまで実行します。 例として、 loops ディレクトリの src/main.rs ファイルを以下のような感じに書き換えてください: ファイル名: src/main.rs fn main() { loop { println!(\"again!\"); // また }\n} このプログラムを実行すると、プログラムを手動で止めるまで、何度も何度も続けて again! と出力するでしょう。 ほとんどの端末でctrl-cというショートカットが使え、 永久ループに囚われてしまったプログラムを終了させられます。試しにやってみましょう: $ cargo run Compiling loops v0.1.0 (file:///projects/loops) Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs Running `target/debug/loops`\nagain!\nagain!\nagain!\nagain!\n^Cagain! ^C という記号が出た場所が、ctrl-cを押した場所です。 ^C の後には again! と表示されたり、 されなかったりします。ストップシグナルをコードが受け取った時にループのどこにいたかによります。 幸いなことに、Rustにはループを抜け出す別のより信頼できる手段があります。 ループ内に break キーワードを配置することで、プログラムに実行を終了すべきタイミングを教えることができます。 第2章の「正しい予想をした後に終了する」節の数当てゲーム内でこれをして、ユーザが予想を的中させ、 ゲームに勝った時にプログラムを終了させたことを思い出してください。 while で条件付きループ プログラムにとってループ内で条件式を評価できると、有益なことがしばしばあります。条件が真の間、 ループが走るわけです。条件が真でなくなった時にプログラムは break を呼び出し、ループを終了します。 このタイプのループは、 loop 、 if 、 else 、 break を組み合わせることでも実装できます; お望みなら、プログラムで今、試してみるのもいいでしょう。 しかし、このパターンは頻出するので、Rustにはそれ用の文法要素が用意されていて、 while ループと呼ばれます。 リスト3-3は、 while を使用しています: プログラムは3回ループし、それぞれカウントダウンします。 それから、ループ後に別のメッセージを表示して終了します: ファイル名: src/main.rs fn main() { let mut number = 3; while number != 0 { println!(\"{}!\", number); number = number - 1; } // 発射! println!(\"LIFTOFF!!!\");\n} リスト3-3: 条件が真の間、コードを走らせる while ループを使用する この文法要素により、 loop 、 if 、 else 、 break を使った時に必要になるネストがなくなり、 より明確になります。条件が真の間、コードは実行されます; そうでなければ、ループを抜けます. for でコレクションを覗き見る while 要素を使って配列などのコレクションの要素を覗き見ることができます。例えば、リスト3-4を見ましょう。 ファイル名: src/main.rs fn main() { let a = [10, 20, 30, 40, 50]; let mut index = 0; while index < 5 { // 値は{}です println!(\"the value is: {}\", a[index]); index = index + 1; }\n} リスト3-4: while ループでコレクションの各要素を覗き見る ここで、コードは配列の要素を順番にカウントアップして覗いています。番号0から始まり、 配列の最終番号に到達するまでループします(つまり、 index < 5 が真でなくなる時です)。 このコードを走らせると、配列内の全要素が出力されます: $ cargo run Compiling loops v0.1.0 (file:///projects/loops) Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs Running `target/debug/loops`\nthe value is: 10\nthe value is: 20\nthe value is: 30\nthe value is: 40\nthe value is: 50 予想通り、配列の5つの要素が全てターミナルに出力されています。 index 変数の値はどこかで 5 という値になるものの、 配列から6番目の値を拾おうとする前にループは実行を終了します。 しかし、このアプローチは間違いが発生しやすいです; 添え字の長さが間違っていれば、 プログラムはパニックしてしまいます。また遅いです。 コンパイラが実行時にループの各回ごとに境界値チェックを行うようなコードを追加するからです。 より効率的な対立案として、 for ループを使ってコレクションの各アイテムに対してコードを実行することができます。 for ループはリスト3-5のこんな見た目です。 ファイル名: src/main.rs fn main() { let a = [10, 20, 30, 40, 50]; for element in a.iter() { // 値は{}です println!(\"the value is: {}\", element); }\n} リスト3-4: for ループを使ってコレクションの各要素を覗き見る このコードを走らせたら、リスト3-4と同じ出力が得られるでしょう。より重要なのは、 コードの安全性を向上させ、配列の終端を超えてアクセスしたり、 終端に届く前にループを終えてアイテムを見逃してしまったりするバグの可能性を完全に排除したことです。 例えば、リスト3-4のコードで、 a 配列からアイテムを1つ削除したのに、条件式を while index < 4 にするのを忘れていたら、 コードはパニックします。 for ループを使っていれば、配列の要素数を変えても、 他のコードをいじることを覚えておく必要はなくなるわけです。 for ループのこの安全性と簡潔性により、Rustで使用頻度の最も高いループになっています。 リスト3-3で while ループを使ったカウントダウンサンプルのように、一定の回数、同じコードを実行したいような状況であっても、 多くのRustaceanは、 for ループを使うでしょう。どうやってやるかといえば、 Range 型を使うのです。Range型は、標準ライブラリで提供される片方の数字から始まって、 もう片方の数字未満の数値を順番に生成する型です。 for ループと、まだ話していない別のメソッド rev を使って範囲を逆順にしたカウントダウンはこうなります: ファイル名: src/main.rs fn main() { for number in (1..4).rev() { println!(\"{}!\", number); } println!(\"LIFTOFF!!!\");\n} こちらのコードの方が少しいいでしょう?","breadcrumbs":"普遍的なプログラミング概念 » ループでの繰り返し","id":"ch03-05-control-flow.html#aループでの繰り返し","title":"ループでの繰り返し"},"ch03-05-control-flow.html#if-式":{"body":"if式によって、条件に依存して枝分かれをさせることができます。条件を与え、以下のように宣言します。 「もし条件が合ったら、この一連のコードを実行しろ。条件に合わなければ、この一連のコードは実行するな」と。 projects ディレクトリに branches という名のプロジェクトを作って if 式について掘り下げていきましょう。 src/main.rs ファイルに、以下のように入力してください: ファイル名: src/main.rs fn main() { let number = 3; if number < 5 { println!(\"condition was true\"); // 条件は真でした } else { println!(\"condition was false\"); // 条件は偽でした }\n} if 式は全て、キーワードの if から始め、条件式を続けます。今回の場合、 条件式は変数 number が5未満の値になっているかどうかをチェックします。 条件が真の時に実行したい一連のコードを条件式の直後に波かっこで包んで配置します。 if 式の条件式と紐付けられる一連のコードは、 時として アーム と呼ばれることがあります。 第2章の「予想と秘密の数字を比較する」の節で議論した match 式のアームと同じです。 オプションとして、 else 式を含むこともでき(ここではそうしています)、これによりプログラムは、 条件式が偽になった時に実行するコードを与えられることになります。仮に、 else 式を与えずに条件式が偽になったら、 プログラムは単に if ブロックを飛ばして次のコードを実行しにいきます。 このコードを走らせてみましょう; 以下のような出力を目の当たりにするはずです: $ cargo run Compiling branches v0.1.0 (file:///projects/branches) Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs Running `target/debug/branches`\ncondition was true number の値を条件が false になるような値に変更してどうなるか確かめてみましょう: let number = 7; 再度プログラムを実行して、出力に注目してください: $ cargo run Compiling branches v0.1.0 (file:///projects/branches) Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs Running `target/debug/branches`\ncondition was false このコード内の条件式は、 bool 型で なければならない ことにも触れる価値があります。 条件式が、 bool 型でない時は、エラーになります。例えば、試しに以下のコードを実行してみてください: ファイル名: src/main.rs fn main() { let number = 3; if number { println!(\"number was three\"); // 数値は3です }\n} 今回、 if の条件式は 3 という値に評価され、コンパイラがエラーを投げます: error[E0308]: mismatched types (型が合いません) --> src/main.rs:4:8 |\n4 | if number { | ^^^^^^ expected bool, found integral variable | (bool型を予期したのに、整数変数が見つかりました) | = note: expected type `bool` found type `{integer}` このエラーは、コンパイラは bool 型を予期していたのに、整数だったことを示唆しています。 RubyやJavaScriptなどの言語とは異なり、Rustでは、論理値以外の値が、自動的に論理値に変換されることはありません。 明示し、必ず if には条件式として、 論理値 を与えなければなりません。 例えば、数値が 0 以外の時だけ if のコードを走らせたいなら、以下のように if 式を変更することができます: ファイル名: src/main.rs fn main() { let number = 3; if number != 0 { println!(\"number was something other than zero\"); // 数値は0以外の何かです }\n} このコードを実行したら、 number was something other than zero と表示されるでしょう。 else if で複数の条件を扱う if と else を組み合わせて else if 式にすることで複数の条件を持たせることもできます。例です: ファイル名: src/main.rs fn main() { let number = 6; if number % 4 == 0 { // 数値は4で割り切れます println!(\"number is divisible by 4\"); } else if number % 3 == 0 { // 数値は3で割り切れます println!(\"number is divisible by 3\"); } else if number % 2 == 0 { // 数値は2で割り切れます println!(\"number is divisible by 2\"); } else { // 数値は4、3、2で割り切れません println!(\"number is not divisible by 4, 3, or 2\"); }\n} このプログラムには、通り道が4つあります。実行後、以下のような出力を目の当たりにするはずです: $ cargo run Compiling branches v0.1.0 (file:///projects/branches) Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs Running `target/debug/branches`\nnumber is divisible by 3 このプログラムを実行すると、 if 式が順番に吟味され、最初に条件が真になった本体が実行されます。 6は2で割り切れるものの、 number is devisible by 2 や、 else ブロックの number is not divisible by 4, 3, or 2 という出力はされないことに注目してください。 それは、Rustが最初の真条件のブロックのみを実行し、 条件に合ったものが見つかったら、残りはチェックすらしないからです。 else if 式を使いすぎると、コードがめちゃくちゃになってしまうので、1つ以上あるなら、 コードをリファクタリングしたくなるかもしれません。これらのケースに有用な match と呼ばれる、 強力なRustの枝分かれ文法要素については第6章で解説します。 let 文内で if 式を使う if は式なので、 let 文の右辺に持ってくることができます。リスト3-2のようにですね。 ファイル名: src/main.rs fn main() { let condition = true; let number = if condition { 5 } else { 6 }; // numberの値は、{}です println!(\"The value of number is: {}\", number);\n} リスト3-2: if 式の結果を変数に代入する この number 変数は、 if 式の結果に基づいた値に束縛されます。このコードを走らせてどうなるか確かめてください: $ cargo run Compiling branches v0.1.0 (file:///projects/branches) Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs Running `target/debug/branches`\nThe value of number is: 5 一連のコードは、そのうちの最後の式に評価され、数値はそれ単独でも式になることを思い出してください。 今回の場合、この if 式全体の値は、どのブロックのコードが実行されるかに基づきます。これはつまり、 if の各アームの結果になる可能性がある値は、同じ型でなければならないということになります; リスト3-2で、 if アームも else アームも結果は、 i32 の整数でした。以下の例のように、 型が合わない時には、エラーになるでしょう: ファイル名: src/main.rs fn main() { let condition = true; let number = if condition { 5 } else { \"six\" }; println!(\"The value of number is: {}\", number);\n} このコードをコンパイルしようとすると、エラーになります。 if と else アームは互換性のない値の型になり、 コンパイラがプログラム内で問題の見つかった箇所をスバリ指摘してくれます: error[E0308]: if and else have incompatible types (ifとelseの型に互換性がありません) --> src/main.rs:4:18 |\n4 | let number = if condition { | __________________^\n5 | | 5\n6 | | } else {\n7 | | \"six\"\n8 | | }; | |_____^ expected integral variable, found &str | (整数変数を予期しましたが、&strが見つかりました) | = note: expected type `{integer}` found type `&str` if ブロックの式は整数に評価され、 else ブロックの式は文字列に評価されます。これでは動作しません。 変数は単独の型でなければならないからです。コンパイラは、コンパイル時に number 変数の型を確実に把握する必要があるため、 コンパイル時に number が使われている箇所全部で型が有効かどうか検査することができるのです。 number の型が実行時にしか決まらないのであれば、コンパイラはそれを実行することができなくなってしまいます; どの変数に対しても、架空の複数の型があることを追いかけなければならないのであれば、コンパイラはより複雑になり、 コードに対して行える保証が少なくなってしまうでしょう。","breadcrumbs":"普遍的なプログラミング概念 » if 式","id":"ch03-05-control-flow.html#if-式","title":"if 式"},"ch04-00-understanding-ownership.html#a所有権を理解する":{"body":"所有権はRustの最もユニークな機能であり、これのおかげでガベージコレクタなしで安全性担保を行うことができるのです。 故に、Rustにおいて、所有権がどう動作するのかを理解するのは重要です。この章では、所有権以外にも、関連する機能を いくつか話していきます: 借用、スライス、そして、コンパイラがデータをメモリにどう配置するかです。","breadcrumbs":"所有権を理解する","id":"ch04-00-understanding-ownership.html#a所有権を理解する","title":"所有権を理解する"},"ch04-01-what-is-ownership.html#aスタックとヒープ":{"body":"多くのプログラミング言語において、スタックとヒープについて考える機会はそう多くないでしょう。 しかし、Rustのようなシステムプログラミング言語においては、値がスタックに積まれるかヒープに置かれるかは、 言語の振る舞い方や、特定の決断を下す理由などに影響以上のものを与えるのです。 この章の後半でスタックとヒープを交えて所有権の一部が解説されるので、ここでちょっと予行演習をしておきましょう。 スタックもヒープも、実行時にコードが使用できるメモリの一部になりますが、異なる手段で構成されています。 スタックは、得た順番に値を並べ、逆の順で値を取り除いていきます。これは、 last in, first out ( 訳注 : あえて日本語にするなら、「最後に入れたものが最初に出てくる」といったところでしょうか)と呼ばれます。 お皿の山を思い浮かべてください: お皿を追加する時には、山の一番上に置き、お皿が必要になったら、一番上から1枚を取り去りますよね。 途中や一番下に追加したり、取り除いたりすることもできません。データを追加することは、 スタックにpushする といい、データを取り除くことは、 スタックからpopする と表現します( 訳注 : 日本語では単純に英語をそのまま活用してプッシュ、ポップと表現するでしょう)。 データへのアクセス方法のおかげで、スタックは高速です: 新しいデータを置いたり、 データを取得する場所を探す必要が絶対にないわけです。というのも、その場所は常に一番上だからですね。 スタックを高速にする特性は他にもあり、それはスタック上のデータは全て既知の固定サイズでなければならないということです。 コンパイル時にサイズがわからなかったり、サイズが可変のデータについては、代わりにヒープに格納することができます。 ヒープは、もっとごちゃごちゃしています: ヒープにデータを置く時、あるサイズのスペースを求めます。 OSはヒープ上に十分な大きさの空の領域を見つけ、使用中にし、 ポインタ を返します。ポインタとは、その場所へのアドレスです。 この過程は、 ヒープに領域を確保する(allocating on the heap) と呼ばれ、時としてそのフレーズを単に allocateする などと省略したりします。 ( 訳注 : こちらもこなれた日本語訳はないでしょう。allocateは「メモリを確保する」と訳したいところですが) スタックに値を積むことは、メモリ確保とは考えられません。ポインタは、既知の固定サイズなので、 スタックに保管することができますが、実データが必要になったら、ポインタを追いかける必要があります。 レストランで席を確保することを考えましょう。入店したら、グループの人数を告げ、 店員が全員座れる空いている席を探し、そこまで誘導します。もしグループの誰かが遅れて来るのなら、 着いた席の場所を尋ねてあなたを発見することができます。 ヒープへのデータアクセスは、スタックのデータへのアクセスよりも低速です。 ポインタを追って目的の場所に到達しなければならないからです。現代のプロセッサは、メモリをあちこち行き来しなければ、 より速くなります。似た例えを続けましょう。レストランで多くのテーブルから注文を受ける給仕人を考えましょう。最も効率的なのは、 次のテーブルに移らずに、一つのテーブルで全部の注文を受け付けてしまうことです。テーブルAで注文を受け、 それからテーブルBの注文、さらにまたA、それからまたBと渡り歩くのは、かなり低速な過程になってしまうでしょう。 同じ意味で、プロセッサは、 データが隔離されている(ヒープではそうなっている可能性がある)よりも近くにある(スタックではこうなる)ほうが、 仕事をうまくこなせるのです。ヒープに大きな領域を確保する行為も時間がかかることがあります。 コードが関数を呼び出すと、関数に渡された値(ヒープのデータへのポインタも含まれる可能性あり)と、 関数のローカル変数がスタックに載ります。関数の実行が終了すると、それらの値はスタックから取り除かれます。 どの部分のコードがどのヒープ上のデータを使用しているか把握すること、ヒープ上の重複するデータを最小化すること、 メモリ不足にならないようにヒープ上の未使用のデータを掃除することは全て、所有権が解決する問題です。 一度所有権を理解したら、あまり頻繁にスタックとヒープに関して考える必要はなくなるでしょうが、 ヒープデータを管理することが所有権の存在する理由だと知っていると、所有権がありのままで動作する理由を 説明するのに役立つこともあります。","breadcrumbs":"所有権を理解する » スタックとヒープ","id":"ch04-01-what-is-ownership.html#aスタックとヒープ","title":"スタックとヒープ"},"ch04-01-what-is-ownership.html#aメモリと確保":{"body":"文字列リテラルの場合、中身はコンパイル時に判明しているので、テキストは最終的なバイナリファイルに直接ハードコードされます。 このため、文字列リテラルは、高速で効率的になるのです。しかし、これらの特性は、 その文字列リテラルの不変性にのみ端を発するものです。残念なことに、コンパイル時にサイズが不明だったり、 プログラム実行に合わせてサイズが可変なテキスト片用に一塊のメモリをバイナリに確保しておくことは不可能です。 String 型では、可変かつ伸長可能なテキスト破片をサポートするために、コンパイル時には不明な量のメモリを ヒープに確保して内容を保持します。つまり: メモリは、実行時にOSに要求される。 String 型を使用し終わったら、OSにこのメモリを返還する方法が必要である。 この最初の部分は、既にしています: String::from 関数を呼んだら、その実装が必要なメモリを要求するのです。 これは、プログラミング言語において、極めて普遍的です。 しかしながら、2番目の部分は異なります。 ガベージコレクタ (GC)付きの言語では、GCがこれ以上、 使用されないメモリを検知して片付けるため、プログラマは、そのことを考慮する必要はありません。 GCがないなら、メモリがもう使用されないことを見計らって、明示的に返還するコードを呼び出すのは、 プログラマの責任になります。ちょうど要求の際にしたようにですね。これを正確にすることは、 歴史的にも難しいプログラミング問題の一つであり続けています。もし、忘れていたら、メモリを無駄にします。 タイミングが早すぎたら、無効な変数を作ってしまいます。2回解放してしまっても、バグになるわけです。 allocate と free は完璧に1対1対応にしなければならないのです。 Rustは、異なる道を歩んでいます: ひとたび、メモリを所有している変数がスコープを抜けたら、 メモリは自動的に返還されます。こちらの例は、 リスト4-1のスコープ例を文字列リテラルから String 型を使うものに変更したバージョンになります: { let s = String::from(\"hello\"); // sはここから有効になる // sで作業をする\n} // このスコープはここでおしまい。sは // もう有効ではない String 型が必要とするメモリをOSに返還することが自然な地点があります: s 変数がスコープを抜ける時です。 変数がスコープを抜ける時、Rustは特別な関数を呼んでくれます。この関数は、 drop と呼ばれ、 ここに String 型の書き手はメモリを返還するコードを配置することができます。Rustは、閉じ波括弧で自動的に drop 関数を呼び出します。 注釈: C++では、要素の生存期間の終了地点でリソースを解放するこのパターンを時に、 RAII (Resource Aquisition Is Initialization: リソースの獲得は、初期化である)と呼んだりします。 Rustの drop 関数は、あなたがRAIIパターンを使ったことがあれば、馴染み深いものでしょう。 このパターンは、Rustコードの書かれ方に甚大な影響をもたらします。現状は簡単そうに見えるかもしれませんが、 ヒープ上に確保されたデータを複数の変数に使用させるようなもっと複雑な場面では、コードの振る舞いは、 予期しないものになる可能性もあります。これから、そのような場面を掘り下げてみましょう。 変数とデータの相互作用法: ムーブ Rustにおいては、複数の変数が同じデータに対して異なる手段で相互作用することができます。 整数を使用したリスト4-2の例を見てみましょう。 let x = 5;\nlet y = x; リスト4-2: 変数 x の整数値を y に代入する もしかしたら、何をしているのか予想することができるでしょう: 「値 5 を x に束縛する; それから x の値をコピーして y に束縛する。」これで、 二つの変数( x と y )が存在し、両方、値は 5 になりました。これは確かに起こっている現象を説明しています。 なぜなら、整数は既知の固定サイズの単純な値で、これら二つの 5 という値は、スタックに積まれるからです。 では、 String バージョンを見ていきましょう: let s1 = String::from(\"hello\");\nlet s2 = s1; このコードは先ほどのコードに酷似していますので、動作方法も同じだと思い込んでしまうかもしれません: 要するに、2行目で s1 の値をコピーし、 s2 に束縛するということです。ところが、 これは全く起こることを言い当てていません。 図4-1を見て、ベールの下で String に何が起きているかを確かめてください。 String 型は、左側に示されているように、3つの部品でできています: 文字列の中身を保持するメモリへのポインタと長さ、そして、許容量です。この種のデータは、スタックに保持されます。 右側には、中身を保持したヒープ上のメモリがあります。 図4-1: s1 に束縛された \"hello\" という値を保持する String のメモリ上の表現 長さは、 String 型の中身が現在使用しているメモリ量をバイトで表したものです。許容量は、 String 型がOSから受け取った全メモリ量をバイトで表したものです。長さと許容量の違いは問題になることですが、 この文脈では違うので、とりあえずは、許容量を無視しても構わないでしょう。 s1 を s2 に代入すると、 String 型のデータがコピーされます。つまり、スタックにあるポインタ、長さ、 許容量をコピーするということです。ポインタが指すヒープ上のデータはコピーしません。言い換えると、 メモリ上のデータ表現は図4-2のようになるということです。 図4-2: s1 のポインタ、長さ、許容量のコピーを保持する変数 s2 のメモリ上での表現 メモリ上の表現は、図4-3のようにはなり ません 。これは、 Rustが代わりにヒープデータもコピーするという選択をしていた場合のメモリ表現ですね。Rustがこれをしていたら、 ヒープ上のデータが大きい時に s2 = s1 という処理の実行時性能がとても悪くなっていた可能性があるでしょう。 図4-3: Rustがヒープデータもコピーしていた場合に s2 = s1 という処理が行なった可能性のあること 先ほど、変数がスコープを抜けたら、Rustは自動的に drop 関数を呼び出し、 その変数が使っていたヒープメモリを片付けると述べました。しかし、図4-2は、 両方のデータポインタが同じ場所を指していることを示しています。これは問題です: s2 と s1 がスコープを抜けたら、 両方とも同じメモリを解放しようとします。これは 二重解放 エラーとして知られ、以前触れたメモリ安全性上のバグの一つになります。 メモリを2回解放することは、memory corruption ( 訳注 : メモリの崩壊。意図せぬメモリの書き換え) につながり、 セキュリティ上の脆弱性を生む可能性があります。 メモリ安全性を保証するために、Rustにおいてこの場面で起こることの詳細がもう一つあります。 確保されたメモリをコピーしようとする代わりに、コンパイラは、 s1 が最早有効ではないと考え、 故に s1 がスコープを抜けた際に何も解放する必要がなくなるわけです。 s2 の生成後に s1 を使用しようとしたら、 どうなるかを確認してみましょう。動かないでしょう: let s1 = String::from(\"hello\");\nlet s2 = s1; println!(\"{}, world!\", s1); コンパイラが無効化された参照は使用させてくれないので、以下のようなエラーが出るでしょう: error[E0382]: use of moved value: `s1` (ムーブされた値の使用: `s1`) --> src/main.rs:5:28 |\n3 | let s2 = s1; | -- value moved here\n4 |\n5 | println!(\"{}, world!\", s1); | ^^ value used here after move | (ムーブ後にここで使用されています) | = note: move occurs because `s1` has type `std::string::String`, which does not implement the `Copy` trait (注釈: ムーブが起きたのは、`s1`が`std::string::String`という `Copy`トレイトを実装していない型だからです) 他の言語を触っている間に\"shallow copy\"と\"deep copy\"という用語を耳にしたことがあるなら、 データのコピーなしにポインタと長さ、許容量をコピーするという概念は、shallow copyのように思えるかもしれません。 ですが、コンパイラは最初の変数をも無効化するので、shallow copyと呼ばれる代わりに、 ムーブ として知られているわけです。この例では、 s1 は s2 に ムーブ されたと表現するでしょう。 以上より、実際に起きることを図4-4に示してみました。 図4-4: s1 が無効化された後のメモリ表現 これにて一件落着です。 s2 だけが有効なので、スコープを抜けたら、それだけがメモリを解放して、 終わりになります。 付け加えると、これにより暗示される設計上の選択があります: Rustでは、 自動的にデータの\"deep copy\"が行われることは絶対にないわけです。それ故に、あらゆる 自動 コピーは、実行時性能の観点で言うと、 悪くないと考えてよいことになります。 変数とデータの相互作用法: クローン 仮に、スタック上のデータだけでなく、 本当に String 型のヒープデータのdeep copyが必要ならば、 clone と呼ばれるよくあるメソッドを使うことができます。メソッド記法については第5章で議論しますが、 メソッドは多くのプログラミング言語に見られる機能なので、以前に見かけたこともあるんじゃないでしょうか。 これは、 clone メソッドの動作例です: let s1 = String::from(\"hello\");\nlet s2 = s1.clone(); println!(\"s1 = {}, s2 = {}\", s1, s2); これは問題なく動作し、図4-3で示した動作を明示的に生み出します。ここでは、 ヒープデータが 実際に コピーされています。 clone メソッドの呼び出しを見かけたら、何らかの任意のコードが実行され、その実行コストは高いと把握できます。 何か違うことが起こっているなと見た目でわかるわけです。 スタックのみのデータ: コピー まだ話題にしていない別の問題があります。 この整数を使用したコードは、一部をリスト4-2で示しましたが、うまく動作する有効なものです: let x = 5;\nlet y = x; println!(\"x = {}, y = {}\", x, y); ですが、このコードは一見、今学んだことと矛盾しているように見えます: clone メソッドの呼び出しがないのに、 x は有効で、 y にムーブされませんでした。 その理由は、整数のようなコンパイル時に既知のサイズを持つ型は、スタック上にすっぽり保持されるので、 実際の値をコピーするのも高速だからです。これは、変数 y を生成した後にも x を無効化したくなる理由がないことを意味します。 換言すると、ここでは、shallow copyとdeep copyの違いがないことになり、 clone メソッドを呼び出しても、一般的なshallow copy以上のことをしなくなり、 そのまま放置しておけるということです。 Rustには Copy トレイトと呼ばれる特別な注釈があり、 整数のようなスタックに保持される型に対して配置することができます(トレイトについては第10章でもっと詳しく話します)。 型が Copy トレイトに適合していれば、代入後も古い変数が使用可能になります。コンパイラは、 型やその一部分でも Drop トレイトを実装している場合、 Copy トレイトによる注釈をさせてくれません。 型の値がスコープを外れた時に何か特別なことを起こす必要がある場合に、 Copy 注釈を追加すると、コンパイルエラーが出ます。 型に Copy 注釈をつける方法について学ぶには、付録Cの「導出可能なトレイト」をご覧ください。 では、どの型が Copy なのでしょうか?ある型について、ドキュメントをチェックすればいいのですが、 一般規則として、単純なスカラー値の集合は何でも Copy であり、メモリ確保が必要だったり、 何らかの形態のリソースだったりするものは Copy ではありません。ここに Copy の型の一部を並べておきます。 あらゆる整数型。 u32 など。 論理値型である bool 。 true と false という値がある。 あらゆる浮動小数点型、 f64 など。 文字型である char 。 タプル。ただ、 Copy の型だけを含む場合。例えば、 (i32, i32) は Copy だが、 (i32, String) は違う。","breadcrumbs":"所有権を理解する » メモリと確保","id":"ch04-01-what-is-ownership.html#aメモリと確保","title":"メモリと確保"},"ch04-01-what-is-ownership.html#a変数スコープ":{"body":"第2章で、Rustプログラムの例はすでに見ています。もう基本的な記法は通り過ぎたので、 fn main() { というコードはもう例に含みません。従って、例をなぞっているなら、 これからの例は main 関数に手動で入れ込まなければいけなくなるでしょう。結果的に、例は少々簡潔になり、 定型コードよりも具体的な詳細に集中しやすくなります。 所有権の最初の例として、何らかの変数の スコープ について見ていきましょう。スコープとは、 要素が有効になるプログラム内の範囲のことです。以下のような変数があるとしましょう: let s = \"hello\"; 変数 s は、文字列リテラルを参照し、ここでは、文字列の値はプログラムのテキストとしてハードコードされています。 この変数は、宣言された地点から、現在の スコープ の終わりまで有効になります。リスト4-1には、 変数 s が有効な場所に関する注釈がコメントで付記されています。 { // sは、ここでは有効ではない。まだ宣言されていない let s = \"hello\"; // sは、ここから有効になる // sで作業をする\n} // このスコープは終わり。もうsは有効ではない リスト4-1: 変数と有効なスコープ 言い換えると、ここまでに重要な点は二つあります: s が スコープに入る と、有効になる スコープを抜ける まで、有効なまま ここで、スコープと変数が有効になる期間の関係は、他の言語に類似しています。さて、この理解のもとに、 String 型を導入して構築していきましょう。","breadcrumbs":"所有権を理解する » 変数スコープ","id":"ch04-01-what-is-ownership.html#a変数スコープ","title":"変数スコープ"},"ch04-01-what-is-ownership.html#a戻り値とスコープ":{"body":"値を返すことでも、所有権は移動します。リスト4-4は、リスト4-3と似た注釈のついた例です。 ファイル名: src/main.rs fn main() { let s1 = gives_ownership(); // gives_ownershipは、戻り値をs1に // ムーブする let s2 = String::from(\"hello\"); // s2がスコープに入る let s3 = takes_and_gives_back(s2); // s2はtakes_and_gives_backにムーブされ // 戻り値もs3にムーブされる\n} // ここで、s3はスコープを抜け、ドロップされる。s2もスコープを抜けるが、ムーブされているので、 // 何も起きない。s1もスコープを抜け、ドロップされる。 fn gives_ownership() -> String { // gives_ownershipは、戻り値を // 呼び出した関数にムーブする let some_string = String::from(\"hello\"); // some_stringがスコープに入る some_string // some_stringが返され、呼び出し元関数に // ムーブされる\n} // takes_and_gives_backは、Stringを一つ受け取り、返す。\nfn takes_and_gives_back(a_string: String) -> String { // a_stringがスコープに入る。 a_string // a_stringが返され、呼び出し元関数にムーブされる\n} リスト4-4: 戻り値の所有権を移動する 変数の所有権は、毎回同じパターンを辿っています: 別の変数に値を代入すると、ムーブされます。 ヒープにデータを含む変数がスコープを抜けると、データが別の変数に所有されるようムーブされていない限り、 drop により片付けられるでしょう。 所有権を取り、またその所有権を戻す、ということを全ての関数でしていたら、ちょっとめんどくさいですね。 関数に値は使わせるものの所有権を取らないようにさせるにはどうするべきでしょうか。 返したいと思うかもしれない関数本体で発生したあらゆるデータとともに、再利用したかったら、渡されたものをまた返さなきゃいけないのは、 非常に煩わしいことです。 タプルで、複数の値を返すことは可能です。リスト4-5のようにですね。 ファイル名: src/main.rs fn main() { let s1 = String::from(\"hello\"); let (s2, len) = calculate_length(s1); //'{}'の長さは、{}です println!(\"The length of '{}' is {}.\", s2, len);\n} fn calculate_length(s: String) -> (String, usize) { let length = s.len(); // len()メソッドは、Stringの長さを返します (s, length)\n} リスト4-5: 引数の所有権を返す でも、これでは、大袈裟すぎますし、ありふれているはずの概念に対して、作業量が多すぎます。 私たちにとって幸運なことに、Rustにはこの概念に対する機能があり、 参照 と呼ばれます。","breadcrumbs":"所有権を理解する » 戻り値とスコープ","id":"ch04-01-what-is-ownership.html#a戻り値とスコープ","title":"戻り値とスコープ"},"ch04-01-what-is-ownership.html#a所有権とは":{"body":"Rustの中心的な機能は、 所有権 です。この機能は、説明するのは簡単なのですが、言語の残りの機能全てにかかるほど 深い裏の意味を含んでいるのです。 全てのプログラムは、実行中にコンピュータのメモリの使用方法を管理する必要があります。プログラムが動作するにつれて、 定期的に使用されていないメモリを検索するガベージコレクションを持つ言語もありますが、他の言語では、 プログラマが明示的にメモリを確保したり、解放したりしなければなりません。Rustでは第3の選択肢を取っています: メモリは、コンパイラがコンパイル時にチェックする一定の規則とともに所有権システムを通じて管理されています。 どの所有権機能も、実行中にプログラムの動作を遅くすることはありません。 所有権は多くのプログラマにとって新しい概念なので、慣れるまでに時間がかかります。 嬉しいことに、Rustと、所有権システムの規則の経験を積むと、より自然に安全かつ効率的なコードを構築できるようになります。 その調子でいきましょう! 所有権を理解した時、Rustを際立たせる機能の理解に対する強固な礎を得ることになるでしょう。この章では、 非常に一般的なデータ構造に着目した例を取り扱うことで所有権を学んでいきます: 文字列です。","breadcrumbs":"所有権を理解する » 所有権とは?","id":"ch04-01-what-is-ownership.html#a所有権とは","title":"所有権とは?"},"ch04-01-what-is-ownership.html#a所有権と関数":{"body":"意味論的に、関数に値を渡すことと、値を変数に代入することは似ています。関数に変数を渡すと、 代入のようにムーブやコピーされます。リスト4-3は変数がスコープに入ったり、 抜けたりする地点について注釈してある例です。 ファイル名: src/main.rs fn main() { let s = String::from(\"hello\"); // sがスコープに入る takes_ownership(s); // sの値が関数にムーブされ... // ... ここではもう有効ではない let x = 5; // xがスコープに入る makes_copy(x); // xも関数にムーブされるが、 // i32はCopyなので、この後にxを使っても // 大丈夫 } // ここでxがスコープを抜け、sもスコープを抜ける。ただし、sの値はムーブされているので、何も特別なことは起こらない。 // fn takes_ownership(some_string: String) { // some_stringがスコープに入る。 println!(\"{}\", some_string);\n} // ここでsome_stringがスコープを抜け、`drop`が呼ばれる。後ろ盾してたメモリが解放される。 // fn makes_copy(some_integer: i32) { // some_integerがスコープに入る println!(\"{}\", some_integer);\n} // ここでsome_integerがスコープを抜ける。何も特別なことはない。 リスト4-3: 所有権とスコープが注釈された関数群 takes_ownership の呼び出し後に s を呼び出そうとすると、コンパイラは、コンパイルエラーを投げるでしょう。 これらの静的チェックにより、ミスを犯さないでいられます。 s や x を使用するコードを main に追加してみて、 どこで使えて、そして、所有権規則により、どこで使えないかを確認してください。","breadcrumbs":"所有権を理解する » 所有権と関数","id":"ch04-01-what-is-ownership.html#a所有権と関数","title":"所有権と関数"},"ch04-01-what-is-ownership.html#a所有権規則":{"body":"まず、所有権のルールについて見ていきましょう。 この規則を具体化する例を扱っていく間もこれらのルールを肝に銘じておいてください: Rustの各値は、 所有者 と呼ばれる変数と対応している。 いかなる時も所有者は一つである。 所有者がスコープから外れたら、値は破棄される。","breadcrumbs":"所有権を理解する » 所有権規則","id":"ch04-01-what-is-ownership.html#a所有権規則","title":"所有権規則"},"ch04-01-what-is-ownership.html#string-型":{"body":"所有権の規則を具体化するには、第3章の「データ型」節で講義したものよりも、より複雑なデータ型が必要になります。 以前講義した型は全てスタックに保管され、スコープが終わるとスタックから取り除かれますが、 ヒープに確保されるデータ型を観察して、 コンパイラがどうそのデータを掃除すべきタイミングを把握しているかを掘り下げていきたいと思います。 ここでは、例として String 型を使用し、 String 型の所有権にまつわる部分に着目しましょう。 また、この観点は、標準ライブラリや自分で生成する他の複雑なデータ型にも適用されます。 String 型については、第8章でより深く議論します。 既に文字列リテラルは見かけましたね。文字列リテラルでは、文字列の値はプログラムにハードコードされます。 文字列リテラルは便利ですが、テキストを使いたいかもしれない場面全てに最適なわけではありません。一因は、 文字列リテラルが不変であることに起因します。別の原因は、コードを書く際に、全ての文字列値が判明するわけではないからです: 例えば、ユーザ入力を受け付け、それを保持したいとしたらどうでしょうか?このような場面用に、Rustには、 2種類目の文字列型、 String 型があります。この型はヒープにメモリを確保するので、 コンパイル時にはサイズが不明なテキストも保持することができるのです。 from 関数を使用して、 文字列リテラルから String 型を生成できます。以下のように: let s = String::from(\"hello\"); この二重コロンは、 string_from などの名前を使うのではなく、 String 型直下の from 関数を特定する働きをする演算子です。この記法について詳しくは、 第5章の「メソッド記法」節と、第7章の「モジュール定義」でモジュールを使った名前空間分けについて話をするときに議論します。 この種の文字列は、可変化することが できます : let mut s = String::from(\"hello\"); s.push_str(\", world!\"); // push_str()関数は、リテラルをStringに付け加える println!(\"{}\", s); // これは`hello, world!`と出力する では、ここでの違いは何でしょうか?なぜ、 String 型は可変化できるのに、リテラルはできないのでしょうか? 違いは、これら二つの型がメモリを扱う方法にあります。","breadcrumbs":"所有権を理解する » String 型","id":"ch04-01-what-is-ownership.html#string-型","title":"String 型"},"ch04-02-references-and-borrowing.html#a参照と借用":{"body":"リスト4-5のタプルコードの問題は、 String 型を呼び出し元の関数に戻さないと、 calculate_length を呼び出した後に、 String オブジェクトが使えなくなることであり、これは String オブジェクトが calculate_length にムーブされてしまうためでした。 ここで、値の所有権をもらう代わりに引数としてオブジェクトへの参照を取る calculate_length 関数を定義し、 使う方法を見てみましょう: ファイル名: src/main.rs fn main() { let s1 = String::from(\"hello\"); let len = calculate_length(&s1); // '{}'の長さは、{}です println!(\"The length of '{}' is {}.\", s1, len);\n} fn calculate_length(s: &String) -> usize { s.len()\n} まず、変数宣言と関数の戻り値にあったタプルコードは全てなくなったことに気付いてください。 2番目に、 &s1 を calcuate_length に渡し、その定義では、 String 型ではなく、 &String を受け取っていることに注目してください。 これらのアンド記号が参照であり、これのおかげで所有権をもらうことなく値を参照することができるのです。 図4-5はその図解です。 図4-5: String s1 を指す &String s の図表 注釈: & による参照の逆は、 参照外し であり、参照外し演算子の * で達成できます。 第8章で参照外し演算子の使用例を眺め、第15章で参照外しについて詳しく議論します。 ここの関数呼び出しについて、もっと詳しく見てみましょう: # fn calculate_length(s: &String) -> usize {\n# s.len()\n# }\nlet s1 = String::from(\"hello\"); let len = calculate_length(&s1); この &s1 という記法により、 s1 の値を 参照する 参照を生成することができますが、これを所有することはありません。 所有してないということは、指している値は、参照がスコープを抜けてもドロップされないということです。 同様に、関数のシグニチャでも、 & を使用して引数 s の型が参照であることを示しています。 説明的な注釈を加えてみましょう: fn calculate_length(s: &String) -> usize { // sはStringへの参照 s.len()\n} // ここで、sはスコープ外になる。けど、参照しているものの所有権を持っているわけではないので // 何も起こらない 変数 s が有効なスコープは、あらゆる関数の引数のものと同じですが、所有権はないので、 s がスコープを抜けても、 参照が指しているものをドロップすることはありません。関数が実際の値の代わりに参照を引数に取ると、 所有権をもらわないので、所有権を返す目的で値を返す必要はありません。 関数の引数に参照を取ることを 借用 と呼びます。現実生活のように、誰かが何かを所有していたら、 それを借りることができます。用が済んだら、返さなきゃいけないわけです。 では、借用した何かを変更しようとしたら、どうなるのでしょうか?リスト4-6のコードを試してください。 ネタバレ注意: 動きません! ファイル名: src/main.rs fn main() { let s = String::from(\"hello\"); change(&s);\n} fn change(some_string: &String) { some_string.push_str(\", world\");\n} リスト4-6: 借用した値を変更しようと試みる これがエラーです: error[E0596]: cannot borrow immutable borrowed content `*some_string` as mutable\n(エラー: 不変な借用をした中身`*some_string`を可変で借用できません) --> error.rs:8:5 |\n7 | fn change(some_string: &String) { | ------- use `&mut String` here to make mutable\n8 | some_string.push_str(\", world\"); | ^^^^^^^^^^^ cannot borrow as mutable 変数が標準で不変なのと全く同様に、参照も不変なのです。参照している何かを変更することは叶わないわけです。","breadcrumbs":"所有権を理解する » 参照と借用","id":"ch04-02-references-and-borrowing.html#a参照と借用","title":"参照と借用"},"ch04-02-references-and-borrowing.html#a参照の規則":{"body":"参照について議論したことを再確認しましょう: 任意のタイミングで、一つの可変参照 か 不変な参照いくつでもの どちらか を行える。 参照は常に有効でなければならない。 次は、違う種類の参照を見ていきましょう: スライスです。","breadcrumbs":"所有権を理解する » 参照の規則","id":"ch04-02-references-and-borrowing.html#a参照の規則","title":"参照の規則"},"ch04-02-references-and-borrowing.html#a可変な参照":{"body":"一捻り加えるだけでリスト4-6のコードのエラーは解決します: ファイル名: src/main.rs fn main() { let mut s = String::from(\"hello\"); change(&mut s);\n} fn change(some_string: &mut String) { some_string.push_str(\", world\");\n} 始めに、 s を mut に変えなければなりませんでした。そして、 &mut s で可変な参照を生成し、 some_string: &mut String で可変な参照を受け入れなければなりませんでした。 ところが、可変な参照には大きな制約が一つあります: 特定のスコープで、ある特定のデータに対しては、 一つしか可変な参照を持てないことです。こちらのコードは失敗します: ファイル名: src/main.rs let mut s = String::from(\"hello\"); let r1 = &mut s;\nlet r2 = &mut s; これがエラーです: error[E0499]: cannot borrow `s` as mutable more than once at a time\n(エラー: 一度に`s`を可変として2回以上借用することはできません) --> borrow_twice.rs:5:19 |\n4 | let r1 = &mut s; | - first mutable borrow occurs here | (最初の可変な参照はここ)\n5 | let r2 = &mut s; | ^ second mutable borrow occurs here | (二つ目の可変な参照はここ)\n6 | } | - first borrow ends here | (最初の借用はここで終わり) この制約は、可変化を許可するものの、それを非常に統制の取れた形で行えます。これは、新たなRustaceanにとっては、 壁です。なぜなら、多くの言語では、いつでも好きな時に可変化できるからです。 この制約がある利点は、コンパイラがコンパイル時にデータ競合を防ぐことができる点です。 データ競合とは、競合条件と類似していて、これら3つの振る舞いが起きる時に発生します: 2つ以上のポインタが同じデータに同時にアクセスする。 少なくとも一つのポインタがデータに書き込みを行っている。 データへのアクセスを同期する機構が使用されていない。 データ競合は未定義の振る舞いを引き起こし、実行時に追いかけようとした時に特定し解決するのが難しい問題です。 しかし、Rustは、データ競合が起こるコードをコンパイルさえしないので、この問題が発生しないようにしてくれるわけです。 いつものように、波かっこを使って新しいスコープを生成し、 同時並行 なものでなく、複数の可変な参照を作ることができます。 let mut s = String::from(\"hello\"); { let r1 = &mut s; } // r1はここでスコープを抜けるので、問題なく新しい参照を作ることができる let r2 = &mut s; 可変と不変な参照を組み合わせることに関しても、似たような規則が存在しています。このコードはエラーになります: let mut s = String::from(\"hello\"); let r1 = &s; // 問題なし\nlet r2 = &s; // 問題なし\nlet r3 = &mut s; // 大問題! これがエラーです: error[E0502]: cannot borrow `s` as mutable because it is also borrowed as\nimmutable\n(エラー: `s`は不変で借用されているので、可変で借用できません) --> borrow_thrice.rs:6:19 |\n4 | let r1 = &s; // no problem | - immutable borrow occurs here\n5 | let r2 = &s; // no problem\n6 | let r3 = &mut s; // BIG PROBLEM | ^ mutable borrow occurs here\n7 | } | - immutable borrow ends here ふう! さらに 不変な参照をしている間は、可変な参照をすることはできません。不変参照の使用者は、 それ以降に値が突然変わることなんて予想してません!しかしながら、複数の不変参照をすることは可能です。 データを読み込んでいるだけの人に、他人がデータを読み込むことに対して影響を与える能力はないからです。 これらのエラーは、時としてイライラするものではありますが、Rustコンパイラがバグの可能性を早期に指摘してくれ(それも実行時ではなくコンパイル時に)、 問題の発生箇所をズバリ示してくれるのだと覚えておいてください。そうして想定通りにデータが変わらない理由を追いかける必要がなくなります。","breadcrumbs":"所有権を理解する » 可変な参照","id":"ch04-02-references-and-borrowing.html#a可変な参照","title":"可変な参照"},"ch04-02-references-and-borrowing.html#a宙に浮いた参照":{"body":"ポインタのある言語では、誤ってダングリングポインタを生成してしまいやすいです。ダングリングポインタとは、 他人に渡されてしまった可能性のあるメモリを指すポインタのことであり、その箇所へのポインタを保持している間に、 メモリを解放してしまうことで発生します。対照的にRustでは、コンパイラが、 参照がダングリング参照に絶対ならないよう保証してくれます: つまり、何らかのデータへの参照があったら、 コンパイラは参照がスコープを抜けるまで、データがスコープを抜けることがないよう確認してくれるわけです。 ダングリング参照作りを試してみますが、コンパイラはこれをコンパイルエラーで阻止します: ファイル名: src/main.rs fn main() { let reference_to_nothing = dangle();\n} fn dangle() -> &String { let s = String::from(\"hello\"); &s\n} こちらがエラーです: error[E0106]: missing lifetime specifier\n(エラー: ライフタイム指定子がありません) --> main.rs:5:16 |\n5 | fn dangle() -> &String { | ^ expected lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from (助言: この関数の戻り値型は、借用した値を含んでいますが、借用される値がどこにもありません) = help: consider giving it a 'static lifetime ('staticライフタイムを与えることを考慮してみてください) このエラーメッセージは、まだ講義していない機能について触れています: ライフタイム です。 ライフタイムについては第10章で詳しく議論しますが、ライフタイムに関する部分を無視すれば、 このメッセージは、確かにこのコードが問題になる理由に関する鍵を握っています: this function's return type contains a borrowed value, but there is no value\nfor it to be borrowed from. dangle コードの各段階で一体何が起きているのかを詳しく見ていきましょう: ファイル名: src/main.rs fn dangle() -> &String { // dangleはStringへの参照を返す let s = String::from(\"hello\"); // sは新しいString &s // String sへの参照を返す\n} // ここで、sはスコープを抜け、ドロップされる。そのメモリは消される。 // 危険だ s は、 dangle 内で生成されているので、 dangle のコードが終わったら、 s は解放されてしまいますが、 そこへの参照を返そうとしました。つまり、この参照は無効な String を指していると思われるのです。 よくないことです!コンパイラは、これを阻止してくれるのです。 ここでの解決策は、 String を直接返すことです: fn no_dangle() -> String { let s = String::from(\"hello\"); s\n} これは何の問題もなく動きます。所有権はムーブされ、何も解放されることはありません。","breadcrumbs":"所有権を理解する » 宙に浮いた参照","id":"ch04-02-references-and-borrowing.html#a宙に浮いた参照","title":"宙に浮いた参照"},"ch04-03-slices.html#aまとめ":{"body":"所有権、借用、スライスの概念は、コンパイル時にRustプログラムにおいて、メモリ安全性を保証します。 Rust言語も他のシステムプログラミング言語と同じように、メモリの使用法について制御させてくれるわけですが、 所有者がスコープを抜けたときにデータの所有者に自動的にデータを片付けさせることは、この制御を得るために、 余計なコードを書いてデバッグする必要がないことを意味します。 所有権は、Rustの他のいろんな部分が動作する方法に影響を与えるので、これ以降もこれらの概念についてさらに語っていく予定です。 第5章に移って、 struct でデータをグループ化することについて見ていきましょう。","breadcrumbs":"所有権を理解する » まとめ","id":"ch04-03-slices.html#aまとめ","title":"まとめ"},"ch04-03-slices.html#aスライス型":{"body":"所有権のない別のデータ型は、 スライス です。スライスにより、コレクション全体というより、 その内の一連の要素を参照することができます。 ここに小さなプログラミング問題があります: 文字列を受け取って、その文字列中の最初の単語を返す関数を書いてください。 関数が文字列中に空白を見つけなかったら、文字列全体が一つの単語に違いないので、文字列全体が返されるべきです。 この関数のシグニチャについて考えてみましょう: fn first_word(s: &String) -> ? この関数、 first_word は引数に &String をとります。所有権はいらないので、これで十分です。 ですが、何を返すべきでしょうか?文字列の 一部 について語る方法が全くありません。しかし、 単語の終端の添え字を返すことができますね。リスト4-7に示したように、その方法を試してみましょう。 ファイル名: src/main.rs fn first_word(s: &String) -> usize { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return i; } } s.len()\n} リスト4-7: String 引数へのバイト数で表された添え字を返す first_word 関数 String の値を要素ごとに見て、空白かどうかを確かめる必要があるので、 as_bytes メソッドを使って、 String オブジェクトをバイト配列に変換しています。 let bytes = s.as_bytes(); 次に、そのバイト配列に対して、 iter メソッドを使用してイテレータを生成しています: for (i, &item) in bytes.iter().enumerate() { イテレータについて詳しくは、第13章で議論します。今は、 iter は、コレクション内の各要素を返すメソッドであること、 enumerate が iter の結果を包んで、代わりにタプルの一部として各要素を返すことを知っておいてください。 enumerate から返ってくるタプルの第1要素は、添え字であり、2番目の要素は、(コレクションの)要素への参照になります。 これは、手動で添え字を計算するよりも少しだけ便利です。 enumerate メソッドがタプルを返すので、Rustのあらゆる場所同様、パターンを使って、そのタプルを分配できます。 従って、 for ループ内で、タプルの添え字に対する i とタプルの1バイトに対応する &item を含むパターンを指定しています。 .iter().enumerate() から要素への参照を取得するので、パターンに & を使っています。 for ループ内で、バイトリテラル表記を使用して空白を表すバイトを検索しています。空白が見つかったら、その位置を返します。 それ以外の場合、 s.len() を使って文字列の長さを返します。 if item == b' ' { return i; }\n} s.len() さて、文字列内の最初の単語の終端の添え字を見つけ出せるようになりましたが、問題があります。 usize 型を単独で返していますが、これは &String の文脈でのみ意味を持つ数値です。 言い換えると、 String から切り離された値なので、将来的にも有効である保証がないのです。 リスト4-7の first_word 関数を使用するリスト4-8のプログラムを考えてください。 ファイル名: src/main.rs # fn first_word(s: &String) -> usize {\n# let bytes = s.as_bytes();\n#\n# for (i, &item) in bytes.iter().enumerate() {\n# if item == b' ' {\n# return i;\n# }\n# }\n#\n# s.len()\n# }\n#\nfn main() { let mut s = String::from(\"hello world\"); let word = first_word(&s); // wordの中身は、値5になる s.clear(); // Stringを空にする。つまり、\"\"と等しくする // wordはまだ値5を保持しているが、もうこの値を有効に使用できる文字列は存在しない。 // wordは完全に無効なのだ!\n} リスト4-8: first_word 関数の呼び出し結果を保持し、 String の中身を変更する このプログラムは何のエラーもなくコンパイルが通り、 word を s.clear() の呼び出し後に使用しても、 コンパイルが通ります。 word は s の状態に全く関連づけられていないので、その中身はまだ値 5 のままです。 その値 5 を変数 s に使用し、最初の単語を取り出そうとすることはできますが、これはバグでしょう。 というのも、 s の中身は、 5 を word に保存してから変わってしまったからです。 word 内の添え字が s に格納されたデータと同期されなくなるのを心配することは、面倒ですし間違いになりやすいです! これらの添え字の管理は、 second_word 関数を書いたら、さらに難しくなります。 そのシグニチャは以下のようになるはずです: fn second_word(s: &String) -> (usize, usize) { 今、私たちは開始 と 終端の添え字を追うようになりました。特定の状態のデータから計算されたけど、 その状態に全く紐付かない値が増えました。いつの間にか変わってしまうので、同期を取る必要のある、関連性のない変数が3つになってしまいました。 運のいいことに、Rustにはこの問題への解決策が用意されています: 文字列スライスです。","breadcrumbs":"所有権を理解する » スライス型","id":"ch04-03-slices.html#aスライス型","title":"スライス型"},"ch04-03-slices.html#a他のスライス":{"body":"文字列リテラルは、ご想像通り、文字列に特化したものです。ですが、もっと一般的なスライス型も存在します。 この配列を考えてください: let a = [1, 2, 3, 4, 5]; 文字列の一部を参照したくなる可能性があるのと同様、配列の一部を参照したくなる可能性もあります。 以下のようにすれば、参照することができます: let a = [1, 2, 3, 4, 5]; let slice = &a[1..3]; このスライスは、 &[i32] という型になります。これも文字列スライスと同じように動作します。 つまり、最初の要素への参照と長さを保持することです。他のすべての種類のコレクションに対して、 この種のスライスは使用するでしょう。これらのコレクションについて詳しくは、 第8章でベクタについて話すときに議論します。","breadcrumbs":"所有権を理解する » 他のスライス","id":"ch04-03-slices.html#a他のスライス","title":"他のスライス"},"ch04-03-slices.html#a文字列スライス":{"body":"文字列スライス とは、 String の一部への参照で、こんな見た目をしています: let s = String::from(\"hello world\"); let hello = &s[0..5];\nlet world = &s[6..11]; これは、 String 全体への参照を取ることに似ていますが、余計な [0..5] という部分が付いています。 String 全体への参照というよりも、 String の一部への参照です。 開始..終点 という記法は、 開始 から始まり、 終点 未満までずっと続く範囲です。 [starting_index..ending_index] と指定することで、角かっこに範囲を使い、スライスを生成できます。 ここで、 starting_index はスライスの最初の位置、 ending_index はスライスの終端位置よりも、 1大きくなります。内部的には、スライスデータ構造は、開始地点とスライスの長さを保持しており、 スライスの長さは ending_index から starting_index を引いたものに対応します。以上より、 let world = &s[6..11]; の場合には、 world は s の7バイト目へのポインタと5という長さを保持するスライスになるでしょう。 図4-6は、これを図解しています。 図4-6: String オブジェクトの一部を参照する文字列スライス Rustの .. という範囲記法で、最初の番号(ゼロ)から始めたければ、2連ピリオドの前に値を書かなければいいのです。 換言すれば、これらは等価です: let s = String::from(\"hello\"); let slice = &s[0..2];\nlet slice = &s[..2]; 同様の意味で、 String の最後のバイトをスライスが含むのならば、末尾の数値を書かなければいいのです。 つまり、これらは等価になります: let s = String::from(\"hello\"); let len = s.len(); let slice = &s[3..len];\nlet slice = &s[3..]; さらに、両方の値を省略すると、文字列全体のスライスを得られます。故に、これらは等価です: let s = String::from(\"hello\"); let len = s.len(); let slice = &s[0..len];\nlet slice = &s[..]; 注釈: 文字列スライスの範囲添え字は、有効なUTF-8文字境界に置かなければなりません。 マルチバイト文字の真ん中で文字列スライスを生成しようとしたら、エラーでプログラムは落ちるでしょう。 文字列スライスを導入する目的で、この節ではASCIIのみを想定しています; UTF-8に関するより徹底した議論は、 第8章の「文字列でUTF-8エンコードされたテキストを格納する」節で行います。 これら全ての情報を心に留めて、 first_word を書き直してスライスを返すようにしましょう。 文字列スライスを意味する型は、 &str と記述します: ファイル名: src/main.rs fn first_word(s: &String) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[0..i]; } } &s[..]\n} リスト4-7で取った手段と同じ方法で単語の終端添え字を取得しています。つまり、最初の空白を探すことです。 空白を発見したら、文字列の最初を開始地点、空白の添え字を終了地点として使用して文字列スライスを返しています。 これで、 first_word を呼び出すと、元のデータに紐付けられた単独の値を得られるようになりました。 この値は、スライスの開始地点への参照とスライス中の要素数から構成されています。 second_word 関数についても、スライスを返すことでうまくいくでしょう: fn second_word(s: &String) -> &str { これで、ずっと混乱しにくい素直なAPIになりました。なぜなら、 String への参照が有効なままであることをコンパイラが、 保証してくれるからです。最初の単語の終端添え字を得た時に、 文字列を空っぽにして先ほどの添え字が無効になってしまったリスト4-8のプログラムのバグを覚えていますか? そのコードは、論理的に正しくないのですが、即座にエラーにはなりませんでした。問題は後になってから発生し、 それは空の文字列に対して、最初の単語の添え字を使用し続けようとした時でした。スライスならこんなバグはあり得ず、 コードに問題があるなら、もっと迅速に判明します。スライスバージョンの first_word を使用すると、 コンパイルエラーが発生します: ファイル名: src/main.rs fn main() { let mut s = String::from(\"hello world\"); let word = first_word(&s); s.clear(); // error! (エラー!) println!(\"the first word is: {}\", word);\n} こちらがコンパイルエラーです: $ cargo run Compiling ownership v0.1.0 (file:///projects/ownership)\nerror[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable\n(エラー: 不変として借用されているので、`s`を可変で借用できません) --> src/main.rs:18:5 |\n16 | let word = first_word(&s); | -- immutable borrow occurs here (不変借用はここで発生しています)\n17 | 18 | s.clear(); // error! (エラー!) | ^^^^^^^^^ mutable borrow occurs here (可変借用はここで発生しています)\n19 | 20 | println!(\"the first word is: {}\", word); | ---- immutable borrow later used here (不変借用はその後ここで使われています) error: aborting due to previous error For more information about this error, try `rustc --explain E0502`.\nerror: could not compile `ownership`. To learn more, run the command again with --verbose. 借用規則から、何かへの不変な参照がある時、さらに可変な参照を得ることはできないことを思い出してください。 clear は String を切り詰める必要があるので、可変な参照を得ようとして失敗しているわけです。 RustのおかげでAPIが使いやすくなるだけでなく、ある種のエラー全てを完全にコンパイル時に排除してくれるのです! 文字列リテラルはスライスである 文字列は、バイナリに埋め込まれると話したことを思い出してください。今やスライスのことを知ったので、 文字列リテラルを正しく理解することができます。 let s = \"Hello, world!\"; ここでの s の型は、 &str です: バイナリのその特定の位置を指すスライスです。 これは、文字列が不変である理由にもなっています。要するに、 &str は不変な参照なのです。 引数としての文字列スライス リテラルや String 値のスライスを得ることができると知ると、 first_word に対して、もう一つ改善点を見出すことができます。 シグニチャです: fn first_word(s: &String) -> &str { もっと経験を積んだRustaceanなら、代わりにリスト4-9のようなシグニチャを書くでしょう。というのも、こうすると、 同じ関数を String 値と &str 値両方に使えるようになるからです。 fn first_word(s: &str) -> &str { リスト4-9: s 引数の型に文字列スライスを使用して first_word 関数を改善する もし、文字列スライスがあるなら、それを直接渡せます。 String があるなら、 その String 全体のスライスを渡せます。 String への参照の代わりに文字列スライスを取るよう関数を定義すると、 何も機能を失うことなくAPIをより一般的で有益なものにできるのです。 Filename: src/main.rs # fn first_word(s: &str) -> &str {\n# let bytes = s.as_bytes();\n#\n# for (i, &item) in bytes.iter().enumerate() {\n# if item == b' ' {\n# return &s[0..i];\n# }\n# }\n#\n# &s[..]\n# }\nfn main() { let my_string = String::from(\"hello world\"); // first_wordは`String`のスライスに対して機能する let word = first_word(&my_string[..]); let my_string_literal = \"hello world\"; // first_wordは文字列リテラルのスライスに対して機能する let word = first_word(&my_string_literal[..]); // 文字列リテラルは、すでに文字列スライス*な*ので、 // スライス記法なしでも機能するのだ! let word = first_word(my_string_literal);\n}","breadcrumbs":"所有権を理解する » 文字列スライス","id":"ch04-03-slices.html#a文字列スライス","title":"文字列スライス"},"ch05-00-structs.html#a構造体を使用して関係のあるデータを構造化する":{"body":"struct または、 構造体 は、意味のあるグループを形成する複数の関連した値をまとめ、名前付けできる独自のデータ型です。 あなたがオブジェクト指向言語に造詣が深いなら、 struct はオブジェクトのデータ属性みたいなものです。 この章では、タプルと構造体を対照的に比較し、構造体の使用法をデモし、メソッドや関連関数を定義して、 構造体のデータに紐付く振る舞いを指定する方法について議論します。構造体と enum (第6章で議論します)は、 自分のプログラム領域で新しい型を定義し、Rustのコンパイル時型精査機能をフル活用する構成要素になります。","breadcrumbs":"構造体を使用して関係のあるデータを構造化する","id":"ch05-00-structs.html#a構造体を使用して関係のあるデータを構造化する","title":"構造体を使用して関係のあるデータを構造化する"},"ch05-01-defining-structs.html#aフィールドと変数が同名の時にフィールド初期化省略記法を使う":{"body":"仮引数名と構造体のフィールド名がリスト5-4では、全く一緒なので、 フィールド初期化省略 記法を使って build_user を書き換えても、 振る舞いは全く同じにしつつ、リスト5-5に示したように email と username を繰り返さなくてもよくなります。 # struct User {\n# username: String,\n# email: String,\n# sign_in_count: u64,\n# active: bool,\n# }\n#\nfn build_user(email: String, username: String) -> User { User { email, username, active: true, sign_in_count: 1, }\n} リスト5-5: email と username 引数が構造体のフィールドと同名なので、 フィールド初期化省略法を使用する build_user 関数 ここで、 email というフィールドを持つ User 構造体の新規インスタンスを生成しています。 email フィールドを build_user 関数の email 引数の値にセットしたいわけです。 email フィールドと email 引数は同じ名前なので、 email: email と書くのではなく、 email と書くだけで済むのです。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » フィールドと変数が同名の時にフィールド初期化省略記法を使う","id":"ch05-01-defining-structs.html#aフィールドと変数が同名の時にフィールド初期化省略記法を使う","title":"フィールドと変数が同名の時にフィールド初期化省略記法を使う"},"ch05-01-defining-structs.html#aフィールドのないユニット様よう構造体":{"body":"また、一切フィールドのない構造体を定義することもできます!これらは、 () 、ユニット型と似たような振る舞いをすることから、 ユニット様構造体 と呼ばれます。ユニット様構造体は、ある型にトレイトを実装するけれども、 型自体に保持させるデータは一切ない場面に有効になります。トレイトについては第10章で議論します。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » フィールドのないユニット様(よう)構造体","id":"ch05-01-defining-structs.html#aフィールドのないユニット様よう構造体","title":"フィールドのないユニット様(よう)構造体"},"ch05-01-defining-structs.html#a構造体を定義しインスタンス化する":{"body":"構造体は第3章で議論したタプルと似ています。タプル同様、構造体の一部を異なる型にできます。 一方タプルとは違って、各データ片には名前をつけるので、値の意味が明確になります。 この名前のおかげで、構造体はタプルに比して、より柔軟になるわけです: データの順番に頼って、 インスタンスの値を指定したり、アクセスしたりする必要がないのです。 構造体の定義は、 struct キーワードを入れ、構造体全体に名前を付けます。構造体名は、 一つにグループ化されるデータ片の意義を表すものであるべきです。そして、波かっこ内に、 データ片の名前と型を定義し、これは フィールド と呼ばれます。例えば、リスト5-1では、 ユーザアカウントに関する情報を保持する構造体を示しています。 struct User { username: String, email: String, sign_in_count: u64, active: bool,\n} リスト5-1: User 構造体定義 構造体を定義した後に使用するには、各フィールドに対して具体的な値を指定して構造体の インスタンス を生成します。 インスタンスは、構造体名を記述し、 key: value ペアを含む波かっこを付け加えることで生成します。 ここで、キーはフィールド名、値はそのフィールドに格納したいデータになります。フィールドは、 構造体で宣言した通りの順番に指定する必要はありません。換言すると、構造体定義とは、 型に対する一般的な雛形のようなものであり、インスタンスは、その雛形を特定のデータで埋め、その型の値を生成するわけです。 例えば、リスト5-2で示されたように特定のユーザを宣言することができます。 # struct User {\n# username: String,\n# email: String,\n# sign_in_count: u64,\n# active: bool,\n# }\n#\nlet user1 = User { email: String::from(\"someone@example.com\"), username: String::from(\"someusername123\"), active: true, sign_in_count: 1,\n}; リスト5-2: User 構造体のインスタンスを生成する 構造体から特定の値を得るには、ドット記法が使えます。このユーザのEメールアドレスだけが欲しいなら、 この値を使いたかった場所全部で user1.email が使えます。インスタンスが可変であれば、 ドット記法を使い特定のフィールドに代入することで値を変更できます。リスト5-3では、 可変な User インスタンスの email フィールド値を変更する方法を示しています。 # struct User {\n# username: String,\n# email: String,\n# sign_in_count: u64,\n# active: bool,\n# }\n#\nlet mut user1 = User { email: String::from(\"someone@example.com\"), username: String::from(\"someusername123\"), active: true, sign_in_count: 1,\n}; user1.email = String::from(\"anotheremail@example.com\"); リスト5-3: ある User インスタンスの email フィールド値を変更する インスタンス全体が可変でなければならないことに注意してください; Rustでは、一部のフィールドのみを可変にすることはできないのです。 また、あらゆる式同様、構造体の新規インスタンスを関数本体の最後の式として生成して、 そのインスタンスを返すことを暗示できます。 リスト5-4は、与えられたemailとusernameで User インスタンスを生成する build_user 関数を示しています。 active フィールドには true 値が入り、 sign_in_count には値 1 が入ります。 # struct User {\n# username: String,\n# email: String,\n# sign_in_count: u64,\n# active: bool,\n# }\n#\nfn build_user(email: String, username: String) -> User { User { email: email, username: username, active: true, sign_in_count: 1, }\n} リスト5-4: Eメールとユーザ名を取り、 User インスタンスを返す build_user 関数 構造体のフィールドと同じ名前を関数の引数にもつけることは筋が通っていますが、 email と username というフィールド名と変数を繰り返さなきゃいけないのは、ちょっと面倒です。 構造体にもっとフィールドがあれば、名前を繰り返すことはさらに煩わしくなるでしょう。 幸運なことに、便利な省略記法があります!","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » 構造体を定義し、インスタンス化する","id":"ch05-01-defining-structs.html#a構造体を定義しインスタンス化する","title":"構造体を定義し、インスタンス化する"},"ch05-01-defining-structs.html#a構造体データの所有権":{"body":"リスト5-1の User 構造体定義において、 &str 文字列スライス型ではなく、所有権のある String 型を使用しました。 これは意図的な選択です。というのも、この構造体のインスタンスには全データを所有してもらう必要があり、 このデータは、構造体全体が有効な間はずっと有効である必要があるのです。 構造体に、他の何かに所有されたデータへの参照を保持させることもできますが、 そうするには ライフタイム という第10章で議論するRustの機能を使用しなければなりません。 ライフタイムのおかげで構造体に参照されたデータが、構造体自体が有効な間、ずっと有効であることを保証してくれるのです。 ライフタイムを指定せずに構造体に参照を保持させようとしたとしましょう。以下の通りですが、これは動きません: ファイル名: src/main.rs struct User { username: &str, email: &str, sign_in_count: u64, active: bool,\n} fn main() { let user1 = User { email: \"someone@example.com\", username: \"someusername123\", active: true, sign_in_count: 1, };\n} コンパイラは、ライフタイム指定子が必要だと怒るでしょう: error[E0106]: missing lifetime specifier\n(エラー: ライフタイム指定子がありません) --> | 2 | username: &str, | ^ expected lifetime parameter (ライフタイム引数を予期しました) error[E0106]: missing lifetime specifier --> | 3 | email: &str, | ^ expected lifetime parameter 第10章で、これらのエラーを解消して構造体に参照を保持する方法について議論しますが、 当面、今回のようなエラーは、 &str のような参照の代わりに、 String のような所有された型を使うことで修正します。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » 構造体データの所有権","id":"ch05-01-defining-structs.html#a構造体データの所有権","title":"構造体データの所有権"},"ch05-01-defining-structs.html#a構造体更新記法で他のインスタンスからインスタンスを生成する":{"body":"多くは前のインスタンスの値を使用しつつ、変更する箇所もある形で新しいインスタンスを生成できるとしばしば有用です。 構造体更新記法 でそうすることができます。 まず、リスト5-6では、更新記法なしで user2 に新しい User インスタンスを生成する方法を示しています。 email と username には新しい値をセットしていますが、それ以外にはリスト5-2で生成した user1 の値を使用しています。 # struct User {\n# username: String,\n# email: String,\n# sign_in_count: u64,\n# active: bool,\n# }\n#\n# let user1 = User {\n# email: String::from(\"someone@example.com\"),\n# username: String::from(\"someusername123\"),\n# active: true,\n# sign_in_count: 1,\n# };\n#\nlet user2 = User { email: String::from(\"another@example.com\"), username: String::from(\"anotherusername567\"), active: user1.active, sign_in_count: user1.sign_in_count,\n}; リスト5-6: user1 の一部の値を使用しつつ、新しい User インスタンスを生成する 構造体更新記法を使用すると、リスト5-7に示したように、コード量を減らしつつ、同じ効果を達成できます。 .. という記法により、 明示的にセットされていない残りのフィールドが、与えられたインスタンスのフィールドと同じ値になるように指定します。 # struct User {\n# username: String,\n# email: String,\n# sign_in_count: u64,\n# active: bool,\n# }\n#\n# let user1 = User {\n# email: String::from(\"someone@example.com\"),\n# username: String::from(\"someusername123\"),\n# active: true,\n# sign_in_count: 1,\n# };\n#\nlet user2 = User { email: String::from(\"another@example.com\"), username: String::from(\"anotherusername567\"), ..user1\n}; リスト5-7: 構造体更新記法を使用して、新しい User インスタンス用の値に新しい email と username をセットしつつ、 残りの値は、 user1 変数のフィールド値を使う リスト5-7のコードも、 email と username については異なる値、 active と sign_in_count フィールドについては、 user1 と同じ値になるインスタンスを user2 に生成します。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » 構造体更新記法で他のインスタンスからインスタンスを生成する","id":"ch05-01-defining-structs.html#a構造体更新記法で他のインスタンスからインスタンスを生成する","title":"構造体更新記法で他のインスタンスからインスタンスを生成する"},"ch05-01-defining-structs.html#a異なる型を生成する名前付きフィールドのないタプル構造体を使用する":{"body":"構造体名により追加の意味を含むものの、フィールドに紐づけられた名前がなく、むしろフィールドの型だけの タプル構造体 と呼ばれる、 タプルに似た構造体を定義することもできます。タプル構造体は、構造体名が提供する追加の意味は含むものの、 フィールドに紐付けられた名前はありません; むしろ、フィールドの型だけが存在します。タプル構造体は、タプル全体に名前をつけ、 そのタプルを他のタプルとは異なる型にしたい場合に有用ですが、普通の構造体のように各フィールド名を与えるのは、 冗長、または余計になるでしょう。 タプル構造体を定義するには、 struct キーワードの後に構造体名、さらにタプルに含まれる型を続けます。 例えば、こちらは、 Color と Point という2種類のタプル構造体の定義と使用法です: struct Color(i32, i32, i32);\nstruct Point(i32, i32, i32); let black = Color(0, 0, 0);\nlet origin = Point(0, 0, 0); black と origin の値は、違う型であることに注目してください。これらは、異なるタプル構造体のインスタンスだからですね。 定義された各構造体は、構造体内のフィールドが同じ型であっても、それ自身が独自の型になります。 例えば、 Color 型を引数に取る関数は、 Point を引数に取ることはできません。たとえ、両者の型が、 3つの i32 値からできていてもです。それ以外については、タプル構造体のインスタンスは、 タプルと同じように振る舞います: 分配して個々の部品にしたり、 . と添え字を使用して個々の値にアクセスするなどです。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » 異なる型を生成する名前付きフィールドのないタプル構造体を使用する","id":"ch05-01-defining-structs.html#a異なる型を生成する名前付きフィールドのないタプル構造体を使用する","title":"異なる型を生成する名前付きフィールドのないタプル構造体を使用する"},"ch05-02-example-structs.html#aタプルでリファクタリングする":{"body":"リスト5-9は、タプルを使う別バージョンのプログラムを示しています。 ファイル名: src/main.rs fn main() { let rect1 = (30, 50); println!( \"The area of the rectangle is {} square pixels.\", area(rect1) );\n} fn area(dimensions: (u32, u32)) -> u32 { dimensions.0 * dimensions.1\n} リスト5-9: タプルで四角形の幅と高さを指定する ある意味では、このプログラムはマシです。タプルのおかげで少し構造的になり、一引数を渡すだけになりました。 しかし別の意味では、このバージョンは明確性を失っています: タプルは要素に名前を付けないので、 計算が不明瞭になったのです。なぜなら、タプルの一部に添え字アクセスする必要があるからです。 面積計算で幅と高さを混在させるのなら問題はないのですが、四角形を画面に描画したいとなると、問題になるのです! タプルの添え字 0 が 幅 で、添え字 1 が 高さ であることを肝に銘じておかなければなりません。 他人がこのコードをいじることになったら、このことを割り出し、同様に肝に銘じなければならないでしょう。 容易く、このことを忘れたり、これらの値を混ぜこぜにしたりしてエラーを発生させてしまうでしょう。 データの意味をコードに載せていないからです。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » タプルでリファクタリングする","id":"ch05-02-example-structs.html#aタプルでリファクタリングする","title":"タプルでリファクタリングする"},"ch05-02-example-structs.html#aトレイトの導出で有用な機能を追加する":{"body":"プログラムのデバッグをしている間に、 Rectangle のインスタンスを出力し、フィールドの値を確認できると、 素晴らしいわけです。リスト5-11では、以前の章のように、 println! マクロを試しに使用しようとしていますが、動きません。 ファイル名: src/main.rs struct Rectangle { width: u32, height: u32,\n} fn main() { let rect1 = Rectangle { width: 30, height: 50 }; // rect1は{}です println!(\"rect1 is {}\", rect1);\n} リスト5-11: Rectangle のインスタンスを出力しようとする このコードを走らせると、こんな感じのエラーが出ます: error[E0277]: the trait bound `Rectangle: std::fmt::Display` is not satisfied\n(エラー: トレイト境界`Rectangle: std::fmt::Display`が満たされていません) println! マクロには、様々な整形があり、標準では、波括弧は Display として知られる整形をするよう、 println! に指示するのです: 直接エンドユーザ向けの出力です。これまでに見てきた基本型は、 標準で Display を実装しています。というのも、 1 や他の基本型をユーザに見せる方法は一つしかないからです。 しかし構造体では、 println! が出力を整形する方法は自明ではなくなります。出力方法がいくつもあるからです: カンマは必要なの?波かっこを出力する必要はある?全フィールドが見えるべき?この曖昧性のため、 Rustは必要なものを推測しようとせず、構造体には Display 実装が提供されないのです。 エラーを読み下すと、こんな有益な注意書きがあります: `Rectangle` cannot be formatted with the default formatter; try using\n`:?` instead if you are using a format string\n(注釈: `Rectangle`は、デフォルト整形機では、整形できません; フォーマット文字列を使うのなら\n代わりに`:?`を試してみてください) 試してみましょう! pritnln! マクロ呼び出しは、 println!(\"rect1 is {:?}\", rect1); という見た目になるでしょう。 波括弧内に :? という指定子を書くと、 println! に Debug と呼ばれる出力整形を使いたいと指示するのです。 Debug トレイトは、開発者にとって有用な方法で構造体を出力させてくれるので、 コードをデバッグしている最中に、値を確認することができます。 変更してコードを走らせてください。なに!まだエラーが出ます: error[E0277]: the trait bound `Rectangle: std::fmt::Debug` is not satisfied\n(エラー: トレイト境界`Rectangle: std::fmt::Debug`が満たされていません) しかし今回も、コンパイラは有益な注意書きを残してくれています: `Rectangle` cannot be formatted using `:?`; if it is defined in your\ncrate, add `#[derive(Debug)]` or manually implement it\n(注釈: `Rectangle`は`:?`を使って整形できません; 自分のクレートで定義しているのなら\n`#[derive(Debug)]`を追加するか、手動で実装してください) 確かに Rustにはデバッグ用の情報を出力する機能が備わっていますが、この機能を構造体で使えるようにするには、 明示的な選択をしなければならないのです。そうするには、構造体定義の直前に #[derive(Debug)] という注釈を追加します。 そう、リスト5-12で示されている通りです。 ファイル名: src/main.rs #[derive(Debug)]\nstruct Rectangle { width: u32, height: u32,\n} fn main() { let rect1 = Rectangle { width: 30, height: 50 }; println!(\"rect1 is {:?}\", rect1);\n} リスト5-12: Debug トレイトを導出する注釈を追加し、 Rectangle インスタンスをデバッグ用整形機で出力する これでプログラムを実行すれば、エラーは出ず、以下のような出力が得られるでしょう: rect1 is Rectangle { width: 30, height: 50 } 素晴らしい!最善の出力ではないものの、このインスタンスの全フィールドの値を出力しているので、 デバッグ中には間違いなく役に立つでしょう。より大きな構造体があるなら、もう少し読みやすい出力の方が有用です; そのような場合には、 println! 文字列中の {:?} の代わりに {:#?} を使うことができます。 この例で {:#?} というスタイルを使用したら、出力は以下のようになるでしょう: rect1 is Rectangle { width: 30, height: 50\n} Rustには、 derive 注釈で使えるトレイトが多く提供されており、独自の型に有用な振る舞いを追加することができます。 そのようなトレイトとその振る舞いは、付録Cで一覧になっています。 これらのトレイトを独自の動作とともに実装する方法だけでなく、独自のトレイトを生成する方法については、第10章で解説します。 area 関数は、非常に特殊です: 四角形の面積を算出するだけです。 Rectangle 構造体とこの動作をより緊密に結び付けられると、 役に立つでしょう。なぜなら、他のどんな型でもうまく動作しなくなるからです。 area 関数を Rectangle 型に定義された area メソッド に変形することで、 このコードをリファクタリングし続けられる方法について見ていきましょう。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » トレイトの導出で有用な機能を追加する","id":"ch05-02-example-structs.html#aトレイトの導出で有用な機能を追加する","title":"トレイトの導出で有用な機能を追加する"},"ch05-02-example-structs.html#a構造体でリファクタリングする-より意味付けする":{"body":"データにラベル付けをして意味付けを行い、構造体を使います。現在使用しているタプルを全体と一部に名前のあるデータ型に、 変形することができます。そう、リスト5-10に示したように。 ファイル名: src/main.rs struct Rectangle { width: u32, height: u32,\n} fn main() { let rect1 = Rectangle { width: 30, height: 50 }; println!( \"The area of the rectangle is {} square pixels.\", area(&rect1) );\n} fn area(rectangle: &Rectangle) -> u32 { rectangle.width * rectangle.height\n} リスト5-10: Rectangle 構造体を定義する ここでは、構造体を定義し、 Rectangle という名前にしています。波括弧の中で width と height というフィールドを定義し、 u32 という型にしました。それから main 内で Rectangle の特定のインスタンスを生成し、 幅を30、高さを50にしました。 これで area 関数は引数が一つになり、この引数は名前が rectangle 、型は Rectangle 構造体インスタンスへの不変借用になりました。 第4章で触れたように、構造体の所有権を奪うよりも借用する必要があります。こうすることで main は所有権を保って、 rect1 を使用し続けることができ、そのために関数シグニチャと関数呼び出し時に & を使っているわけです。 area 関数は、 Rectangle インスタンスの width と height フィールドにアクセスしています。 これで、 area の関数シグニチャは、我々の意図をズバリ示すようになりました: width と height フィールドを使って、 Rectangle の面積を計算します。これにより、幅と高さが相互に関係していることが伝わり、 タプルの 0 や 1 という添え字を使うよりも、これらの値に説明的な名前を与えられるのです。プログラムの意図が明瞭になりました。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » 構造体でリファクタリングする: より意味付けする","id":"ch05-02-example-structs.html#a構造体でリファクタリングする-より意味付けする","title":"構造体でリファクタリングする: より意味付けする"},"ch05-02-example-structs.html#a構造体を使ったプログラム例":{"body":"構造体を使用したくなる可能性のあるケースを理解するために、四角形の面積を求めるプログラムを書きましょう。 単一の変数から始め、代わりに構造体を使うようにプログラムをリファクタリングします。 Cargoで rectangles という新規バイナリプロジェクトを作成しましょう。このプロジェクトは、 四角形の幅と高さをピクセルで指定し、その面積を求めます。リスト5-8に、プロジェクトの src/main.rs で、 正にそうする一例を短いプログラムとして示しました。 ファイル名: src/main.rs fn main() { let width1 = 30; let height1 = 50; println!( // 四角形の面積は、{}平方ピクセルです \"The area of the rectangle is {} square pixels.\", area(width1, height1) );\n} fn area(width: u32, height: u32) -> u32 { width * height\n} リスト5-8: 個別の幅と高さ変数を指定して四角形の面積を求める では、 cargo run でこのプログラムを走らせてください: The area of the rectangle is 1500 square pixels.\n(四角形の面積は、1500平方ピクセルです)","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » 構造体を使ったプログラム例","id":"ch05-02-example-structs.html#a構造体を使ったプログラム例","title":"構造体を使ったプログラム例"},"ch05-03-method-syntax.html#a--演算子はどこに行ったの":{"body":"CとC++では、メソッド呼び出しには2種類の異なる演算子が使用されます: オブジェクトに対して直接メソッドを呼び出すのなら、 . を使用するし、オブジェクトのポインタに対してメソッドを呼び出し、 先にポインタを参照外しする必要があるなら、 -> を使用するわけです。 言い換えると、 object がポインタなら、 object->something() は、 (*object).something() と同等なのです。 Rustには -> 演算子の代わりとなるようなものはありません; その代わり、Rustには、 自動参照および参照外し という機能があります。Rustにおいてメソッド呼び出しは、 この動作が行われる数少ない箇所なのです。 動作方法はこうです: object.something() とメソッドを呼び出すと、 コンパイラは object がメソッドのシグニチャと合致するように、自動で & か &mut 、 * を付与するのです。 要するに、以下のコードは同じものです: # #[derive(Debug,Copy,Clone)]\n# struct Point {\n# x: f64,\n# y: f64,\n# }\n#\n# impl Point {\n# fn distance(&self, other: &Point) -> f64 {\n# let x_squared = f64::powi(other.x - self.x, 2);\n# let y_squared = f64::powi(other.y - self.y, 2);\n#\n# f64::sqrt(x_squared + y_squared)\n# }\n# }\n# let p1 = Point { x: 0.0, y: 0.0 };\n# let p2 = Point { x: 5.0, y: 6.5 };\np1.distance(&p2);\n(&p1).distance(&p2); 前者の方がずっと明確です。メソッドには自明な受け手( self の型)がいるので、この自動参照機能は動作するのです。 受け手とメソッド名が与えられれば、コンパイラは確実にメソッドが読み込み専用( &self )か、書き込みもする( &mut self )のか、 所有権を奪う( self )のか判断できるわけです。メソッドの受け手に関して借用が明示されないというのが、 所有権を実際に使うのがRustにおいて簡単である大きな理由です。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » -> 演算子はどこに行ったの?","id":"ch05-03-method-syntax.html#a--演算子はどこに行ったの","title":"-> 演算子はどこに行ったの?"},"ch05-03-method-syntax.html#aまとめ":{"body":"構造体により、自分の領域で意味のある独自の型を作成することができます。構造体を使用することで、 関連のあるデータ片を相互に結合させたままにし、各部品に名前を付け、コードを明確にすることができます。 メソッドにより、構造体のインスタンスが行う動作を指定することができ、関連関数により、 構造体に特有の機能をインスタンスを利用することなく、名前空間分けすることができます。 しかし、構造体だけが独自の型を作成する手段ではありません: Rustのenum機能に目を向けて、 別の道具を道具箱に追加しましょう。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » まとめ","id":"ch05-03-method-syntax.html#aまとめ","title":"まとめ"},"ch05-03-method-syntax.html#aより引数の多いメソッド":{"body":"Rectangle 構造体に2番目のメソッドを実装して、メソッドを使う鍛錬をしましょう。今回は、 Rectangle のインスタンスに、 別の Rectangle のインスタンスを取らせ、2番目の Rectangle が self に完全にはめ込まれたら、 true を返すようにしたいのです; そうでなければ、 false を返すべきです。つまり、一旦 can_hold メソッドを定義したら、 リスト5-14のようなプログラムを書けるようになりたいのです。 ファイル名: src/main.rs fn main() { let rect1 = Rectangle { width: 30, height: 50 }; let rect2 = Rectangle { width: 10, height: 40 }; let rect3 = Rectangle { width: 60, height: 45 }; // rect1にrect2ははまり込む? println!(\"Can rect1 hold rect2? {}\", rect1.can_hold(&rect2)); println!(\"Can rect1 hold rect3? {}\", rect1.can_hold(&rect3));\n} リスト5-14: 未完成の can_hold を使用する そして、予期される出力は以下のようになります。なぜなら、 rect2 の各次元は rect1 よりも小さいものの、 rect3 は rect1 より幅が広いからです: Can rect1 hold rect2? true\nCan rect1 hold rect3? false メソッドを定義したいことはわかっているので、 impl Rectangle ブロック内での話になります。 メソッド名は、 can_hold になり、引数として別の Rectangle を不変借用で取るでしょう。 メソッドを呼び出すコードを見れば、引数の型が何になるかわかります: rect1.can_hold(&rect2) は、 &rect2 、 Rectangle のインスタンスである rect2 への不変借用を渡しています。 これは道理が通っています。なぜなら、 rect2 を読み込む(書き込みではなく。この場合、可変借用が必要になります)だけでよく、 can_hold メソッドを呼び出した後にも rect2 が使えるよう、所有権を main に残したままにしたいからです。 can_hold の返り値は、booleanになり、メソッドの中身は、 self の幅と高さがもう一つの Rectangle の幅と高さよりも、 それぞれ大きいことを確認します。リスト5-13の impl ブロックに新しい can_hold メソッドを追記しましょう。 リスト5-15に示した通りです。 ファイル名: src/main.rs # #[derive(Debug)]\n# struct Rectangle {\n# width: u32,\n# height: u32,\n# }\n#\nimpl Rectangle { fn area(&self) -> u32 { self.width * self.height } fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height }\n} リスト5-15: 別の Rectangle のインスタンスを引数として取る can_hold メソッドを、 Rectangle に実装する このコードをリスト5-14の main 関数と合わせて実行すると、望み通りの出力が得られます。 メソッドは、 self 引数の後にシグニチャに追加した引数を複数取ることができ、 その引数は、関数の引数と同様に動作するのです。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » より引数の多いメソッド","id":"ch05-03-method-syntax.html#aより引数の多いメソッド","title":"より引数の多いメソッド"},"ch05-03-method-syntax.html#aメソッドを定義する":{"body":"Rectangle インスタンスを引数に取る area 関数を変え、代わりに Rectangle 構造体上に area メソッドを作りましょう。 リスト5-13に示した通りですね。 ファイル名: src/main.rs #[derive(Debug)]\nstruct Rectangle { width: u32, height: u32,\n} impl Rectangle { fn area(&self) -> u32 { self.width * self.height }\n} fn main() { let rect1 = Rectangle { width: 30, height: 50 }; println!( \"The area of the rectangle is {} square pixels.\", rect1.area() );\n} リスト5-13: Rectangle 構造体上に area メソッドを定義する Rectangle の文脈内で関数を定義するには、 impl (implementation; 実装)ブロックを始めます。 それから area 関数を impl の波かっこ内に移動させ、最初の(今回は唯一の)引数をシグニチャ内と本体内全てで self に変えます。 area 関数を呼び出し、 rect1 を引数として渡す main では、代替としてメソッド記法を使用して、 Rectangle インスタンスの area メソッドを呼び出せます。メソッド記法は、インスタンスの後に続きます: ドット、メソッド名、かっこ、そして引数と続くわけです。 area のシグニチャでは、 rectangle: &Rectangle の代わりに &self を使用しています。 というのも、コンパイラは、このメソッドが impl Rectangle という文脈内に存在するために、 self の型が Rectangle であると把握しているからです。 &Rectangle と同様に、 self の直前に & を使用していることに注意してください。メソッドは、 self の所有権を奪ったり、 ここでしているように不変で self を借用したり、可変で self を借用したりできるのです。 他の引数と全く同じですね。 ここで &self を選んでいるのは、関数バージョンで &Rectangle を使用していたのと同様の理由です: 所有権はいらず、構造体のデータを読み込みたいだけで、書き込む必要はないわけです。 メソッドの一部でメソッドを呼び出したインスタンスを変更したかったら、第1引数に &mut self を使用するでしょう。 self だけを第1引数にしてインスタンスの所有権を奪うメソッドを定義することは稀です; このテクニックは通常、 メソッドが self を何か別のものに変形し、変形後に呼び出し元が元のインスタンスを使用できないようにしたい場合に使用されます。 関数の代替としてメソッドを使う主な利点は、メソッド記法を使用して全メソッドのシグニチャで self の型を繰り返す必要がなくなる以外だと、 体系化です。コードの将来的な利用者に Rectangle の機能を提供しているライブラリ内の各所でその機能を探させるのではなく、 この型のインスタンスでできることを一つの impl ブロックにまとめあげています。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » メソッドを定義する","id":"ch05-03-method-syntax.html#aメソッドを定義する","title":"メソッドを定義する"},"ch05-03-method-syntax.html#aメソッド記法":{"body":"メソッド は関数に似ています: fn キーワードと名前で宣言されるし、引数と返り値があるし、 どこか別の場所で呼び出された時に実行されるコードを含みます。ところが、 メソッドは構造体の文脈(あるいはenumかトレイトオブジェクトの。これらについては各々第6章と17章で解説します)で定義されるという点で、 関数とは異なり、最初の引数は必ず self になり、これはメソッドが呼び出されている構造体インスタンスを表します。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » メソッド記法","id":"ch05-03-method-syntax.html#aメソッド記法","title":"メソッド記法"},"ch05-03-method-syntax.html#a複数の-impl-ブロック":{"body":"各構造体には、複数の impl ブロックを存在させることができます。例えば、リスト5-15はリスト5-16に示したコードと等価で、 リスト5-16では、各メソッドごとに impl ブロックを用意しています。 # #[derive(Debug)]\n# struct Rectangle {\n# width: u32,\n# height: u32,\n# }\n#\nimpl Rectangle { fn area(&self) -> u32 { self.width * self.height }\n} impl Rectangle { fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height }\n} リスト5-16: 複数の impl ブロックを使用してリスト5-15を書き直す ここでこれらのメソッドを個々の impl ブロックに分ける理由はないのですが、合法な書き方です。 複数の impl ブロックが有用になるケースは第10章で見ますが、そこではジェネリック型と、トレイトについて議論します。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » 複数の impl ブロック","id":"ch05-03-method-syntax.html#a複数の-impl-ブロック","title":"複数の impl ブロック"},"ch05-03-method-syntax.html#a関連関数":{"body":"impl ブロックの別の有益な機能は、 impl ブロック内に self を引数に取ら ない 関数を定義できることです。 これは、構造体に関連付けられているので、 関連関数 と呼ばれます。それでも、関連関数は関数であり、メソッドではありません。 というのも、対象となる構造体のインスタンスが存在しないからです。もう String::from という関連関数を使用したことがありますね。 関連関数は、構造体の新規インスタンスを返すコンストラクタによく使用されます。例えば、一次元の引数を取り、 長さと幅両方に使用する関連関数を提供することができ、その結果、同じ値を2回指定する必要なく、 正方形の Rectangle を生成しやすくすることができます。 ファイル名: src/main.rs # #[derive(Debug)]\n# struct Rectangle {\n# width: u32,\n# height: u32,\n# }\n#\nimpl Rectangle { fn square(size: u32) -> Rectangle { Rectangle { width: size, height: size } }\n} この関連関数を呼び出すために、構造体名と一緒に :: 記法を使用します; 一例は let sq = Rectangle::square(3); です。 この関数は、構造体によって名前空間分けされています: :: という記法は、関連関数とモジュールによって作り出される名前空間両方に使用されます。 モジュールについては第7章で議論します。","breadcrumbs":"構造体を使用して関連のあるデータを構造化する » 関連関数","id":"ch05-03-method-syntax.html#a関連関数","title":"関連関数"},"ch06-00-enums.html#enumとパターンマッチング":{"body":"この章では、 列挙型 について見ていきます。列挙型は、 enum とも称されます。enumは、取りうる値を列挙することで、 型を定義させてくれます。最初に、enumを定義し、使用して、enumがデータとともに意味をコード化する方法を示します。 次に、特別に有用なenumである Option について掘り下げていきましょう。この型は、 値が何かかなんでもないかを表現します。それから、 match 式のパターンマッチングにより、 どうenumの色々な値に対して異なるコードを走らせやすくなるかを見ます。最後に、 if let 文法要素も、 如何(いか)にenumをコードで扱う際に使用可能な便利で簡潔な慣用句であるかを解説します。 enumは多くの言語に存在する機能ですが、その能力は言語ごとに異なります。Rustのenumは、F#、OCaml、Haskellなどの、 関数型言語に存在する 代数的データ型 に最も酷似しています。","breadcrumbs":"Enumとパターンマッチング","id":"ch06-00-enums.html#enumとパターンマッチング","title":"Enumとパターンマッチング"},"ch06-01-defining-an-enum.html#enumの値":{"body":"以下のようにして、 IpAddrKind の各列挙子のインスタンスは生成できます: # enum IpAddrKind {\n# V4,\n# V6,\n# }\n#\nlet four = IpAddrKind::V4;\nlet six = IpAddrKind::V6; enumの列挙子は、その識別子の元に名前空間分けされていることと、 2連コロンを使ってその二つを区別していることに注意してください。 これが有効な理由は、こうすることで、値 IpAddrKind::V4 と IpAddrKind::V6 という値は両方とも、 同じ型 IpAddrKind になったからです。そうしたら、例えば、どんな IpAddrKind を取る関数も定義できるようになります。 # enum IpAddrKind {\n# V4,\n# V6,\n# }\n#\nfn route(ip_type: IpAddrKind) { } そして、この関数をどちらの列挙子に対しても呼び出せます: # enum IpAddrKind {\n# V4,\n# V6,\n# }\n#\n# fn route(ip_type: IpAddrKind) { }\n#\nroute(IpAddrKind::V4);\nroute(IpAddrKind::V6); enumの利用には、さらなる利点さえもあります。このIPアドレス型についてもっと考えてみると、現状では、 実際のIPアドレスの データ を保持する方法がありません。つまり、どんな 種類 であるかを知っているだけです。 構造体について第5章で学んだばっかりとすると、この問題に対して、あなたはリスト6-1のように対処するかもしれません。 enum IpAddrKind { V4, V6,\n} struct IpAddr { kind: IpAddrKind, address: String,\n} let home = IpAddr { kind: IpAddrKind::V4, address: String::from(\"127.0.0.1\"),\n}; let loopback = IpAddr { kind: IpAddrKind::V6, address: String::from(\"::1\"),\n}; リスト6-1: IPアドレスのデータと IpAddrKind の列挙子を struct を使って保持する ここでは、二つのフィールドを持つ IpAddr という構造体を定義しています: IpAddrKind 型(先ほど定義したenumですね)の kind フィールドと、 String 型の address フィールドです。この構造体のインスタンスが2つあります。最初のインスタンス、 home には kind として IpAddrKind::V4 があり、紐付けられたアドレスデータは 127.0.0.1 です。 2番目のインスタンス、 loopback には、 kind の値として、 IpAddrKind のもう一つの列挙子、 V6 があり、 アドレス ::1 が紐付いています。構造体を使って kind と address 値を一緒に包んだので、 もう列挙子は値と紐付けられています。 各enumの列挙子に直接データを格納して、enumを構造体内に使うというよりもenumだけを使って、 同じ概念をもっと簡潔な方法で表現することができます。この新しい IpAddr の定義は、 V4 と V6 列挙子両方に String 値が紐付けられていることを述べています。 enum IpAddr { V4(String), V6(String),\n} let home = IpAddr::V4(String::from(\"127.0.0.1\")); let loopback = IpAddr::V6(String::from(\"::1\")); enumの各列挙子にデータを直接添付できるので、余計な構造体を作る必要は全くありません。 構造体よりもenumを使うことには、別の利点もあります: 各列挙子に紐付けるデータの型と量は、異なってもいいのです。 バージョン4のIPアドレスには、常に0から255の値を持つ4つの数値があります。 V4 のアドレスは、4つの u8 型の値として格納するけれども、 V6 のアドレスは引き続き、単独の String 型の値で格納したかったとしても、構造体では不可能です。 enumなら、こんな場合も容易に対応できます: enum IpAddr { V4(u8, u8, u8, u8), V6(String),\n} let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from(\"::1\")); バージョン4とバージョン6のIPアドレスを格納するデータ構造を定義する複数の異なる方法を示してきました。 しかしながら、蓋を開けてみれば、IPアドレスを格納してその種類をコード化したくなるということは一般的なので、 標準ライブラリに使用可能な定義があります! 標準ライブラリでの IpAddr の定義のされ方を見てみましょう: 私たちが定義し、使用したのと全く同じenumと列挙子がありますが、アドレスデータを二種の異なる構造体の形で列挙子に埋め込み、 この構造体は各列挙子用に異なる形で定義されています。 struct Ipv4Addr { // 省略\n} struct Ipv6Addr { // 省略\n} enum IpAddr { V4(Ipv4Addr), V6(Ipv6Addr),\n} このコードは、enum列挙子内にいかなる種類のデータでも格納できることを描き出しています: 例を挙げれば、文字列、数値型、構造体などです。他のenumを含むことさえできます!また、 標準ライブラリの型は、あなたの想像するよりも複雑ではないことがしばしばあります。 標準ライブラリに IpAddr に対する定義は含まれるものの、標準ライブラリの定義をまだ我々のスコープに導入していないので、 干渉することなく自分自身の定義を生成して使用できることに注意してください。型をスコープに導入することについては、 第7章でもっと詳しく言及します。 リスト6-2でenumの別の例を見てみましょう: 今回のコードは、幅広い種類の型が列挙子に埋め込まれています。 enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32),\n} リスト6-2: 列挙子各々が異なる型と量の値を格納する Message enum このenumには、異なる型の列挙子が4つあります: Quit には紐付けられたデータは全くなし。 Move は、中に匿名構造体を含む。 Write は、単独の String オブジェクトを含む。 ChangeColor は、3つの i32 値を含む。 リスト6-2のような列挙子を含むenumを定義することは、enumの場合、 struct キーワードを使わず、 全部の列挙子が Message 型の元に分類される点を除いて、異なる種類の構造体定義を定義するのと類似しています。 以下の構造体も、先ほどのenumの列挙子が保持しているのと同じデータを格納することができるでしょう: struct QuitMessage; // ユニット構造体\nstruct MoveMessage { x: i32, y: i32,\n}\nstruct WriteMessage(String); // タプル構造体\nstruct ChangeColorMessage(i32, i32, i32); // タプル構造体 ですが、異なる構造体を使っていたら、各々、それ自身の型があるので、単独の型になるリスト6-2で定義した Message enumほど、 これらの種のメッセージいずれもとる関数を簡単に定義することはできないでしょう。 enumと構造体にはもう1点似通っているところがあります: impl を使って構造体にメソッドを定義できるのと全く同様に、 enumにもメソッドを定義することができるのです。こちらは、 Message enum上に定義できる call という名前のメソッドです: # enum Message {\n# Quit,\n# Move { x: i32, y: i32 },\n# Write(String),\n# ChangeColor(i32, i32, i32),\n# }\n#\nimpl Message { fn call(&self) { // method body would be defined here // メソッド本体はここに定義される }\n} let m = Message::Write(String::from(\"hello\"));\nm.call(); メソッドの本体では、 self を使用して、メソッドを呼び出した相手の値を取得できるでしょう。この例では、 Message::Write(String::from(\"hello\")) という値を持つ、変数 m を生成したので、これが m.call() を走らせた時に、 call メソッドの本体内で self が表す値になります。 非常に一般的で有用な別の標準ライブラリのenumを見てみましょう: Option です。","breadcrumbs":"Enumとパターンマッチング » Enumの値","id":"ch06-01-defining-an-enum.html#enumの値","title":"Enumの値"},"ch06-01-defining-an-enum.html#enumを定義する":{"body":"コードで表現したくなるかもしれない場面に目を向けて、enumが有用でこの場合、構造体よりも適切である理由を確認しましょう。 IPアドレスを扱う必要が出たとしましょう。現在、IPアドレスの規格は二つあります: バージョン4とバージョン6です。 これらは、プログラムが遭遇するIPアドレスのすべての可能性です: 列挙型は、取りうる値をすべて 列挙 でき、 これが列挙型の名前の由来です。 どんなIPアドレスも、バージョン4かバージョン6のどちらかになりますが、同時に両方にはなり得ません。 IPアドレスのその特性により、enumデータ構造が適切なものになります。というのも、 enumの値は、その列挙子のいずれか一つにしかなり得ないからです。バージョン4とバージョン6のアドレスは、 どちらも根源的にはIPアドレスですから、コードがいかなる種類のIPアドレスにも適用される場面を扱う際には、 同じ型として扱われるべきです。 この概念をコードでは、 IpAddrKind 列挙型を定義し、IPアドレスがなりうる種類、 V4 と V6 を列挙することで、 表現できます。これらは、enumの 列挙子 として知られています: enum IpAddrKind { V4, V6,\n} これで、 IpAddrKind はコードの他の場所で使用できる独自のデータ型になります。","breadcrumbs":"Enumとパターンマッチング » Enumを定義する","id":"ch06-01-defining-an-enum.html#enumを定義する","title":"Enumを定義する"},"ch06-01-defining-an-enum.html#option--enumとnull値に勝る利点":{"body":"前節で、 IpAddr enumがRustの型システムを使用して、プログラムにデータ以上の情報をコード化できる方法を目撃しました。 この節では、 Option のケーススタディを掘り下げていきます。この型も標準ライブラリにより定義されているenumです。 この Option 型はいろんな箇所で使用されます。なぜなら、値が何かかそうでないかという非常に一般的な筋書きをコード化するからです。 この概念を型システムの観点で表現することは、コンパイラが、プログラマが処理すべき場面全てを処理していることをチェックできることを意味します; この機能は、他の言語において、究極的にありふれたバグを阻止することができます。 プログラミング言語のデザインは、しばしばどの機能を入れるかという観点で考えられるが、 除いた機能も重要なのです。Rustには、他の多くの言語にはあるnull機能がありません。 null とはそこに何も値がないことを意味する値です。nullのある言語において、 変数は常に二者択一どちらかの状態になります: nullかそうでないかです。 nullの開発者であるトニー・ホーア(Tony Hoare)の2009年のプレゼンテーション、 \"Null References: The Billion Dollar Mistake\"(Null参照: 10億ドルの間違い)では、こんなことが語られています。 私はそれを10億ドルの失敗と呼んでいます。その頃、私は、オブジェクト指向言語の参照に対する、 最初のわかりやすい型システムを設計していました。私の目標は、 どんな参照の使用も全て完全に安全であるべきことを、コンパイラにそのチェックを自動で行ってもらって保証することだったのです。 しかし、null参照を入れるという誘惑に打ち勝つことができませんでした。それは、単純に実装が非常に容易だったからです。 これが無数のエラーや脆弱性、システムクラッシュにつながり、過去40年で10億ドルの苦痛や損害を引き起こしたであろうということなのです。 null値の問題は、nullの値をnullでない値のように使用しようとしたら、何らかの種類のエラーが出ることです。 このnullかそうでないかという特性は広く存在するので、この種の間違いを大変犯しやすいのです。 しかしながら、nullが表現しようとしている概念は、それでも役に立つものです: nullは、 何らかの理由で現在無効、または存在しない値のことなのです。 問題は、全く概念にあるのではなく、特定の実装にあるのです。そんな感じなので、Rustにはnullがありませんが、 値が存在するか不在かという概念をコード化するenumならあります。このenumが Option で、 以下のように 標準ライブラリに定義 されています。 enum Option { Some(T), None,\n} Option は有益すぎて、初期化処理(prelude)にさえ含まれています。つまり、明示的にスコープに導入する必要がないのです。 さらに、列挙子もそうなっています: Some と None を Option:: の接頭辞なしに直接使えるわけです。 ただ、 Option はそうは言っても、普通のenumであり、 Some(T) と None も Option 型のただの列挙子です。 という記法は、まだ語っていないRustの機能です。これは、ジェネリック型引数であり、ジェネリクスについて詳しくは、 第10章で解説します。とりあえず、知っておく必要があることは、 は、 Option enumの Some 列挙子が、 あらゆる型のデータを1つだけ持つことができることを意味していることだけです。こちらは、 Option 値を使って、数値型や文字列型を保持する例です。 let some_number = Some(5);\nlet some_string = Some(\"a string\"); let absent_number: Option = None; Some ではなく、 None を使ったら、コンパイラに Option の型が何になるかを教えなければいけません。 というのも、 None 値を見ただけでは、 Some 列挙子が保持する型をコンパイラが推論できないからです。 Some 値がある時、値が存在するとわかり、その値は、 Some に保持されています。 None 値がある場合、 ある意味、nullと同じことを意図します: 有効な値がないのです。では、なぜ Option の方が、 nullよりも少しでも好ましいのでしょうか? 簡潔に述べると、 Option と T (ここで T はどんな型でもいい)は異なる型なので、 コンパイラが Option 値を確実に有効な値かのようには使用させてくれません。 例えば、このコードは i8 を Option に足そうとしているので、コンパイルできません。 let x: i8 = 5;\nlet y: Option = Some(5); let sum = x + y; このコードを動かしたら、以下のようなエラーメッセージが出ます。 error[E0277]: the trait bound `i8: std::ops::Add>` is\nnot satisfied\n(エラー: `i8: std::ops::Add>`というトレイト境界が満たされていません) --> |\n5 | let sum = x + y; | ^ no implementation for `i8 + std::option::Option` | なんて強烈な!実際に、このエラーメッセージは、 i8 と Option が異なる型なので、 足し合わせる方法がコンパイラにはわからないことを意味します。Rustにおいて、 i8 のような型の値がある場合、 コンパイラが常に有効な値であることを確認してくれます。この値を使う前にnullであることをチェックする必要なく、 自信を持って先に進むことができるのです。 Option がある時(あるいはどんな型を扱おうとしていても)のみ、 値を保持していない可能性を心配する必要があるわけであり、 コンパイラはプログラマが値を使用する前にそのような場面を扱っているか確かめてくれます。 言い換えると、 T 型の処理を行う前には、 Option を T に変換する必要があるわけです。一般的に、 これにより、nullの最もありふれた問題の一つを捕捉する一助になります: 実際にはnullなのに、 そうでないかのように想定することです。 不正確にnullでない値を想定する心配をしなくてもよいということは、コード内でより自信を持てることになります。 nullになる可能性のある値を保持するには、その値の型を Option にすることで明示的に同意しなければなりません。 それからその値を使用する際には、値がnullである場合を明示的に処理する必要があります。 値が Option 以外の型であるとこ全てにおいて、値がnullでないと安全に想定することが できます 。 これは、Rustにとって、意図的な設計上の決定であり、nullの普遍性を制限し、Rustコードの安全性を向上させます。 では、 Option 型の値がある時、その値を使えるようにするには、どのように Some 列挙子から T 型の値を取り出せばいいのでしょうか? Option には様々な場面で有効に活用できる非常に多くのメソッドが用意されています; ドキュメント でそれらを確認できます。 Option のメソッドに馴染むと、 Rustの旅が極めて有益になるでしょう。 一般的に、 Option 値を使うには、各列挙子を処理するコードが欲しくなります。 Some(T) 値がある時だけ走る何らかのコードが欲しくなり、このコードが内部の T を使用できます。 None 値があった場合に走る別のコードが欲しくなり、そちらのコードは T 値は使用できない状態になります。 match 式が、enumとともに使用した時にこれだけの動作をするフロー制御文法要素になります: enumの列挙子によって、違うコードが走り、そのコードがマッチした値の中のデータを使用できるのです。","breadcrumbs":"Enumとパターンマッチング » Option enumとNull値に勝る利点","id":"ch06-01-defining-an-enum.html#option--enumとnull値に勝る利点","title":"Option enumとNull値に勝る利点"},"ch06-02-match.html#a_-というプレースホルダー":{"body":"Rustには、全ての可能性を列挙したくない時に使用できるパターンもあります。例えば、 u8 は、有効な値として、 0から255までを取ります。1、3、5、7の値にだけ興味があったら、0、2、4、6、8、9と255までの数値を列挙する必要に迫られたくはないです。 幸運なことに、する必要はありません: 代わりに特別なパターンの _ を使用できます: let some_u8_value = 0u8;\nmatch some_u8_value { 1 => println!(\"one\"), 3 => println!(\"three\"), 5 => println!(\"five\"), 7 => println!(\"seven\"), _ => (),\n} _ というパターンは、どんな値にもマッチします。他のアームの後に記述することで、 _ は、 それまでに指定されていない全ての可能性にマッチします。 () は、ただのユニット値なので、 _ の場合には、 何も起こりません。結果として、 _ プレースホルダーの前に列挙していない可能性全てに対しては、 何もしたくないと言えるわけです。 ですが、 一つ のケースにしか興味がないような場面では、 match 式はちょっと長ったらしすぎます。 このような場面用に、Rustには、 if let が用意されています。","breadcrumbs":"Enumとパターンマッチング » _ というプレースホルダー","id":"ch06-02-match.html#a_-というプレースホルダー","title":"_ というプレースホルダー"},"ch06-02-match.html#aマッチは包括的":{"body":"もう一つ議論する必要のある match の観点があります。一点バグがありコンパイルできないこんなバージョンの plus_one 関数を考えてください: fn plus_one(x: Option) -> Option { match x { Some(i) => Some(i + 1), }\n} None の場合を扱っていないため、このコードはバグを生みます。幸い、コンパイラが捕捉できるバグです。 このコードのコンパイルを試みると、こんなエラーが出ます: error[E0004]: non-exhaustive patterns: `None` not covered\n(エラー: 包括的でないパターン: `None`がカバーされてません) --> |\n6 | match x { | ^ pattern `None` not covered 全可能性を網羅していないことをコンパイラは検知しています。もっと言えば、どのパターンを忘れているかさえ知っているのです。 Rustにおけるマッチは、 包括的 です: 全てのあらゆる可能性を網羅し尽くさなければ、コードは有効にならないのです。 特に Option の場合には、私達が明示的に None の場合を処理するのを忘れないようにしてくれます。 nullになるかもしれないのに値があると思い込まないよう、すなわち前に議論した10億ドルの失敗を犯さないよう、 コンパイラが保護してくれるわけです。","breadcrumbs":"Enumとパターンマッチング » マッチは包括的","id":"ch06-02-match.html#aマッチは包括的","title":"マッチは包括的"},"ch06-02-match.html#a値に束縛されるパターン":{"body":"マッチのアームの別の有益な機能は、パターンにマッチした値の一部に束縛できる点です。こうして、 enumの列挙子から値を取り出すことができます。 例として、enumの列挙子の一つを中にデータを保持するように変えましょう。1999年から2008年まで、 アメリカは、片側に50の州それぞれで異なるデザインをしたクォーターコインを鋳造していました。 他のコインは州のデザインがなされることはなかったので、クォーターだけがこのおまけの値を保持します。 Quarter 列挙子を変更して、 UsState 値が中に保持されるようにすることで enum にこの情報を追加でき、 それをしたのがリスト6-4のコードになります。 #[derive(Debug)] // すぐに州を点検できるように\nenum UsState { Alabama, Alaska, // ... などなど\n} enum Coin { Penny, Nickel, Dime, Quarter(UsState),\n} リスト6-4: Quarter 列挙子が UsState の値も保持する Coin enum 友人の一人が50州全部のクォーターコインを収集しようとしているところを想像しましょう。コインの種類で小銭を並べ替えつつ、 友人が持っていない種類だったら、コレクションに追加できるように、各クォーターに関連した州の名前を出力します。 このコードのmatch式では、 Coin::Quarter 列挙子の値にマッチする state という名の変数をパターンに追加します。 Coin::Quarter がマッチすると、 state 変数はそのクォーターのstateの値に束縛されます。それから、 state をそのアームのコードで使用できます。以下のようにですね: # #[derive(Debug)]\n# enum UsState {\n# Alabama,\n# Alaska,\n# }\n#\n# enum Coin {\n# Penny,\n# Nickel,\n# Dime,\n# Quarter(UsState),\n# }\n#\nfn value_in_cents(coin: Coin) -> u32 { match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter(state) => { println!(\"State quarter from {:?}!\", state); 25 }, }\n} value_in_cents(Coin::Quarter(UsState::Alaska)) と呼び出すつもりだったなら、 coin は Coin::Quarter(UsState::Alaska) になります。その値をmatchの各アームと比較すると、 Coin::Quarter(state) に到達するまで、どれにもマッチしません。その時に、 state に束縛されるのは、 UsState::Alaska という値です。そして、 println! 式でその束縛を使用することができ、 そのため、 Coin enumの列挙子から Quarter に対する中身のstateの値を取得できたわけです。","breadcrumbs":"Enumとパターンマッチング » 値に束縛されるパターン","id":"ch06-02-match.html#a値に束縛されるパターン","title":"値に束縛されるパターン"},"ch06-02-match.html#match-フロー制御演算子":{"body":"Rustには、一連のパターンに対して値を比較し、マッチしたパターンに応じてコードを実行させてくれる match と呼ばれる、 非常に強力なフロー制御演算子があります。パターンは、リテラル値、変数名、ワイルドカードやその他多数のもので構成することができます; 第18章で、全ての種類のパターンと、その目的については解説します。 match のパワーは、 パターンの表現力とコンパイラが全てのありうるパターンを処理しているかを確認してくれるという事実に由来します。 match 式をコイン並べ替え装置のようなものと考えてください: コインは、様々なサイズの穴が空いた通路を流れ落ち、 各コインは、サイズのあった最初の穴に落ちます。同様に、値は match の各パターンを通り抜け、値が「適合する」最初のパターンで、 値は紐付けられたコードブロックに落ち、実行中に使用されるわけです。 コインについて話したので、それを match を使用する例にとってみましょう!数え上げ装置と同じ要領で未知のアメリカコインを一枚取り、 どの種類のコインなのか決定し、その価値をセントで返す関数をリスト6-3で示したように記述することができます。 enum Coin { Penny, Nickel, Dime, Quarter,\n} fn value_in_cents(coin: Coin) -> u32 { match coin { Coin::Penny => 1, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, }\n} リスト6-3: enumとそのenumの列挙子をパターンにした match 式 value_in_cents 関数内の match を噛み砕きましょう。まず、 match キーワードに続けて式を並べています。 この式は今回の場合、値 coin です。 if で使用した式と非常に酷似しているみたいですね。しかし、大きな違いがあります: if では、式は論理値を返す必要がありますが、ここでは、どんな型でも構いません。この例における coin の型は、 1行目で定義した Coin enumです。 次は、 match アームです。一本のアームには2つの部品があります: パターンと何らかのコードです。 今回の最初のアームは Coin::Penny という値のパターンであり、パターンと動作するコードを区別する => 演算子が続きます。 この場合のコードは、ただの値 1 です。各アームは次のアームとカンマで区切られています。 この match 式が実行されると、結果の値を各アームのパターンと順番に比較します。パターンに値がマッチしたら、 そのコードに紐付けられたコードが実行されます。パターンが値にマッチしなければ、コイン並べ替え装置と全く同じように、 次のアームが継続して実行されます。必要なだけパターンは存在できます: リスト6-3では、 match には4本のアームがあります。 各アームに紐付けられるコードは式であり、マッチしたアームの式の結果が match 式全体の戻り値になります。 典型的に、アームのコードが短い場合、波かっこは使用されません。リスト6-3では、各アームが値を返すだけなので、 これに倣っています。マッチのアームで複数行のコードを走らせたいのなら、波かっこを使用することができます。 例えば、以下のコードは、メソッドが Coin::Penny とともに呼び出されるたびに「Lucky penny!」と表示しつつ、 ブロックの最後の値、 1 を返すでしょう。 # enum Coin {\n# Penny,\n# Nickel,\n# Dime,\n# Quarter,\n# }\n#\nfn value_in_cents(coin: Coin) -> u32 { match coin { Coin::Penny => { println!(\"Lucky penny!\"); 1 }, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter => 25, }\n}","breadcrumbs":"Enumとパターンマッチング » match フロー制御演算子","id":"ch06-02-match.html#match-フロー制御演算子","title":"match フロー制御演算子"},"ch06-02-match.html#optiont-とのマッチ":{"body":"前節では、 Option を使用する際に、 Some ケースから中身の T の値を取得したくなりました。要するに、 Coin enumに対して行ったように、 match を使って Option を扱うこともできるというわけです! コインを比較する代わりに、 Option の列挙子を比較するのですが、 match 式の動作の仕方は同じままです。 Option を取る関数を書きたくなったとし、中に値があったら、その値に1を足すことにしましょう。 中に値がなければ、関数は None 値を返し、何も処理を試みるべきではありません。 match のおかげで、この関数は大変書きやすく、リスト6-5のような見た目になります。 fn plus_one(x: Option) -> Option { match x { None => None, Some(i) => Some(i + 1), }\n} let five = Some(5);\nlet six = plus_one(five);\nlet none = plus_one(None); リスト6-5: Option に match 式を使う関数 plus_one の最初の実行についてもっと詳しく検証しましょう。 plus_one(five) と呼び出した時、 plus_one の本体の変数 x は Some(5) になります。そして、これをマッチの各アームと比較します。 None => None, Some(5) という値は、 None というパターンにはマッチしませんので、次のアームに処理が移ります。 Some(i) => Some(i + 1), Some(5) は Some(i) にマッチしますか?なんと、します!列挙子が同じです。 i は Some に含まれる値に束縛されるので、 i は値 5 になります。それから、このマッチのアームのコードが実行されるので、 i の値に1を足し、 合計の 6 を中身にした新しい Some 値を生成します。 さて、 x が None になるリスト6-5の2回目の plus_one の呼び出しを考えましょう。 match に入り、 最初のアームと比較します。 None => None, マッチします!足し算する値がないので、プログラムは停止し、 => の右辺にある None 値が返ります。 最初のアームがマッチしたため、他のアームは比較されません。 match とenumの組み合わせは、多くの場面で有効です。Rustコードにおいて、このパターンはよく見かけるでしょう: enumに対し match し、内部のデータに変数を束縛させ、それに基づいたコードを実行します。最初はちょっと巧妙ですが、 一旦慣れてしまえば、全ての言語にあってほしいと願うことになるでしょう。一貫してユーザのお気に入りなのです。","breadcrumbs":"Enumとパターンマッチング » Option とのマッチ","id":"ch06-02-match.html#optiont-とのマッチ","title":"Option とのマッチ"},"ch06-03-if-let.html#aまとめ":{"body":"これで、enumを使用してワンセットの列挙された値のどれかになりうる独自の型を生成する方法を講義しました。 標準ライブラリの Option が型システムを使用して、エラーを回避する際に役立つ方法についても示しました。 enumの値がデータを内部に含む場合、処理すべきケースの数に応じて、 match か if let を使用して値を取り出し、 使用できます。 もうRustプログラムで構造体とenumを使用して、自分の領域の概念を表現できます。API内で使用するために独自の型を生成することで、 型安全性を保証することができます: コンパイラが、各関数の予期する型の値のみを関数が得ることを確かめてくれるのです。 使用するのに率直な整理整頓されたAPIをユーザに提供し、ユーザが必要とするものだけを公開するために、 今度は、Rustのモジュールに目を向けてみましょう。","breadcrumbs":"Enumとパターンマッチング » まとめ","id":"ch06-03-if-let.html#aまとめ","title":"まとめ"},"ch06-03-if-let.html#if-let-で簡潔なフロー制御":{"body":"if let 記法で if と let をより冗長性の少ない方法で組み合わせ、残りを無視しつつ、一つのパターンにマッチする値を扱うことができます。 Option にマッチするけれど、値が3の時にだけコードを実行したい、リスト6-6のプログラムを考えてください。 let some_u8_value = Some(0u8);\nmatch some_u8_value { Some(3) => println!(\"three\"), _ => (),\n} リスト6-6: 値が Some(3) の時だけコードを実行する match Some(3) にマッチした時だけ何かをし、他の Some 値や None 値の時には何もしたくありません。 match 式を満たすためには、列挙子を一つだけ処理した後に _ => () を追加しなければなりません。 これでは、追加すべき定型コードが多すぎます。 その代わり、 if let を使用してもっと短く書くことができます。以下のコードは、 リスト6-6の match と同じように振る舞います: # let some_u8_value = Some(0u8);\nif let Some(3) = some_u8_value { println!(\"three\");\n} if let という記法は等号記号で区切られたパターンと式を取り、式が match に与えられ、パターンが最初のアームになった match と、 同じ動作をします。 if let を使うと、タイプ数が減り、インデントも少なくなり、定型コードも減ります。しかしながら、 match では強制された包括性チェックを失ってしまいます。 match か if let かの選択は、 特定の場面でどんなことをしたいかと簡潔性を得ることが包括性チェックを失うのに適切な代償となるかによります。 言い換えると、 if let は値が一つのパターンにマッチした時にコードを走らせ、 他は無視する match への糖衣構文と考えることができます。 if let では、 else を含むこともできます。 else に入るコードブロックは、 if let と else に等価な match 式の _ の場合に入るコードブロックと同じになります。 リスト6-4の Coin enum定義を思い出してください。ここでは、 Quarter 列挙子は、 UsState の値も保持していましたね。クォーターコインの状態を告げつつ、 見かけたクォーター以外のコインの枚数を数えたいなら、以下のように match 式で実現することができるでしょう: # #[derive(Debug)]\n# enum UsState {\n# Alabama,\n# Alaska,\n# }\n#\n# enum Coin {\n# Penny,\n# Nickel,\n# Dime,\n# Quarter(UsState),\n# }\n# let coin = Coin::Penny;\nlet mut count = 0;\nmatch coin { // {:?}州のクォーターコイン Coin::Quarter(state) => println!(\"State quarter from {:?}!\", state), _ => count += 1,\n} または、以下のように if let と else を使うこともできるでしょう: # #[derive(Debug)]\n# enum UsState {\n# Alabama,\n# Alaska,\n# }\n#\n# enum Coin {\n# Penny,\n# Nickel,\n# Dime,\n# Quarter(UsState),\n# }\n# let coin = Coin::Penny;\nlet mut count = 0;\nif let Coin::Quarter(state) = coin { println!(\"State quarter from {:?}!\", state);\n} else { count += 1;\n} match を使って表現するには冗長的すぎるロジックがプログラムにあるようなシチュエーションに遭遇したら、 if let もRust道具箱にあることを思い出してください。","breadcrumbs":"Enumとパターンマッチング » if let で簡潔なフロー制御","id":"ch06-03-if-let.html#if-let-で簡潔なフロー制御","title":"if let で簡潔なフロー制御"},"ch07-00-modules.html#aモジュールを使用してコードを体系化し再利用する":{"body":"Rustでのプログラミングをし始めた頃は、コードは全て main 関数内に収まったかもしれません。コードが肥大化するにつれ、 最終的に機能を別の関数に移して再利用性とまとまりを高めるでしょう。コードを細切りにすることで、 個々のコード片をそれだけで理解しやすくします。しかし、あまりにも多くの関数があったらどうなるでしょうか? Rustには、コードの再利用を体系化された形で行うことのできるモジュールシステムが組み込まれています。 コードを関数に抽出するのと同様に、関数(や他のコード、構造体やenumなど)を異なるモジュールに抽出することができます。 モジュール とは、関数や型定義を含む名前空間のことで、それらの定義がモジュール外からも見えるようにするか(public)否か(private)は、 選択することができます。以下が、モジュールの動作法の概要です: mod キーワードで新規モジュールを宣言します。モジュール内のコードは、この宣言の直後の波かっこ内か、 別のファイルに存在します。 標準では、関数、型、定数、モジュールは非公開です。 pub キーワードで要素は公開され、 名前空間の外からも見えるようになります。 use キーワードでモジュールやモジュール内の定義をスコープに入れることができるので、 参照するのが楽になります。 この各部品を見て、それらが全体にどうはまり込むかを理解します。","breadcrumbs":"モジュールを使用してコードを体系化し、再利用する","id":"ch07-00-modules.html#aモジュールを使用してコードを体系化し再利用する","title":"モジュールを使用してコードを体系化し、再利用する"},"ch07-01-mod-and-the-filesystem.html#aモジュールを別ファイルに移す":{"body":"モジュールは階層構造をなす……コンピュータにおいて、もっと見慣れた構造に似ていませんか: そう、ファイルシステムです! Rustのモジュールシステムを複数のファイルで使用して、プロジェクトを分割するので、 全部が src/lib.rs や src/main.rs に存在することにはならなくなります。これの例として、 リスト7-3のようなコードから始めましょう。 ファイル名: src/lib.rs mod client { fn connect() { }\n} mod network { fn connect() { } mod server { fn connect() { } }\n} リスト7-3: 全て src/lib.rs に定義された三つのモジュール、 client 、 network 、 network::server src/lib.rs ファイルのモジュール階層は、こうなっています: communicator ├── client └── network └── server これらのモジュールが多数の関数を含み、その関数が長ったらしくなってきたら、このファイルをスクロールして、 弄りたいコードを探すのが困難になるでしょう。関数が一つ以上のmodブロックにネストされているので、 関数の中身となるコードも長ったらしくなってしまうのです。これだけで、 client 、 network 、 server モジュールを src/lib.rs から分け、 単独のファイルに配置するには十分でしょう。 最初に、 client モジュールのコードを client モジュールの宣言だけに置き換えましょう。 すると、 src/lib.rs はリスト7-4のコードのようになります。 ファイル名: src/lib.rs mod client; mod network { fn connect() { } mod server { fn connect() { } }\n} リスト7-4: client モジュールの中身を抽出するが、宣言は src/lib.rs に残したまま 一応、 client モジュールをここで 宣言 していますが、ブロックをセミコロンで置換したことで、 client モジュールのスコープのコードは別の場所を探すようにコンパイラに指示しているわけです。 言い換えると、 mod client; の行は、以下のような意味になります: mod client { // contents of client.rs\n} さて、このモジュール名の外部ファイルを作成する必要が出てきました。 src/ ディレクトリ内に client.rs ファイルを作成し、 開いてください。それから以下のように入力してください。前段階で削除した client モジュールの connect 関数です: ファイル名: src/client.rs fn connect() {\n} このファイルには、 mod 宣言が必要ないことに着目してください。なぜなら、 src/lib.rs に mod を使って、 もう client モジュールを宣言しているからです。このファイルは、 client モジュールの 中身 を提供するだけなのです。 ここにも mod client を記述したら、 client に client という名前のサブモジュールを与えることになってしまいます! コンパイラは、標準で src/lib.rs だけを検索します。プロジェクトにもっとファイルを追加したかったら、 src/lib.rs で他のファイルも検索するよう、コンパイラに指示する必要があるのです; このため、 mod client を src/lib.rs に定義し、 src/client.rs には定義できなかったのです。 これでプロジェクトは問題なくコンパイルできるはずです。まあ、警告がいくつか出るんですが。 cargo run ではなく、 cargo build を使うことを忘れないでください。バイナリクレートではなく、 ライブラリクレートだからですね: $ cargo build Compiling communicator v0.1.0 (file:///projects/communicator) warning: function is never used: `connect`\n(警告: 関数は使用されていません: `connect`) --> src/client.rs:1:1 |\n1 | / fn connect() {\n2 | | } | |_^ | = note: #[warn(dead_code)] on by default warning: function is never used: `connect` --> src/lib.rs:4:5 |\n4 | / fn connect() {\n5 | | } | |_____^ warning: function is never used: `connect` --> src/lib.rs:8:9 |\n8 | / fn connect() {\n9 | | } | |_________^ これらの警告は、全く使用されていない関数があると忠告してくれています。今は、警告を危惧する必要はありません; この章の後ほど、「 pub で公開するか制御する」節で扱います。嬉しいことにただの警告です; プロジェクトはビルドに成功しました! 次に、同様のパターンで network モジュールも単独のファイルに抽出しましょう。 src/lib.rs で、 network モジュールの本体を削除し、宣言にセミコロンを付加してください。こんな感じです: ファイル名: src/lib.rs mod client; mod network; それから新しい src/network.rs ファイルを作成して、以下のように入力してください: ファイル名: src/network.rs fn connect() {\n} mod server { fn connect() { }\n} このモジュールファイル内にもまだ mod 宣言があることに注意してください; server はまだ network のサブモジュールにしたいからです。 再度 cargo build してください。成功!抽出すべきモジュールがもう1個あります: server です。 これはサブモジュール(つまり、モジュール内のモジュール)なので、 モジュール名に倣ったファイルにモジュールを抽出するという今の手法は、通用しません。いずれにしても、 エラーが確認できるように、試してみましょう。まず、 src/network.rs ファイルを server モジュールの中身を含む代わりに、 mod server; となるように変更してください。 ファイル名: src/network.rs fn connect() {\n} mod server; そして、 src/server.rs ファイルを作成し、抽出した server モジュールの中身を入力してください: ファイル名: src/server.rs fn connect() {\n} cargo build を実行しようとすると、リスト7-5に示したようなエラーが出ます: $ cargo build Compiling communicator v0.1.0 (file:///projects/communicator)\nerror: cannot declare a new module at this location\n(エラー: この箇所では新規モジュールを宣言できません) --> src/network.rs:4:5 |\n4 | mod server; | ^^^^^^ |\nnote: maybe move this module `src/network.rs` to its own directory via `src/network/mod.rs`\n(注釈: もしかして、`src/network.rs`というこのモジュールを`src/network/mod.rs`経由で独自のディレクトリに移すの) --> src/network.rs:4:5 |\n4 | mod server; | ^^^^^^\nnote: ... or maybe `use` the module `server` instead of possibly redeclaring it\n(注釈: それとも、再度宣言する可能性はなく、`server`というモジュールを`use`したの) --> src/network.rs:4:5 |\n4 | mod server; | ^^^^^^ リスト7-5: server サブモジュールを src/server.rs に抽出しようとしたときのエラー エラーは、 この箇所では新規モジュールを宣言できません と忠告し、 src/network.rs の mod server; 行を指し示しています。 故に、 src/network.rs は、 src/lib.rs と何かしら違うのです: 理由を知るために読み進めましょう。 リスト7-5の真ん中の注釈は、非常に有用です。というのも、まだ話題にしていないことを指摘しているからです。 note: maybe move this module `network` to its own directory via\n`network/mod.rs` 以前行ったファイル命名パターンに従い続けるのではなく、注釈が提言していることをすることができます: 親モジュール名である network という名前の新規 ディレクトリ を作成する。 src/network.rs ファイルを network ディレクトリに移し、 src/network/mod.rs と名前を変える。 サブモジュールファイルの src/server.rs を network ディレクトリに移す。 以下が、これを実行するコマンドです: $ mkdir src/network\n$ mv src/network.rs src/network/mod.rs\n$ mv src/server.rs src/network cargo build を走らせたら、コンパイルは通ります(まだ警告はありますけどね)。 それでも、モジュールの配置は、リスト7-3で src/lib.rs に全てのコードを収めていたときと全く同じになります: communicator ├── client └── network └── server 対応するファイルの配置は、以下のようになっています: └── src ├── client.rs ├── lib.rs └── network ├── mod.rs └── server.rs では、 network::server モジュールを抽出したかったときに、 なぜ、 src/network.rs ファイルを src/network/mod.rs ファイルに変更し、 network::server のコードを network ディレクトリ内の src/network/server.rs に置かなければならなかったのでしょうか? なぜ、単に network::server モジュールを src/server.rs に抽出できなかったのでしょうか? 理由は、 server.rs ファイルが src ディレクトリにあると、コンパイラが、 server は network のサブモジュールと考えられることを検知できないからです。 ここでのコンパイラの動作をはっきりさせるために、以下のようなモジュール階層をもつ別の例を考えましょう。 こちらでは、定義は全て src/lib.rs に存在します。 communicator ├── client └── network └── client この例でも、モジュールは3つあります: client 、 network 、 network::client です。 以前と同じ手順を経てモジュールをファイルに抽出すると、 client モジュール用に src/client.rs を作成することになるでしょう。 network モジュールに関しては、 src/network.rs を作成します。しかし、 network::client モジュールを src/client.rs ファイルに抽出することはできません。 もうトップ階層の client モジュールとして存在するからです! client と network::client 双方 のコードを src/client.rs ファイルに書くことができたら、 コンパイラは、コードが client 用なのか、 network::client 用なのか知る術を失ってしまいます。 従って、 network モジュールの network::client サブモジュールをファイルに抽出するには、 src/network.rs ファイルではなく、 network モジュールのディレクトリを作成する必要があったわけです。 そうすれば、 network モジュールのコードは、 src/network/mod.rs ファイルに移ることになり、 network::client というサブモジュールは専用の src/network/client.rs ファイルを持てるわけです。 これで、頂点にある src/client.rs は間違いなく、 client モジュールに属するコードになるわけです。","breadcrumbs":"モジュール » モジュールを別ファイルに移す","id":"ch07-01-mod-and-the-filesystem.html#aモジュールを別ファイルに移す","title":"モジュールを別ファイルに移す"},"ch07-01-mod-and-the-filesystem.html#aモジュールファイルシステムの規則":{"body":"ファイルに関するモジュール規則をまとめましょう: foo という名前のモジュールにサブモジュールがなければ、 foo の定義は、 foo.rs というファイルに書くべきです。 foo というモジュールに本当にサブモジュールがあったら、 foo の定義は、 foo/mod.rs というファイルに書くべきです。 これらのルールは再帰的に適用されるので、 foo というモジュールに bar というサブモジュールがあり、 bar にはサブモジュールがなければ、 src ディレクトリには以下のようなファイルが存在するはずです: ├── foo\n│ ├── bar.rs (`foo::bar`内の定義を含む)\n│ └── mod.rs (`mod bar`を含む、`foo`内の定義を含む) モジュールは、親モジュールのファイル内で mod キーワードを使って宣言されるべきなのです。 次は、 pub キーワードについて話し、警告を取り除きます!","breadcrumbs":"モジュール » モジュールファイルシステムの規則","id":"ch07-01-mod-and-the-filesystem.html#aモジュールファイルシステムの規則","title":"モジュールファイルシステムの規則"},"ch07-01-mod-and-the-filesystem.html#aモジュール定義":{"body":"communicator ネットワークライブラリについて、まずは connect という関数定義を含む network という名前のモジュールを定義します。 Rustにおいて、モジュール定義は全て、 mod キーワードから開始します。このコードを src/lib.rs ファイルの頭、 テストコードの上に追記してください。 ファイル名: src/lib.rs mod network { fn connect() { }\n} mod キーワードに続いて、モジュール名の network 、さらに一連のコードを波かっこ内に記述します。 このブロック内に存在するものは全て、 network という名前空間に属します。今回の場合、 connect という単独の関数があります。この関数を network モジュール外のスクリプトから呼び出したい場合、 モジュールを指定し、以下のように名前空間記法の :: を使用する必要があるでしょう: network::connect() 。 同じ src/lib.rs ファイル内に複数のモジュールを並べることもできます。例として、 connect という関数を含む client モジュールも用意するには、リスト7-1に示したように追記すればいいわけです。 ファイル名: src/lib.rs mod network { fn connect() { }\n} mod client { fn connect() { }\n} リスト7-1: src/lib.rs に並べて定義された network モジュールと client モジュール これで、 network::connect 関数と client::connect 関数が用意できました。これらは全く異なる機能を有する可能性があり、 異なるモジュールに存在するので、関数名がお互いに衝突することはありません。 今回の場合、ライブラリを構成しているので、ライブラリビルド時にエントリーポイントとなるファイルは、 src/lib.rs になります。しかし、モジュールを作成するという点に関しては、 src/lib.rs には何も特別なことはありません。 ライブラリクレートに対して src/lib.rs にモジュールを生成するのと同様に、 バイナリクレートに対して src/main.rs にモジュールを生成することもできます。実は、モジュール内にモジュールを書くこともでき、 モジュールが肥大化するにつれて、関連のある機能を一緒くたにし、機能を切り離すのに有用なのです。 コードを体系化すると選択する方法は、コードの部分部分の関連性に対する考え方によります。 例ですが、 client コードとその connect 関数は、リスト7-2のように、代わりに network 名前空間内に存在したら、 ライブラリの使用者にとって意味のあるものになるかもしれません。 ファイル名: src/lib.rs mod network { fn connect() { } mod client { fn connect() { } }\n} リスト7-2: client モジュールを network モジュール内に移動させる src/lib.rs ファイル内で、すでにある mod network と mod client の定義をリスト7-2のものと置き換えると、 client モジュールは network の内部モジュールになるわけです。関数、 network::connect と network::client::connect はどちらも connect という名前ですが、 異なる名前空間にあるので、互いに干渉することはありません。 このように、モジュールは階層構造を形成します。 src/lib.rs の中身が頂点に立ち、サブモジュールが子供になるわけです。 リスト7-1の例を階層構造という観点で見たときの構造は、以下のような感じになります: communicator ├── network └── client さらに、リスト7-2の例に対応する階層構造は、以下の通りです: communicator └── network └── client この階層構造は、リスト7-2において、 client モジュールは network モジュールの兄弟というよりも、子供になっていることを示しています。 より複雑なプロジェクトなら、たくさんのモジュールが存在し、把握するのに論理的に体系化しておく必要があるでしょう。 プロジェクト内で「論理的」とは、あなた次第であり、ライブラリ作成者と使用者がプロジェクトの領域についてどう考えるか次第でもあるわけです。 こちらで示したテクニックを使用して、並列したモジュールや、ネストしたモジュールなど、どんな構造のモジュールでも、 作成してください。","breadcrumbs":"モジュール » モジュール定義","id":"ch07-01-mod-and-the-filesystem.html#aモジュール定義","title":"モジュール定義"},"ch07-01-mod-and-the-filesystem.html#mod-とファイルシステム":{"body":"モジュールの例をCargoで新規プロジェクトを生成することから始めるが、バイナリクレートの代わりに、 ライブラリクレートを作成します: 他人が依存として自分のプロジェクトに引き込めるプロジェクトです。 例を挙げると、第2章で議論した rand クレートは、数当てゲームプロジェクトで依存に使用したライブラリクレートです。 何らかの一般的なネットワーク機能を提供するライブラリの骨格を作成します; モジュールと関数の体系化に集中し、 関数の本体にどんなコードが入るかについては気にかけません。このライブラリを communicator と呼びましょう。 ライブラリを生成するために、 --bin の代わりに --lib オプションを渡してください: $ cargo new communicator --lib\n$ cd communicator Cargoが src/main.rs の代わりに src/lib.rs を生成したことに注目してください。 src/lib.rs には、 以下のような記述があります: ファイル名: src/lib.rs #[cfg(test)]\nmod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); }\n} Cargoは、 --bin オプションを使った時に得られる\"Hello, world!\"バイナリではなく、空のテストを生成して、 ライブラリの事始めをしてくれました。 #[] と mod tests という記法については、この章の後ほど、 「 super を使用して親モジュールにアクセスする」節で見ますが、今のところは、 このコードを src/lib.rs の最後に残しておきましょう。 src/main.rs ファイルがないので、 cargo run コマンドでCargoが実行できるものは何もないわけです。 従って、 cargo build コマンドを使用してライブラリクレートのコードをコンパイルします。 コードの意図によって、いろんなシチュエーションで最適になるライブラリコードを体系化する別のオプションをお目にかけます。","breadcrumbs":"モジュール » mod とファイルシステム","id":"ch07-01-mod-and-the-filesystem.html#mod-とファイルシステム","title":"mod とファイルシステム"},"ch07-02-controlling-visibility-with-pub.html#aプライバシー例":{"body":"もうちょっと鍛錬を得るために、もういくつかプライバシー例を見てみましょう。新しいライブラリプロジェクトを作成し、 リスト7-6のコードを新規プロジェクトの src/lib.rs に入力してください。 ファイル名: src/lib.rs mod outermost { pub fn middle_function() {} fn middle_secret_function() {} mod inside { pub fn inner_function() {} fn secret_function() {} }\n} fn try_me() { outermost::middle_function(); outermost::middle_secret_function(); outermost::inside::inner_function(); outermost::inside::secret_function();\n} リスト7-6: 公開と非公開関数の例。不正なものもあります このコードをコンパイルする前に、 try_me 関数のどの行がエラーになるか当ててみてください。 それからコンパイルを試して、合ってたかどうか確かめ、エラーの議論を求めて読み進めてください! エラーを確かめる try_me 関数は、プロジェクトのルートモジュールに存在しています。 outermost という名前のモジュールは非公開ですが、 プライバシー規則の2番目にある通り、 try_me のように、 outermost は現在(ルート)のモジュールなので、 try_me 関数は、 outermost モジュールにアクセスすることを許可されるのです。 middle_function は公開なので、 outermost::middle_function という呼び出しも動作し、 try_me は middle_function にその親モジュールの outermost を通してアクセスしています。 このモジュールは、アクセス可能と既に決定しました。 outermost::middle_secret_function の呼び出しは、コンパイルエラーになるでしょう。 middle_secret_function は非公開なので、2番目の規則が適用されます。ルートモジュールは、 middle_secret_function の現在のモジュール( outermost がそうです)でも、 middle_secret_function の現在のモジュールの子供でもないのです。 inside という名前のモジュールは非公開で子モジュールを持たないので、現在のモジュールである outermost からのみアクセスできます。 つまり、 try_me 関数は、 outermost::inside::inner_function も outermost::inside::secret_function も呼び出すことを許されないのです。 エラーを修正する エラーを修正しようとする過程でできるコード変更案は、以下の通りです。各々試してみる前に、 エラーを解消できるか当ててみてください。それからコンパイルして正しかったか間違っていたか確かめ、 プライバシー規則を使用して理由を理解してください。もっと実験を企てて試してみるのもご自由に! inside モジュールが公開だったらどうだろうか? outermost が公開で、 inside が非公開ならどうだろうか? inner_function の本体で ::outermost::middle_secret_function() を呼び出したらどうだろうか? (頭の二つのコロンは、ルートモジュールから初めてモジュールを参照したいということを意味します) 今度は、 use キーワードで要素をスコープに導入する話をしましょう。","breadcrumbs":"モジュール » プライバシー例","id":"ch07-02-controlling-visibility-with-pub.html#aプライバシー例","title":"プライバシー例"},"ch07-02-controlling-visibility-with-pub.html#aプライバシー規則":{"body":"まとめると、要素の公開性は以下のようなルールになります: 要素が公開なら、どの親モジュールを通してもアクセス可能です。 要素が非公開なら、直接の親モジュールとその親の子モジュールのみアクセスできます。","breadcrumbs":"モジュール » プライバシー規則","id":"ch07-02-controlling-visibility-with-pub.html#aプライバシー規則","title":"プライバシー規則"},"ch07-02-controlling-visibility-with-pub.html#a関数を公開にする":{"body":"コンパイラに何かを公開すると指示するには、定義の先頭に pub キーワードを追記します。 今は、 client::connect が未使用であるとする警告とバイナリークレートの モジュール`client`が非公開である エラーの解消に努めます。 src/lib.rs を弄って、 client モジュールを公開にしてください。そう、こんな感じに: ファイル名: src/lib.rs pub mod client; mod network; pub キーワードは、 mod の直前に配置されています。再度ビルドしてみましょう: error[E0603]: function `connect` is private --> src/main.rs:4:5 |\n4 | communicator::client::connect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ やった!違うエラーになりました!そうです、別のエラーメッセージは、祝杯を上げる理由になるのです。 新エラーは、 関数`connect`は非公開です と示しているので、 src/client.rs を編集して、 client::connect も公開にしましょう: ファイル名: src/client.rs pub fn connect() {\n} さて、再び、 cargo build を走らせてください: warning: function is never used: `connect` --> src/network/mod.rs:1:1 |\n1 | / fn connect() {\n2 | | } | |_^ | = note: #[warn(dead_code)] on by default warning: function is never used: `connect` --> src/network/server.rs:1:1 |\n1 | / fn connect() {\n2 | | } | |_^ コードのコンパイルが通り、 client:connect が使用されていないという警告はなくなりました! コード未使用警告が必ずしも、コード内の要素を公開にしなければならないことを示唆しているわけではありません: これらの関数を公開APIの一部にしたく なかった ら、未使用コード警告がもう必要なく、安全に削除できるコードに注意を向けてくれている可能性もあります。 また未使用コード警告は、ライブラリ内でこの関数を呼び出している箇所全てを誤って削除した場合に、 バグに目を向けさせてくれている可能性もあります。 しかし今回は、 本当に 他の2つの関数もクレートの公開APIにしたいので、これも pub とマークして残りの警告を除去しましょう。 src/network/mod.rs を変更して以下のようにしてください: ファイル名: src/network/mod.rs pub fn connect() {\n} mod server; そして、コードをコンパイルします: warning: function is never used: `connect` --> src/network/mod.rs:1:1 |\n1 | / pub fn connect() {\n2 | | } | |_^ | = note: #[warn(dead_code)] on by default warning: function is never used: `connect` --> src/network/server.rs:1:1 |\n1 | / fn connect() {\n2 | | } | |_^ んんー、 nework::connect は pub に設定されたにもかかわらず、まだ未使用関数警告が出ます。 その理由は、関数はモジュール内で公開になったものの、関数が存在する network モジュールは公開ではないからです。 今回は、ライブラリの内部から外に向けて作業をした一方、 client::connect では、外から内へ作業をしていました。 src/lib.rs を変えて network も公開にする必要があります。以下のように: ファイル名: src/lib.rs pub mod client; pub mod network; これでコンパイルすれば、あの警告はなくなります: warning: function is never used: `connect` --> src/network/server.rs:1:1 |\n1 | / fn connect() {\n2 | | } | |_^ | = note: #[warn(dead_code)] on by default 残る警告は1つなので、自分で解消してみてください!","breadcrumbs":"モジュール » 関数を公開にする","id":"ch07-02-controlling-visibility-with-pub.html#a関数を公開にする","title":"関数を公開にする"},"ch07-02-controlling-visibility-with-pub.html#pub-で公開するか制御する":{"body":"リスト7-5に示したエラーメッセージを network と network::server のコードを、 src/network/mod.rs と src/network/server.rs ファイルにそれぞれ移動することで解決しました。 その時点で cargo build はプロジェクトをビルドできましたが、 client::connect と network::connect と network::server::connect 関数が、 使用されていないという警告メッセージが出ていました: warning: function is never used: `connect` --> src/client.rs:1:1 |\n1 | / fn connect() {\n2 | | } | |_^ | = note: #[warn(dead_code)] on by default warning: function is never used: `connect` --> src/network/mod.rs:1:1 |\n1 | / fn connect() {\n2 | | } | |_^ warning: function is never used: `connect` --> src/network/server.rs:1:1 |\n1 | / fn connect() {\n2 | | } | |_^ では、何故このような警告を受けているのでしょうか?結局のところ、必ずしも自分のプロジェクト内ではなく、 利用者 に利用されることを想定した関数を含むライブラリを構成しているので、 これらの connect 関数が使用されていかないということは、問題になるはずはありません。 これらの関数を生成することの要点は、自分ではなく、他のプロジェクトで使用することにあるのです。 このプログラムがこのような警告を引き起こす理由を理解するために、外部から communicator ライブラリを呼び出して、 他のプロジェクトからこれを使用してみましょう。そうするには、以下のようなコードを含む src/main.rs を作成して、 ライブラリクレートと同じディレクトリにバイナリクレートを作成します。 ファイル名: src/main.rs extern crate communicator; fn main() { communicator::client::connect();\n} extern crate コマンドを使用して、 communicator ライブラリクレートをスコープに導入しています。 パッケージには 2つ のクレートが含まれるようになりました。Cargoは、 src/main.rs をバイナリクレートのルートファイルとして扱い、 これはルートファイルが src/lib.rs になる既存のライブラリクレートとは区別されます。このパターンは、 実行形式プロジェクトで非常に一般的です: ほとんどの機能はライブラリクレートにあり、バイナリクレートはそれを使用するわけです。 結果として、他のプログラムもまたこのライブラリクレートを使用でき、良い責任の分離になるわけです。 communicator ライブラリの外部のクレートが検索するという観点から言えば、これまでに作ってきたモジュールは全て、 communicator というクレートと同じ名前を持つモジュール内にあります。クレートのトップ階層のモジュールを ルートモジュール と呼びます。 プロジェクトのサブモジュール内で外部クレートを使用しているとしても、 extern crate はルートモジュール(つまり、 src/main.rs 、 または src/lib.rs )に書くべきということにも、注目してください。それから、 サブモジュールで外部クレートの要素をトップ階層のモジュールかのように参照できるわけです。 現状、バイナリクレートは、 client モジュールからライブラリの connect 関数を呼び出しているだけです。 ところが、 cargo build を呼び出すと、警告の後にエラーが発生します: error[E0603]: module `client` is private\n(エラー: `client`モジュールは非公開です) --> src/main.rs:4:5 |\n4 | communicator::client::connect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ああ!このエラーは、 client モジュールが非公開であることを教えてくれ、それが警告の肝だったわけです。 Rustの文脈において、 公開 とか 非公開 という概念にぶち当たったのは、これが初めてでもあります。 全コードの初期状態は、非公開です: 誰も他の人はコードを使用できないわけです。プログラム内で非公開の関数を使用していなければ、 自分のプログラムだけがその関数を使用することを許可された唯一のコードなので、コンパイラは関数が未使用と警告してくるのです。 client::connect のような関数を公開にすると指定した後は、バイナリクレートからその関数への呼び出しが許可されるだけでなく、 関数が未使用であるという警告も消え去るわけです。関数を公開にすれば、コンパイラは、 関数が自分のプログラム外のコードからも使用されることがあると知ります。コンパイラは、 関数が「使用されている」という架空の外部使用の可能性を考慮してくれます。それ故に、関数が公開とマークされれば、 コンパイラはそれが自分のプログラムで使用されるべきという要求をなくし、その関数が未使用という警告も止めるのです。","breadcrumbs":"モジュール » pub で公開するか制御する","id":"ch07-02-controlling-visibility-with-pub.html#pub-で公開するか制御する","title":"pub で公開するか制御する"},"ch07-03-importing-names-with-use.html#aまとめ":{"body":"これでコードを体系化する新しいテクニックを知りましたね!これらのテクニックを使用して、 関連のある機能をまとめ上げ、ファイルが長くなりすぎるのを防ぎ、ライブラリの使用者に整理整頓された公開APIを提供してください。 次は、自分の素晴らしく綺麗なコードで使用できる標準ライブラリのコレクションデータ構造について見ていきましょう。","breadcrumbs":"モジュール » まとめ","id":"ch07-03-importing-names-with-use.html#aまとめ","title":"まとめ"},"ch07-03-importing-names-with-use.html#a異なるモジュールの名前を参照する":{"body":"モジュール名を呼び出しの一部に使用して、モジュール内に定義された関数の呼び出し方法を講義しました。 リスト7-7に示した nested_modules 関数の呼び出しのような感じですね。 ファイル名: src/main.rs pub mod a { pub mod series { pub mod of { pub fn nested_modules() {} } }\n} fn main() { a::series::of::nested_modules();\n} リスト7-7: 囲まれたモジュールをフルパス指定して関数を呼び出す 見てお分かりの通り、フルパス指定した名前を参照すると非常に長ったらしくなります。 幸い、Rustには、これらの呼び出しをもっと簡潔にするキーワードが用意されています。","breadcrumbs":"モジュール » 異なるモジュールの名前を参照する","id":"ch07-03-importing-names-with-use.html#a異なるモジュールの名前を参照する","title":"異なるモジュールの名前を参照する"},"ch07-03-importing-names-with-use.html#globで全ての名前をスコープに導入する":{"body":"ある名前空間の要素を全て一度にスコープに導入するには、 * 表記が使用でき、これはglob(塊)演算子と呼ばれます。 この例は、あるenumの列挙子を各々を列挙せずに全てスコープに導入しています: enum TrafficLight { Red, Yellow, Green,\n} use TrafficLight::*; fn main() { let red = Red; let yellow = Yellow; let green = Green;\n} * 演算子は TrafficLight 名前空間に存在する全て公開要素をスコープに導入します。 あまりglobは使用するべきではありません: 便利ではありますが、globは予想以上の要素を引き込んで、 名前衝突を引き起こす可能性があるのです。","breadcrumbs":"モジュール » Globで全ての名前をスコープに導入する","id":"ch07-03-importing-names-with-use.html#globで全ての名前をスコープに導入する","title":"Globで全ての名前をスコープに導入する"},"ch07-03-importing-names-with-use.html#super-を使用して親モジュールにアクセスする":{"body":"この章の頭で見かけたように、ライブラリクレートを作成する際、Cargoは tests モジュールを用意してくれました。 今からそれについて詳しく掘り下げていくことにしましょう。 communicator プロジェクトで src/lib.rs を開いてください: ファイル名: src/lib.rs pub mod client; pub mod network; #[cfg(test)]\nmod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); }\n} 第11章でテストについて詳しく説明しますが、これでこの例の一部が持つ意味がわかったのではないでしょうか: 他のモジュールに隣接する tests という名前のモジュールがあり、このモジュールは it_works という名前の関数を含んでいます。 特別な注釈があるものの、 tests モジュールもただのモジュールです!よって、モジュール階層は以下のような見た目になります: communicator ├── client ├── network | └── client └── tests テストは、ライブラリ内でコードの準備運動を行うためのものなので、この it_works 関数から client::connect 関数を呼び出してみましょう。 まあ、尤(もっと)も今のところ、機能の検査は何もしないんですけどね。これはまだ動きません: ファイル名: src/lib.rs #[cfg(test)]\nmod tests { #[test] fn it_works() { client::connect(); }\n} cargo test コマンドを呼び出してテストを実行してください: $ cargo test Compiling communicator v0.1.0 (file:///projects/communicator)\nerror[E0433]: failed to resolve. Use of undeclared type or module `client`\n(エラー: 解決に失敗しました。未定義の型、またはモジュール`client`を使用しています) --> src/lib.rs:9:9 |\n9 | client::connect(); | ^^^^^^ Use of undeclared type or module `client` コンパイルが失敗しましたが、なぜでしょうか? src/main.rs のように、関数の直前に communicator:: を配置する必要はありません。 なぜなら、間違いなくここでは、 communicator ライブラリクレート内にいるからです。 原因は、パスが常に現在のモジュールに対して相対的になり、ここでは tests になっているからです。 唯一の例外は、 use 文内であり、パスは標準でクレートのルートに相対的になります。 tests モジュールは、 client モジュールがスコープに存在する必要があるのです! では、どうやってモジュール階層を一つ上がり、 tests モジュールの client::connect 関数を呼び出すのでしょうか? tests モジュールにおいて、先頭にコロンを使用して、コンパイラにルートから始めて、フルパスを列挙したいと知らせることもできます。 こんな感じで: ::client::connect(); あるいは、 super を使用して現在のモジュールからモジュール階層を一つ上がることもできます。 以下のように: super::client::connect(); この例では、これら二つの選択はそれほど異なるようには見えませんが、モジュール階層がもっと深ければ、 常にルートから書き始めるのは、コードを長ったらしくする原因になります。そのような場合、 super を使用して現在のモジュールから兄弟のモジュールに辿り着くのは、いいショートカットになります。 さらに、コードのいろんなところでルートからパスを指定してから、サブ木構造を別の箇所に移してモジュール構造を変化させた場合、 複数箇所でパスを更新する必要に陥り、面倒なことになるでしょう。 各テストで super:: と入力しなければならないのも不快なことですが、それを解決してくれる道具をもう見かけています: use です! super:: の機能は、 use に与えるパスを変更するので、ルートモジュールではなく、 親モジュールに対して相対的になります。 このような理由から、ことに tests モジュールにおいて use super::somthing は通常、 最善策になるわけです。故に、今ではテストはこんな見た目になりました: ファイル名: src/lib.rs #[cfg(test)]\nmod tests { use super::client; #[test] fn it_works() { client::connect(); }\n} 再度 cargo test を実行すると、テストは通り、テスト結果出力の最初の部分は以下のようになるでしょう: $ cargo test Compiling communicator v0.1.0 (file:///projects/communicator) Running target/debug/communicator-92007ddb5330fa5a running 1 test\ntest tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out","breadcrumbs":"モジュール » super を使用して親モジュールにアクセスする","id":"ch07-03-importing-names-with-use.html#super-を使用して親モジュールにアクセスする","title":"super を使用して親モジュールにアクセスする"},"ch07-03-importing-names-with-use.html#use-キーワードで名前をスコープに導入する":{"body":"Rustの use キーワードは、呼び出したい関数のモジュールをスコープに導入することで、 長ったらしい関数呼び出しを短縮します。以下は、 a::series::of モジュールをバイナリクレートのルートスコープに持ってくる例です: Filename: src/main.rs pub mod a { pub mod series { pub mod of { pub fn nested_modules() {} } }\n} use a::series::of; fn main() { of::nested_modules();\n} use a::series::of; の行は、 of モジュールを参照したい箇所全部でフルパスの a::series::of を使用するのではなく、 of を利用できることを意味しています。 この use キーワードは、指定したものだけをスコープに入れます: モジュールの子供はスコープに導入しないのです。 そのため、 nested_modules 関数を呼び出したい際に、それでもまだ of::nested_modules を使わなければならないのです。 以下のように、代わりに use で関数を指定して、関数をスコープに入れることもできました: pub mod a { pub mod series { pub mod of { pub fn nested_modules() {} } }\n} use a::series::of::nested_modules; fn main() { nested_modules();\n} そうすれば、モジュールをすべて取り除き、関数を直接参照することができます。 enumもモジュールのようにある種の名前空間をなすので、enumの列挙子を use でスコープに導入することもできます。 どんな use 文に関しても、一つの名前空間から複数の要素をスコープに導入する場合、波かっことお尻にカンマを使用することで列挙できます。 こんな感じで: enum TrafficLight { Red, Yellow, Green,\n} use TrafficLight::{Red, Yellow}; fn main() { let red = Red; let yellow = Yellow; let green = TrafficLight::Green;\n} Green を use 文に含んでいないので、まだ Green バリアント用に TrafficLight 名前空間を指定しています。","breadcrumbs":"モジュール » use キーワードで名前をスコープに導入する","id":"ch07-03-importing-names-with-use.html#use-キーワードで名前をスコープに導入する","title":"use キーワードで名前をスコープに導入する"},"ch08-00-common-collections.html#a一般的なコレクション":{"body":"Rustの標準ライブラリは、 コレクション と呼ばれる多くの非常に有益なデータ構造を含んでいます。他の多くのデータ型は、 ある一つの値を表しますが、コレクションは複数の値を含むことができます。組み込みの配列とタプル型とは異なり、 これらのコレクションが指すデータはヒープに確保され、データ量はコンパイル時にわかる必要はなく、 プログラムの実行にあわせて、伸縮可能であることになります。各種のコレクションには異なる能力とコストが存在し、 自分の現在の状況に最適なものを選び取るスキルは、時間とともに育っていきます。この章では、 Rustのプログラムにおいて、非常に頻繁に使用される3つのコレクションについて議論しましょう。 ベクタ型 は、可変長の値を並べて保持できる。 文字列 は、文字のコレクションである。以前、 String 型について触れたが、 この章ではより掘り下げていく。 ハッシュマップ は、値を特定のキーと紐付けさせてくれる。より一般的なデータ構造である、 マップ の特定の実装である。 標準ライブラリで提供されている他の種のコレクションについて学ぶには、 ドキュメント を参照されたし。 ベクタ型、文字列、ハッシュマップの生成と更新方法や、各々が特別な点について議論していきましょう。","breadcrumbs":"一般的なコレクション","id":"ch08-00-common-collections.html#a一般的なコレクション","title":"一般的なコレクション"},"ch08-01-vectors.html#aベクタで一連の値を保持する":{"body":"最初に見るコレクションは、 Vec であり、 ベクタ としても知られています。ベクタは、 メモリ上に値を隣り合わせに並べる単独のデータ構造に2つ以上の値を保持させてくれます。 ベクタには、同じ型の値しか保持できません。要素のリストがある場合に有用です。 例えば、テキストファイルの各行とか、ショッピングカートのアイテムの価格などです。","breadcrumbs":"一般的なコレクション » ベクタで一連の値を保持する","id":"ch08-01-vectors.html#aベクタで一連の値を保持する","title":"ベクタで一連の値を保持する"},"ch08-01-vectors.html#aベクタの値を走査する":{"body":"ベクタの要素に順番にアクセスしたいなら、添え字で1回に1要素にアクセスするのではなく、全要素を走査することができます。 リスト8-8で for ループを使い、 i32 のベクタの各要素に対する不変な参照を得て、それらを出力する方法を示しています。 let v = vec![100, 32, 57];\nfor i in &v { println!(\"{}\", i);\n} リスト8-8: for ループで要素を走査し、ベクタの各要素を出力する 全要素に変更を加える目的で、可変なベクタの各要素への可変な参照を走査することもできます。 リスト8-9の for ループでは、各要素に 50 を足しています。 let mut v = vec![100, 32, 57];\nfor i in &mut v { *i += 50;\n} リスト8-9: ベクタの要素への可変な参照を走査する 可変参照が参照している値を変更するには、 += 演算子を使用する前に、 参照外し演算子( * )を使用して i の値に辿り着かないといけません。","breadcrumbs":"一般的なコレクション » ベクタの値を走査する","id":"ch08-01-vectors.html#aベクタの値を走査する","title":"ベクタの値を走査する"},"ch08-01-vectors.html#aベクタの要素を読む":{"body":"もうベクタを生成し、更新し、破壊する方法を知ったので、中身を読む方法を知るのはいいステップアップです。 ベクタに保持された値を参照する方法は2つあります。例では、さらなる明瞭性を求めて、 これらの関数から返る値の型を注釈しました。 リスト8-5に示したのは、両メソッドがベクタの値に対して、添字記法と get メソッドによりアクセスするところです。 let v = vec![1, 2, 3, 4, 5]; let third: &i32 = &v[2];\nlet third: Option<&i32> = v.get(2); リスト8-5: 添字記法か get メソッドを使用してベクタの要素にアクセスする ここでは、2つのことに注目してください。まず、3番目の要素を得るのに 2 という添え字の値を使用していることです: ベクタは、数値により順序付けされ、添え字は0から始まります。2番目に、3番目の要素を得る2つの方法は、 & と [] を使用して参照を得るものと、番号を引数として get メソッドに渡して、 Option<&T> を得るものということです。 Rustには要素を参照する方法が2通りあるので、ベクタに要素が含まれない番号の値を使用しようとした時に、 プログラムの振る舞いを選択できます。例として、ベクタに5つ要素があり、添え字100の要素にアクセスを試みた場合、 プログラムがすることを確認しましょう。リスト8-6に示したようにですね。 let v = vec![1, 2, 3, 4, 5]; let does_not_exist = &v[100];\nlet does_not_exist = v.get(100); リスト8-6: 5つの要素を含むベクタの添え字100の要素にアクセスしようとする このコードを走らせると、最初の [] メソッドはプログラムをパニックさせます。存在しない要素を参照しているからです。 このメソッドは、ベクタの終端を超えて要素にアクセスしようとした時にプログラムをクラッシュさせたい場合に最適です。 get メソッドがベクタ外の添え字を渡されると、パニックすることなく None を返します。 普通の状態でも、ベクタの範囲外にアクセスする可能性がある場合に、このメソッドを使用することになるでしょう。 そうしたら、コードには Some(&element) か None を扱うロジックが存在することになります。そう、 第6章で議論したように。例えば、添え字は人間に数値を入力してもらうことで得ることもできます。 もし大きすぎる値を誤って入力し、プログラムが None 値を得てしまったら、現在ベクタに幾つ要素があるかをユーザに教え、 再度正しい値を入力してもらうことができるでしょう。その方が、タイプミスでプログラムをクラッシュさせるより、 ユーザに優しくなるでしょう。 プログラムに有効な参照がある場合、借用チェッカー(borrow checker)は(第4章で解説しましたが)、 所有権と借用規則を強制し、ベクタの中身へのこの参照や他のいかなる参照も有効であり続けることを保証してくれます。 同一スコープ上では、可変と不変な参照を同時には存在させられないというルールを思い出してください。 このルールはリスト8-7にも適用され、リスト8-7ではベクタの最初の要素への不変参照を保持し、 終端に要素を追加しようとしていますが、動きません。 let mut v = vec![1, 2, 3, 4, 5]; let first = &v[0]; v.push(6); println!(\"The first element is: {}\", first); リスト8-7: 要素への参照を保持しつつ、ベクタに要素を追加しようとする このコードをコンパイルすると、こんなエラーになります: error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable\n(エラー: 不変としても借用されているので、`v`を可変で借用できません) --> src/main.rs:10:5 |\n8 | let first = &v[0]; | - immutable borrow occurs here | (不変借用はここで発生しています)\n9 |\n10 | v.push(6); | ^^^^^^^^^ mutable borrow occurs here | (可変借用は、ここで発生しています)\n11 |\n12 | println!(\"The first element is: {}\", first); | ----- borrow later used here (不変借用はその後ここで使われています) リスト8-7のコードは、一見動くはずのように見えるかもしれません: なぜ、最初の要素への参照が、 ベクタの終端への変更を気にかける必要があるのでしょうか?このエラーは、ベクタの動作法のせいです: 新規要素をベクタの終端に追加すると、ベクタが現在存在する位置に隣り合って要素を入れるだけの領域がなかった場合に、 メモリの新規確保をして古い要素を新しいスペースにコピーする必要があるかもしれないからです。 その場合、最初の要素を指す参照は、解放されたメモリを指すことになるでしょう。借用規則により、 そのような場面に陥らないよう回避されるのです。 注釈: Vec の実装に関する詳細については、 “The Rustonomicon” を参照してください。 訳注: 日本語版のThe Rustonomiconは こちら です。","breadcrumbs":"一般的なコレクション » ベクタの要素を読む","id":"ch08-01-vectors.html#aベクタの要素を読む","title":"ベクタの要素を読む"},"ch08-01-vectors.html#aベクタをドロップすれば要素もドロップする":{"body":"他のあらゆる 構造体 同様、ベクタもスコープを抜ければ、解放されます。リスト8-4に注釈したようにですね。 { let v = vec![1, 2, 3, 4]; // vで作業をする } // <- vはここでスコープを抜け、解放される リスト8-4: ベクタとその要素がドロップされる箇所を示す ベクタがドロップされると、その中身もドロップされます。つまり、保持されていた整数値が、 片付けられるということです。これは一見単純な点に見えるかもしれませんが、ベクタの要素への参照を導入した途端、 もうちょっと複雑になる可能性を秘めています。次は、それに挑んでいきましょう!","breadcrumbs":"一般的なコレクション » ベクタをドロップすれば、要素もドロップする","id":"ch08-01-vectors.html#aベクタをドロップすれば要素もドロップする","title":"ベクタをドロップすれば、要素もドロップする"},"ch08-01-vectors.html#aベクタを更新する":{"body":"ベクタを生成し、それから要素を追加するには、リスト8-3に示したように、 push メソッドを使用できます。 let mut v = Vec::new(); v.push(5);\nv.push(6);\nv.push(7);\nv.push(8); リスト8-3: push メソッドを使用してベクタ型に値を追加する あらゆる変数同様、第3章で議論したように、値を変化させたかったら、 mut キーワードで可変にする必要があります。 中に配置する数値は全て i32 型であり、コンパイラはこのことをデータから推論するので、 Vec という注釈は必要なくなります。","breadcrumbs":"一般的なコレクション » ベクタを更新する","id":"ch08-01-vectors.html#aベクタを更新する","title":"ベクタを更新する"},"ch08-01-vectors.html#a新しいベクタを生成する":{"body":"新しい空のベクタを作るには、リスト8-1に示されたように、 Vec::new 関数を呼べばよいです。 let v: Vec = Vec::new(); リスト8-1: 新しい空のベクタを生成して i32 型の値を保持する ここでは、型注釈を付け足したことに注目してください。このベクタに対して、何も値を挿入していないので、 コンパイラには、どんなデータを保持させるつもりなのかわからないのです。これは重要な点です。ベクタは、 ジェネリクスを使用して実装されているのです; 独自の型でジェネリクスを使用する方法については、 第10章で解説します。今は、標準ライブラリにより提供されている Vec 型は、どんな型でも保持でき、 特定のベクタが特定の型を保持するとき、その型は山かっこ内に指定されることを知っておいてください。 リスト8-1では、コンパイラに v の Vec は、 i32 型の要素を保持すると指示しました。 より現実的なコードでは、一旦値を挿入したら、コンパイラは保持させたい値の型をしばしば推論できるので、 この型注釈をすることは滅多にありません。初期値のある Vec を生成する方が一般的ですし、 Rustには、利便性のために vec! というマクロも用意されています。このマクロは、 与えた値を保持する新しいベクタ型を生成します。リスト8-2では、 1 、 2 、 3 という値を持つ新しい Vec を生成しています。 let v = vec![1, 2, 3]; リスト8-2: 値を含む新しいベクタを生成する 初期値の i32 値を与えたので、コンパイラは、 v の型が Vec であると推論でき、型注釈は必要なくなりました。 次は、ベクタを変更する方法を見ましょう。","breadcrumbs":"一般的なコレクション » 新しいベクタを生成する","id":"ch08-01-vectors.html#a新しいベクタを生成する","title":"新しいベクタを生成する"},"ch08-01-vectors.html#enumを使って複数の型を保持する":{"body":"この章の冒頭で、ベクタは同じ型の値しか保持できないと述べました。これは不便なこともあります; 異なる型の要素を保持する必要性が出てくるユースケースも確かにあるわけです。幸運なことに、 enumの列挙子は、同じenumの型の元に定義されるので、ベクタに異なる型の要素を保持する必要が出たら、 enumを定義して使用することができます! 例えば、スプレッドシートの行から値を得たくなったとしましょう。ここで行の列には、整数を含むものや、 浮動小数点数を含むもの、文字列を含むものがあります。列挙子が異なる値の型を保持するenumを定義できます。 そして、このenumの列挙子は全て同じ型: enumの型と考えられるわけです。それからそのenumを保持するベクタを生成でき、 結果的に異なる型を保持できるようになるわけです。リスト8-10でこれを模擬しています。 enum SpreadsheetCell { Int(i32), Float(f64), Text(String),\n} let row = vec![ SpreadsheetCell::Int(3), SpreadsheetCell::Text(String::from(\"blue\")), SpreadsheetCell::Float(10.12),\n]; リスト8-10: enum を定義して、一つのベクタに異なる型の値を保持する 各要素を格納するのにヒープ上でズバリどれくらいのメモリが必要になるかをわかるように、 コンパイラがコンパイル時にベクタに入る型を知る必要があります。副次的な利点は、 このベクタではどんな型が許容されるのか明示できることです。もしRustでベクタがどんな型でも保持できたら、 ベクタの要素に対して行われる処理に対して一つ以上の型がエラーを引き起こす可能性があったでしょう。 enumに加えて match 式を使うことは、第6章で議論した通り、コンパイル時にありうる場合全てに対処していることをコンパイラが、 保証できることを意味します。 プログラム記述時にプログラムがベクタに実行時に保持するありとあらゆる一連の型をプログラマが知らない場合、 このenumテクニックはうまく動かないでしょう。代わりにトレイトオブジェクトを使用することができ、こちらは第17章で講義します。 今や、ベクタを使用するべき最も一般的な方法について触れ、議論したので、標準ライブラリで Vec に定義されている多くの有益なメソッドについては、 APIドキュメントを確認することを心得てください。例として、 push に加えて、 pop メソッドは最後の要素を削除して返します。 次のコレクション型に移りましょう: String です!","breadcrumbs":"一般的なコレクション » Enumを使って複数の型を保持する","id":"ch08-01-vectors.html#enumを使って複数の型を保持する","title":"Enumを使って複数の型を保持する"},"ch08-02-strings.html#a文字列でutf-8でエンコードされたテキストを保持する":{"body":"第4章で文字列について語りましたが、今度はより掘り下げていきましょう。新参者のRustaceanは、 3つの概念の組み合わせにより、文字列でよく行き詰まります: Rustのありうるエラーを晒す性質、 多くのプログラマが思っている以上に文字列が複雑なデータ構造であること、そしてUTF-8です。 これらの要因が、他のプログラミング言語から移ってきた場合、一見困難に見えるように絡み合うわけです。 コレクションの文脈で文字列を議論することは、有用なことです。なぜなら、文字列はテキストとして解釈された時に有用になる機能を提供するメソッドと、 バイトの塊で実装されているからです。この節では、生成、更新、読み込みのような全コレクションが持つ String の処理について語ります。 また、 String が他のコレクションと異なる点についても議論します。具体的には、人間とコンピュータが String データを解釈する方法の差異により、 String に添え字アクセスする方法がどう複雑なのかということです。","breadcrumbs":"一般的なコレクション » 文字列でUTF-8でエンコードされたテキストを保持する","id":"ch08-02-strings.html#a文字列でutf-8でエンコードされたテキストを保持する","title":"文字列でUTF-8でエンコードされたテキストを保持する"},"ch08-02-strings.html#a文字列とは":{"body":"まずは、 文字列 という用語の意味を定義しましょう。Rustには、言語の核として1種類しか文字列型が存在しません。 文字列スライスの str で、通常借用された形態 &str で見かけます。第4章で、 文字列スライス について語りました。 これは、別の場所に格納されたUTF-8エンコードされた文字列データへの参照です。例えば、文字列リテラルは、 プログラムのバイナリ出力に格納されるので、文字列スライスになります。 String 型は、言語の核として組み込まれるのではなく、Rustの標準ライブラリで提供されますが、伸長可能、 可変、所有権のあるUTF-8エンコードされた文字列型です。RustaceanがRustにおいて「文字列」を指したら、 どちらかではなく、 String と文字列スライスの &str のことを通常意味します。この節は、大方、 String についてですが、どちらの型もRustの標準ライブラリで重宝されており、 どちらもUTF-8エンコードされています。 また、Rustの標準ライブラリには、他の文字列型も含まれています。 OsString 、 OsStr 、 CString 、 CStr などです。 ライブラリクレートにより、文字列データを格納する選択肢はさらに増えます。 それらの名前が全て String か Str で終わっているのがわかりますか?所有権ありと借用されたバージョンを指しているのです。 ちょうど以前見かけた String と &str のようですね。例えば、これらの文字列型は、異なるエンコード方法でテキストを格納していたり、 メモリ上の表現が異なったりします。この章では、これらの他の種類の文字列については議論しません; 使用方法やどれが最適かについては、APIドキュメントを参照してください。","breadcrumbs":"一般的なコレクション » 文字列とは?","id":"ch08-02-strings.html#a文字列とは","title":"文字列とは?"},"ch08-02-strings.html#a文字列に添え字アクセスする":{"body":"他の多くのプログラミング言語では、文字列中の文字に、添え字で参照してアクセスすることは、有効なコードであり、 一般的な処理です。しかしながら、Rustにおいて、添え字記法で String の一部にアクセスしようとすると、 エラーが発生するでしょう。リスト8-19の非合法なコードを考えてください。 let s1 = String::from(\"hello\");\nlet h = s1[0]; リスト8-19: 文字列に対して添え字記法を試みる このコードは、以下のようなエラーに落ち着きます: error[E0277]: the trait bound `std::string::String: std::ops::Index<{Integer}>` is not satisfied\n(エラー: トレイト境界`std::string::String: std::ops::Index<{Integer}>`が満たされていません) |>\n3 |> let h = s1[0]; |> ^^^^^ the type `std::string::String` cannot be indexed by `{Integer}` |> (型`std::string::String`は`{Integer}`で添え字アクセスできません) = help: the trait `std::ops::Index<{Integer}>` is not implemented for `std::string::String` (ヘルプ: `std::ops::Index<{Integer}>`というトレイトが`std::string::String`に対して実装されていません) エラーと注釈が全てを物語っています: Rustの文字列は、添え字アクセスをサポートしていないのです。 でも、なぜでしょうか?その疑問に答えるには、Rustがメモリにどのように文字列を保持しているかについて議論する必要があります。 内部表現 String は Vec のラッパです。リスト8-14から適切にUTF-8でエンコードされた文字列の例をご覧ください。 まずは、これ: let len = String::from(\"Hola\").len(); この場合、 len は4になり、これは、文字列\"Hola\"を保持するベクタの長さが4バイトであることを意味します。 これらの各文字は、UTF-8でエンコードすると、1バイトになるのです。しかし、以下の行ではどうでしょうか? (この文字列は大文字のキリル文字Zeで始まり、アラビア数字の3では始まっていないことに注意してください) let len = String::from(\"Здравствуйте\").len(); 文字列の長さはと問われたら、あなたは12と答えるかもしれません。ところが、Rustの答えは、24です: “Здравствуйте”をUTF-8でエンコードすると、この長さになります。各Unicodeスカラー値は、2バイトの領域を取るからです。 それ故に、文字列のバイトの添え字は、必ずしも有効なUnicodeのスカラー値とは相互に関係しないのです。 デモ用に、こんな非合法なRustコードを考えてください: let hello = \"Здравствуйте\";\nlet answer = &hello[0]; answer の値は何になるべきでしょうか?最初の文字の З になるべきでしょうか?UTF-8エンコードされた時、 З の最初のバイトは 208 、2番目は 151 になるので、 answer は実際、 208 になるべきですが、 208 は単独では有効な文字ではありません。この文字列の最初の文字を求めている場合、 208 を返すことは、 ユーザの望んでいるものではないでしょう; しかしながら、Rustには、バイト添え字0の位置には、そのデータしかないのです。 文字列がラテン文字のみを含む場合でも、ユーザは一般的にバイト値が返ることを望みません: &\"hello\"[0] がバイト値を返す有効なコードだったら、 h ではなく、 104 を返すでしょう。 予期しない値を返し、すぐには判明しないバグを引き起こさないために、Rustはこのコードを全くコンパイルせず、 開発過程の早い段階で誤解を防いでくれるのです。 バイトとスカラー値と書記素クラスタ!なんてこった! UTF-8について別の要点は、実際Rustの観点から文字列を見るには3つの関連した方法があるということです: バイトとして、スカラー値として、そして、書記素クラスタ(人間が 文字 と呼ぶものに一番近い)としてです。 ヒンディー語の単語、“नमस्ते”をデーヴァナーガリー( 訳注 : サンスクリット語とヒンディー語を書くときに使われる書記法)で表記したものを見たら、 以下のような見た目の u8 値のベクタとして保持されます: [224, 164, 168, 224, 164, 174, 224, 164, 184, 224, 165, 141, 224, 164, 164,\n224, 165, 135] 18バイトになり、このようにしてコンピュータは最終的にこのデータを保持しているわけです。これをUnicodeスカラー値として見たら (Rustの char 型はこれなのですが)このバイトは以下のような見た目になります: ['न', 'म', 'स', '्', 'त', 'े'] ここでは、6つ char 値がありますが、4番目と6番目は文字ではありません: 単独では意味をなさないダイアクリティックです。 最後に、書記素クラスタとして見たら、このヒンディー語の単語を作り上げる人間が4文字と呼ぶであろうものが得られます: [\"न\", \"म\", \"स्\", \"ते\"] Rustには、データが表す自然言語に関わらず、各プログラムが必要な解釈方法を選択できるように、 コンピュータが保持する生の文字列データを解釈する方法がいろいろ用意されています。 Rustで文字を得るのに String に添え字アクセスすることが許されない最後の理由は、 添え字アクセスという処理が常に定数時間(O(1))になると期待されるからです。 しかし、 String でそのパフォーマンスを保証することはできません。というのも、 合法な文字がいくつあるか決定するのに、最初から添え字まで中身を走査する必要があるからです。","breadcrumbs":"一般的なコレクション » 文字列に添え字アクセスする","id":"ch08-02-strings.html#a文字列に添え字アクセスする","title":"文字列に添え字アクセスする"},"ch08-02-strings.html#a文字列はそう単純じゃない":{"body":"まとめると、文字列は込み入っています。プログラミング言語ごとにこの複雑性をプログラマに提示する方法は違います。 Rustでは、 String データを正しく扱うことが、全てのRustプログラムにとっての既定動作になっているわけであり、 これは、プログラマがUTF-8データを素直に扱う際に、よりしっかり考えないといけないことを意味します。 このトレードオフにより、他のプログラミング言語で見えるよりも文字列の複雑性がより露出していますが、 ASCII以外の文字に関するエラーを開発の後半で扱わなければならない可能性が排除されているのです。 もう少し複雑でないものに切り替えていきましょう: ハッシュマップです!","breadcrumbs":"一般的なコレクション » 文字列はそう単純じゃない","id":"ch08-02-strings.html#a文字列はそう単純じゃない","title":"文字列はそう単純じゃない"},"ch08-02-strings.html#a文字列をスライスする":{"body":"文字列に添え字アクセスするのは、しばしば悪い考えです。文字列添え字処理の戻り値の型が明瞭ではないからです: バイト値、文字、書記素クラスタ、あるいは文字列スライスにもなります。故に、文字列スライスを生成するのに、 添え字を使う必要が本当に出た場合にコンパイラは、もっと特定するよう求めてきます。添え字アクセスを特定し、 文字列スライスが欲しいと示唆するためには、 [] で1つの数値により添え字アクセスするのではなく、 範囲とともに [] を使って、特定のバイトを含む文字列スライスを作ることができます: let hello = \"Здравствуйте\"; let s = &hello[0..4]; ここで、 s は文字列の最初の4バイトを含む &str になります。先ほど、これらの文字は各々2バイトになると指摘しましたから、 s は Зд になります。 &hello[0..1] と使用したら、何が起きるでしょうか?答え: Rustはベクタの非合法な添え字にアクセスしたかのように、 実行時にパニックするでしょう: thread 'main' panicked at 'byte index 1 is not a char boundary; it is inside 'З' (bytes 0..2) of `Здравствуйте`', src/libcore/str/mod.rs:2188:4\n('main'スレッドは「バイト添え字1は文字の境界ではありません; `Здравствуйте`の'З'(バイト番号0から2)の中です」でパニックしました) 範囲を使用して文字列スライスを作る際にはプログラムをクラッシュさせることがあるので、気をつけるべきです。","breadcrumbs":"一般的なコレクション » 文字列をスライスする","id":"ch08-02-strings.html#a文字列をスライスする","title":"文字列をスライスする"},"ch08-02-strings.html#a文字列を更新する":{"body":"String は、サイズを伸ばすことができ、 Vec の中身のように、追加のデータをプッシュすれば、中身も変化します。 付け加えると、 String 値を連結する + 演算子や、 format! マクロを便利に使用することができます。 push_str と push で文字列に追加する push_str メソッドで文字列スライスを追記することで、 String を伸ばすことができます。 リスト8-15の通りです。 let mut s = String::from(\"foo\");\ns.push_str(\"bar\"); リスト8-15: push_str メソッドで String に文字列スライスを追記する この2行の後、 s は foobar を含むことになります。 push_str メソッドは、必ずしも引数の所有権を得なくていいので、 文字列スライスを取ります。例えば、リスト8-16のコードは、中身を s1 に追加した後、 s2 を使えなかったら残念だということを示しています。 let mut s1 = String::from(\"foo\");\nlet s2 = \"bar\";\ns1.push_str(s2);\nprintln!(\"s2 is {}\", s2); リスト8-16: 中身を String に追加した後に、文字列スライスを使用する もし、 push_str メソッドが s2 の所有権を奪っていたら、最後の行でその値を出力することは不可能でしょう。 ところが、このコードは予想通りに動きます! push メソッドは、1文字を引数として取り、 String に追加します。リスト8-15は、 push メソッドで l を String に追加するコードを呈示しています。 let mut s = String::from(\"lo\");\ns.push('l'); リスト8-17: push で String 値に1文字を追加する このコードの結果、 s は lol を含むことになるでしょう。 編者注: lol は laughing out loud (大笑いする)の頭文字からできたスラングです。 日本語の www みたいなものですね。 + 演算子、または format! マクロで連結 2つのすでにある文字列を組み合わせたくなることがよくあります。リスト8-18に示したように、 一つ目の方法は、 + 演算子を使用することです。 let s1 = String::from(\"Hello, \");\nlet s2 = String::from(\"world!\");\nlet s3 = s1 + &s2; // s1はムーブされ、もう使用できないことに注意 リスト8-18: + 演算子を使用して二つの String 値を新しい String 値にする このコードの結果、 s3 という文字列は、 Hello, world! を含むことになるでしょう。 追記の後、 s1 がもう有効でなくなった理由と、 s2 への参照を使用した理由は、 + 演算子を使用した時に呼ばれるメソッドのシグニチャと関係があります。 + 演算子は、 add メソッドを使用し、 そのシグニチャは以下のような感じです: fn add(self, s: &str) -> String { これは、標準ライブラリにあるシグニチャそのものではありません: 標準ライブラリでは、 add はジェネリクスで定義されています。 ここでは、ジェネリックな型を具体的な型に置き換えた add のシグニチャを見ており、これは、 このメソッドを String 値とともに呼び出した時に起こることです。ジェネリクスについては、第10章で議論します。 このシグニチャが、 + 演算子の巧妙な部分を理解するのに必要な手がかりになるのです。 まず、 s2 には & がついてます。つまり、 add 関数の s 引数のために最初の文字列に2番目の文字列の参照を追加するということです: String には &str を追加することしかできません。要するに2つの String 値を追加することはできないのです。 でも待ってください。 add の第2引数で指定されているように、 &s2 の型は、 &str ではなく、 &String ではないですか。では、なぜ、リスト8-18は、コンパイルできるのでしょうか? add 呼び出しで &s2 を使える理由は、コンパイラが &String 引数を &str に 型強制 してくれるためです。 add メソッド呼び出しの際、コンパイラは、 参照外し型強制 というものを使用し、ここでは、 &s2 を &s2[..] に変えるものと考えることができます。参照外し型強制について詳しくは、第15章で議論します。 add が s 引数の所有権を奪わないので、この処理後も s2 が有効な String になるわけです。 2番目に、シグニチャから add は self の所有権をもらうことがわかります。 self には & がついてい ない からです。 これはつまり、リスト8-18において s1 は add 呼び出しにムーブされ、その後は有効ではなくなるということです。 故に、 s3 = s1 + &s2; は両文字列をコピーして新しいものを作るように見えますが、 この文は実際には s1 の所有権を奪い、 s2 の中身のコピーを追記し、結果の所有権を返すのです。言い換えると、 たくさんのコピーをしているように見えますが、違います; 実装は、コピーよりも効率的です。 複数の文字列を連結する必要が出ると、 + 演算子の振る舞いは扱いにくくなります: let s1 = String::from(\"tic\");\nlet s2 = String::from(\"tac\");\nlet s3 = String::from(\"toe\"); let s = s1 + \"-\" + &s2 + \"-\" + &s3; ここで、 s は tic-tac-toe になるでしょう。 + と \" 文字のせいで何が起きているのかわかりにくいです。 もっと複雑な文字列の連結には、 format! マクロを使用することができます: let s1 = String::from(\"tic\");\nlet s2 = String::from(\"tac\");\nlet s3 = String::from(\"toe\"); let s = format!(\"{}-{}-{}\", s1, s2, s3); このコードでも、 s は tic-tac-toe になります。 format! マクロは、 println! と同様の動作をしますが、 出力をスクリーンに行う代わりに、中身を String で返すのです。 format! を使用したコードの方がはるかに読みやすく、 引数の所有権を奪いません。","breadcrumbs":"一般的なコレクション » 文字列を更新する","id":"ch08-02-strings.html#a文字列を更新する","title":"文字列を更新する"},"ch08-02-strings.html#a文字列を走査するメソッド群":{"body":"幸いなことに、他の方法でも文字列の要素にアクセスすることができます。 もし、個々のUnicodeスカラー値に対して処理を行う必要があったら、最適な方法は chars メソッドを使用するものです。 “नमस्ते”に対して chars を呼び出したら、分解して6つの char 型の値を返すので、各要素にアクセスするには、 その結果を走査すればいいわけです: for c in \"नमस्ते\".chars() { println!(\"{}\", c);\n} このコードは、以下のように出力します: न\nम\nस\n्\nत\nे bytes メソッドは、各バイトをそのまま返すので、最適になることもあるかもしれません: for b in \"नमस्ते\".bytes() { println!(\"{}\", b);\n} このコードは、 String をなす18バイトを出力します: 224\n164\n// --snip--\n165\n135 ですが、合法なUnicodeスカラー値は、2バイト以上からなる場合もあることは心得ておいてください。 書記素クラスタを文字列から得る方法は複雑なので、この機能は標準ライブラリでは提供されていません。 この機能が必要なら、 crates.io でクレートを入手可能です。","breadcrumbs":"一般的なコレクション » 文字列を走査するメソッド群","id":"ch08-02-strings.html#a文字列を走査するメソッド群","title":"文字列を走査するメソッド群"},"ch08-02-strings.html#a新規文字列を生成する":{"body":"Vec で使用可能な処理の多くが String でも使用できます。文字列を生成する new 関数から始めましょうか。 リスト8-11に示したようにですね。 let mut s = String::new(); リスト8-11: 新しい空の String を生成する この行は、新しい空の s という文字列を生成しています。それからここにデータを読み込むことができるわけです。 だいたい、文字列の初期値を決めるデータがあるでしょう。そのために、 to_string メソッドを使用します。 このメソッドは、文字列リテラルのように、 Display トレイトを実装する型ならなんでも使用できます。 リスト8-12に2例、示しています。 let data = \"initial contents\"; let s = data.to_string(); // the method also works on a literal directly:\nlet s = \"initial contents\".to_string(); リスト8-12: to_string メソッドを使用して文字列リテラルから String を生成する このコードは、 initial contents (初期値)を含む文字列を生成します。 さらに、 String::from 関数を使っても、文字列リテラルから String を生成することができます。 リスト8-13のコードは、 to_string を使用するリスト8-12のコードと等価です。 let s = String::from(\"initial contents\"); リスト8-13: String::from 関数を使って文字列リテラルから String を作る 文字列は、非常に多くのものに使用されるので、多くの異なる一般的なAPIを使用でき、たくさんの選択肢があるわけです。 冗長に思われるものもありますが、適材適所です!今回の場合、 String::from と to_string は全く同じことをします。 従って、どちらを選ぶかは、スタイル次第です。 文字列はUTF-8エンコードされていることを覚えていますか?要するに文字列には、適切にエンコードされていればどんなものでも含めます。 リスト8-14に示したように。 let hello = String::from(\"السلام عليكم\");\nlet hello = String::from(\"Dobrý den\");\nlet hello = String::from(\"Hello\");\nlet hello = String::from(\"שָׁלוֹם\");\nlet hello = String::from(\"नमस्ते\");\nlet hello = String::from(\"こんにちは\");\nlet hello = String::from(\"안녕하세요\");\nlet hello = String::from(\"你好\");\nlet hello = String::from(\"Olá\");\nlet hello = String::from(\"Здравствуйте\");\nlet hello = String::from(\"Hola\"); リスト8-14: いろんな言語の挨拶を文字列に保持する これらは全て、有効な String の値です。","breadcrumbs":"一般的なコレクション » 新規文字列を生成する","id":"ch08-02-strings.html#a新規文字列を生成する","title":"新規文字列を生成する"},"ch08-03-hash-maps.html#aまとめ":{"body":"ベクタ、文字列、ハッシュマップはデータを保持し、アクセスし、変更する必要のあるプログラムで必要になる、 多くの機能を提供してくれるでしょう。今なら解決可能なはずの練習問題を用意しました: 整数のリストが与えられ、ベクタを使ってmean(平均値)、median(ソートされた時に真ん中に来る値)、 mode(最も頻繁に出現する値; ハッシュマップがここでは有効活用できるでしょう)を返してください。 文字列をピッグ・ラテン( 訳注 : 英語の言葉遊びの一つ)に変換してください。各単語の最初の子音は、 単語の終端に移り、\"ay\"が足されます。従って、\"first\"は\"irst-fay\"になります。ただし、 母音で始まる単語には、お尻に\"hay\"が付け足されます(\"apple\"は\"apple-hay\"になります)。 UTF-8エンコードに関する詳細を心に留めておいてください! ハッシュマップとベクタを使用して、ユーザに会社の部署に雇用者の名前を追加させられるテキストインターフェイスを作ってください。 例えば、\"Add Sally to Engineering\"(開発部門にサリーを追加)や\"Add Amir to Sales\"(販売部門にアミールを追加)などです。 それからユーザに、ある部署にいる人間の一覧や部署ごとにアルファベット順で並べ替えられた会社の全人間の一覧を扱わせてあげてください。 標準ライブラリのAPIドキュメントには、この練習問題に有用な、ベクタ、文字列、ハッシュマップのメソッドが解説されています。 処理が失敗することもあるような、より複雑なプログラムに入り込んできています; ということは、 エラーの処理法について議論するのにぴったりということです。次はそれをします!","breadcrumbs":"一般的なコレクション » まとめ","id":"ch08-03-hash-maps.html#aまとめ","title":"まとめ"},"ch08-03-hash-maps.html#aハッシュマップと所有権":{"body":"i32 のような Copy トレイトを実装する型について、値はハッシュマップにコピーされます。 String のような所有権のある値なら、値はムーブされ、リスト8-22でデモされているように、 ハッシュマップはそれらの値の所有者になるでしょう。 use std::collections::HashMap; let field_name = String::from(\"Favorite color\");\nlet field_value = String::from(\"Blue\"); let mut map = HashMap::new();\nmap.insert(field_name, field_value);\n// field_name and field_value are invalid at this point, try using them and\n// see what compiler error you get!\n// field_nameとfield_valueはこの時点で無効になる。試しに使ってみて\n// どんなコンパイルエラーが出るか確認してみて! リスト8-22: 一旦挿入されたら、キーと値はハッシュマップに所有されることを示す insert を呼び出して field_name と field_value がハッシュマップにムーブされた後は、 これらの変数を使用することは叶いません。 値への参照をハッシュマップに挿入したら、値はハッシュマップにムーブされません。参照が指している値は、 最低でもハッシュマップが有効な間は、有効でなければなりません。これらの問題について詳細には、 第10章の「ライフタイムで参照を有効化する」節で語ります。","breadcrumbs":"一般的なコレクション » ハッシュマップと所有権","id":"ch08-03-hash-maps.html#aハッシュマップと所有権","title":"ハッシュマップと所有権"},"ch08-03-hash-maps.html#aハッシュマップに値に紐づいたキーを格納する":{"body":"一般的なコレクションのトリを飾るのは、 ハッシュマップ です。型 HashMap は、 K 型のキーと V 型の値の対応関係を保持します。これを ハッシュ関数 を介して行います。 ハッシュ関数は、キーと値のメモリ配置方法を決めるものです。多くのプログラミング言語でもこの種のデータ構造はサポートされていますが、 しばしば名前が違います。hash、map、object、ハッシュテーブル、連想配列など、枚挙に暇(いとま)はありません。 ハッシュマップは、ベクタのように番号ではなく、どんな型にもなりうるキーを使ってデータを参照したいときに有用です。 例えば、ゲームにおいて、各チームのスコアをハッシュマップで追いかけることができます。ここで、各キーはチーム名、 値が各チームのスコアになります。チーム名が与えられれば、スコアを扱うことができるわけです。 この節でハッシュマップの基礎的なAPIを見ていきますが、より多くのグッズが標準ライブラリにより、 HashMap 上に定義された関数に隠されています。いつものように、 もっと情報が欲しければ、標準ライブラリのドキュメントをチェックしてください。","breadcrumbs":"一般的なコレクション » ハッシュマップに値に紐づいたキーを格納する","id":"ch08-03-hash-maps.html#aハッシュマップに値に紐づいたキーを格納する","title":"ハッシュマップに値に紐づいたキーを格納する"},"ch08-03-hash-maps.html#aハッシュマップの値にアクセスする":{"body":"リスト8-23に示したように、キーを get メソッドに提供することで、ハッシュマップから値を取り出すことができます。 use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from(\"Blue\"), 10);\nscores.insert(String::from(\"Yellow\"), 50); let team_name = String::from(\"Blue\");\nlet score = scores.get(&team_name); リスト8-23: ハッシュマップに保持されたブルーチームのスコアにアクセスする ここで、 score はブルーチームに紐づけられた値になり、結果は Some(&10) となるでしょう。 結果は Some に包まれます。というのも、 get は Option<&V> を返すからです; キーに対応する値がハッシュマップになかったら、 get は None を返すでしょう。プログラムは、この Option を第6章で講義した方法のどれかで扱う必要があるでしょう。 ベクタのように、 for ループでハッシュマップのキーと値のペアを走査することができます: use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from(\"Blue\"), 10);\nscores.insert(String::from(\"Yellow\"), 50); for (key, value) in &scores { println!(\"{}: {}\", key, value);\n} このコードは、各ペアを任意の順番で出力します: Yellow: 50\nBlue: 10","breadcrumbs":"一般的なコレクション » ハッシュマップの値にアクセスする","id":"ch08-03-hash-maps.html#aハッシュマップの値にアクセスする","title":"ハッシュマップの値にアクセスする"},"ch08-03-hash-maps.html#aハッシュマップを更新する":{"body":"キーと値の数は伸長可能なものの、各キーには1回に1つの値しか紐づけることができません。 ハッシュマップ内のデータを変えたい時は、すでにキーに値が紐づいている場合の扱い方を決めなければなりません。 古い値を新しい値で置き換えて、古い値を完全に無視することもできます。古い値を保持して、 新しい値を無視し、キーにまだ値が ない 場合に新しい値を追加するだけにすることもできます。 あるいは、古い値と新しい値を組み合わせることもできます。各方法について見ていきましょう! 値を上書きする キーと値をハッシュマップに挿入し、同じキーを異なる値で挿入したら、そのキーに紐づけられている値は置換されます。 リスト8-24のコードは、 insert を二度呼んでいるものの、ハッシュマップには一つのキーと値の組しか含まれません。 なぜなら、ブルーチームキーに対する値を2回とも挿入しているからです。 use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from(\"Blue\"), 10);\nscores.insert(String::from(\"Blue\"), 25); println!(\"{:?}\", scores); リスト8-24: 特定のキーで保持された値を置き換える このコードは、 {\"Blue\": 25} と出力するでしょう。 10 という元の値は上書きされたのです。 キーに値がなかった時のみ値を挿入する 特定のキーに値があるか確認することは一般的であり、存在しない時に値を挿入することも一般的です。 ハッシュマップには、これを行う entry と呼ばれる特別なAPIがあり、これは、引数としてチェックしたいキーを取ります。 この entry メソッドの戻り値は、 Entry と呼ばれるenumであり、これは存在したりしなかったりする可能性のある値を表します。 イエローチームに対するキーに値が紐づけられているか否か確認したくなったとしましょう。存在しなかったら、 50という値を挿入したく、ブルーチームに対しても同様です。 entry APIを使用すれば、コードはリスト8-25のようになります。 use std::collections::HashMap; let mut scores = HashMap::new();\nscores.insert(String::from(\"Blue\"), 10); scores.entry(String::from(\"Yellow\")).or_insert(50);\nscores.entry(String::from(\"Blue\")).or_insert(50); println!(\"{:?}\", scores); リスト8-25: entry メソッドを使ってキーに値がない場合だけ挿入する Entry 上の or_insert メソッドは、対応する Entry キーが存在した時にそのキーに対する値への可変参照を返すために定義されており、 もしなかったら、引数をこのキーの新しい値として挿入し、新しい値への可変参照を返します。このテクニックの方が、 そのロジックを自分で書くよりもはるかに綺麗な上に、borrow checkerとも親和性が高くなります。 リスト8-25のコードを実行すると、 {\"Yellow\": 50, \"Blue\": 10} と出力するでしょう。 最初の entry 呼び出しは、まだイエローチームに対する値がないので、値50でイエローチームのキーを挿入します。 entry の2回目の呼び出しはハッシュマップを変更しません。なぜなら、ブルーチームには既に10という値があるからです。 古い値に基づいて値を更新する ハッシュマップの別の一般的なユースケースは、キーの値を探し、古い値に基づいてそれを更新することです。 例えば、リスト8-26は、各単語があるテキストに何回出現するかを数え上げるコードを示しています。 キーに単語を入れたハッシュマップを使用し、その単語を何回見かけたか追いかけるために値を増やします。 ある単語を見かけたのが最初だったら、まず0という値を挿入します: use std::collections::HashMap; let text = \"hello world wonderful world\"; let mut map = HashMap::new(); for word in text.split_whitespace() { let count = map.entry(word).or_insert(0); *count += 1;\n} println!(\"{:?}\", map); リスト8-26: 単語とカウントを保持するハッシュマップを使って単語の出現数をカウントする このコードは、 {\"world\": 2, \"hello\": 1, \"wonderful\": 1} と出力するでしょう。 or_insert 関数は実際、このキーに対する値への可変参照( &mut V )を返すのです。 ここでその可変参照を count 変数に保持しているので、その値に代入するには、 まずアスタリスク( * )で count を参照外ししなければならないのです。この可変参照は、 for ループの終端でスコープを抜けるので、これらの変更は全て安全であり、借用規則により許可されるのです。","breadcrumbs":"一般的なコレクション » ハッシュマップを更新する","id":"ch08-03-hash-maps.html#aハッシュマップを更新する","title":"ハッシュマップを更新する"},"ch08-03-hash-maps.html#aハッシュ関数":{"body":"標準では、 HashMap はサービス拒否(DoS)アタックに対して抵抗を示す暗号学的に安全なハッシュ関数を使用します。 これは、利用可能な最速のハッシュアルゴリズムではありませんが、パフォーマンスの欠落と引き換えに安全性を得るというトレードオフは、 価値があります。自分のコードをプロファイリングして、自分の目的では標準のハッシュ関数は遅すぎると判明したら、 異なる hasher を指定することで別の関数に切り替えることができます。hasherとは、 BuildHasher トレイトを実装する型のことです。トレイトについてとその実装方法については、第10章で語ります。 必ずしも独自のhasherを1から作り上げる必要はありません; crates.io には、 他のRustユーザによって共有された多くの一般的なハッシュアルゴリズムを実装したhasherを提供するライブラリがあります。","breadcrumbs":"一般的なコレクション » ハッシュ関数","id":"ch08-03-hash-maps.html#aハッシュ関数","title":"ハッシュ関数"},"ch08-03-hash-maps.html#a新規ハッシュマップを生成する":{"body":"空のハッシュマップを new で作り、要素を insert で追加することができます。リスト8-20では、 名前がブルーとイエローの2チームのスコアを追いかけています。ブルーチームは10点から、イエローチームは50点から始まります。 use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert(String::from(\"Blue\"), 10);\nscores.insert(String::from(\"Yellow\"), 50); リスト8-20: ハッシュマップを生成してキーと値を挿入する 最初に標準ライブラリのコレクション部分から HashMap を use する必要があることに注意してください。 今までの3つの一般的なコレクションの内、これが最も使用頻度が低いので、初期化処理で自動的にスコープに導入される機能には含まれていません。 また、標準ライブラリからのサポートもハッシュマップは少ないです; 例えば、生成するための組み込みマクロがありません。 ベクタと全く同様に、ハッシュマップはデータをヒープに保持します。この HashMap はキーが String 型、 値は i32 型です。ベクタのように、ハッシュマップは均質です: キーは全て同じ型でなければならず、 値も全て同じ型でなければなりません。 ハッシュマップを生成する別の方法は、タプルのベクタに対して collect メソッドを使用するものです。 ここで、各タプルは、キーと値から構成されています。 collect メソッドはいろんなコレクション型にデータをまとめ上げ、 そこには HashMap も含まれています。例として、チーム名と初期スコアが別々のベクタに含まれていたら、 zip メソッドを使ってタプルのベクタを作り上げることができ、そこでは「ブルー」は10とペアになるなどします。 リスト8-21に示したように、それから collect メソッドを使って、そのタプルのベクタをハッシュマップに変換することができるわけです。 use std::collections::HashMap; let teams = vec![String::from(\"Blue\"), String::from(\"Yellow\")];\nlet initial_scores = vec![10, 50]; let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect(); リスト8-21: チームのリストとスコアのリストからハッシュマップを作る ここでは、 HashMap<_, _> という型注釈が必要になります。なぜなら、いろんなデータ構造に まとめ上げる ことができ、 コンパイラは指定しない限り、どれを所望なのかわからないからです。ところが、キーと値の型引数については、 アンダースコアを使用しており、コンパイラはベクタのデータ型に基づいてハッシュマップが含む型を推論することができるのです。","breadcrumbs":"一般的なコレクション » 新規ハッシュマップを生成する","id":"ch08-03-hash-maps.html#a新規ハッシュマップを生成する","title":"新規ハッシュマップを生成する"},"ch09-00-error-handling.html#aエラー処理":{"body":"Rustの信頼性への傾倒は、エラー処理にも及びます。ソフトウェアにおいて、エラーは生きている証しです。 従って、Rustには何かがおかしくなる場面を扱う機能がたくさんあります。多くの場面で、 コンパイラは、プログラマにエラーの可能性を知り、コードのコンパイルが通るまでに何かしら対応を行うことを要求してきます。 この要求により、エラーを発見し、コードを実用に供する前に適切に対処していることを確認することでプログラムを頑健なものにしてくれるのです! Rustでは、エラーは大きく二つに分類されます: 回復可能 と 回復不能 なエラーです。 ファイルが見つからないなどの回復可能なエラーには、問題をユーザに報告し、処理を再試行することが合理的になります。 回復不能なエラーは、常にバグの兆候です。例えば、配列の境界を超えた箇所にアクセスしようとすることなどです。 多くの言語では、この2種のエラーを区別することはなく、例外などの機構を使用して同様に扱います。 Rustには例外が存在しません。代わりに、回復可能なエラーには Result 値があり、 プログラムが回復不能なエラーに遭遇した時には、実行を中止する panic! マクロがあります。 この章では、まず panic! の呼び出しを講義し、それから Result を戻り値にする話をします。 加えて、エラーからの回復を試みるか、実行を中止するか決定する際に考慮すべき事項についても、探究しましょう。","breadcrumbs":"エラー処理","id":"ch09-00-error-handling.html#aエラー処理","title":"エラー処理"},"ch09-01-unrecoverable-errors-with-panic.html#aパニックに対してスタックを巻き戻すか異常終了するか":{"body":"標準では、パニックが発生すると、プログラムは 巻き戻し を始めます。つまり、言語がスタックを遡り、 遭遇した各関数のデータを片付けるということです。しかし、この遡りと片付けはすべきことが多くなります。 対立案は、即座に異常終了し、片付けをせずにプログラムを終了させることです。そうなると、プログラムが使用していたメモリは、 OSが片付ける必要があります。プロジェクトにおいて、実行可能ファイルを極力小さくする必要があれば、 Cargo.toml ファイルの適切な [profile] 欄に panic = 'abort' を追記することで、 パニック時に巻き戻しから異常終了するように切り替えることができます。例として、 リリースモード時に異常終了するようにしたければ、以下を追記してください: [profile.release]\npanic = 'abort' 単純なプログラムで panic! の呼び出しを試してみましょう: ファイル名: src/main.rs fn main() { panic!(\"crash and burn\"); //クラッシュして炎上\n} このプログラムを実行すると、以下のような出力を目の当たりにするでしょう: $ cargo run Compiling panic v0.1.0 (file:///projects/panic) Finished dev [unoptimized + debuginfo] target(s) in 0.25 secs Running `target/debug/panic`\nthread 'main' panicked at 'crash and burn', src/main.rs:2:4\n('main'スレッドはsrc/main.rs:2:4の「クラッシュして炎上」でパニックしました)\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. panic! の呼び出しが、最後の2行に含まれるエラーメッセージを発生させているのです。 1行目にパニックメッセージとソースコード中でパニックが発生した箇所を示唆しています: src/main.rs:2:4 は、 src/main.rs ファイルの2行目4文字目であることを示しています。 この場合、示唆される行は、自分のコードの一部で、その箇所を見に行けば、 panic! マクロ呼び出しがあるわけです。 それ以外では、 panic! 呼び出しが、自分のコードが呼び出しているコードの一部になっている可能性もあるわけです。 エラーメッセージで報告されるファイル名と行番号が、結果的に panic! 呼び出しに導いた自分のコードの行ではなく、 panic! マクロが呼び出されている他人のコードになるでしょう。 panic! 呼び出しの発生元である関数のバックトレースを使用して、 問題を起こしている自分のコードの箇所を割り出すことができます。バックトレースがどんなものか、次に議論しましょう。","breadcrumbs":"エラー処理 » パニックに対してスタックを巻き戻すか異常終了するか","id":"ch09-01-unrecoverable-errors-with-panic.html#aパニックに対してスタックを巻き戻すか異常終了するか","title":"パニックに対してスタックを巻き戻すか異常終了するか"},"ch09-01-unrecoverable-errors-with-panic.html#panic-で回復不能なエラー":{"body":"時として、コードで悪いことが起きるものです。そして、それに対してできることは何もありません。 このような場面で、Rustには panic! マクロが用意されています。 panic! マクロが実行されると、 プログラムは失敗のメッセージを表示し、スタックを巻き戻し掃除して、終了します。これが最もありふれて起こるのは、 何らかのバグが検出された時であり、プログラマには、どうエラーを処理すればいいか明確ではありません。","breadcrumbs":"エラー処理 » panic! で回復不能なエラー","id":"ch09-01-unrecoverable-errors-with-panic.html#panic-で回復不能なエラー","title":"panic! で回復不能なエラー"},"ch09-01-unrecoverable-errors-with-panic.html#panic-バックトレースを使用する":{"body":"別の例を眺めて、自分のコードでマクロを直接呼び出す代わりに、コードに存在するバグにより、 ライブラリで panic! 呼び出しが発生するとどんな感じなのか確かめてみましょう。リスト9-1は、 添え字でベクタの要素にアクセスを試みる何らかのコードです。 ファイル名: src/main.rs fn main() { let v = vec![1, 2, 3]; v[99];\n} リスト9-1: ベクタの境界を超えて要素へのアクセスを試み、 panic! の呼び出しを発生させる ここでは、ベクタの100番目の要素(添え字は0始まりなので添え字99)にアクセスを試みていますが、ベクタには3つしか要素がありません。 この場面では、Rustはパニックします。 [] の使用は、要素を返すと想定されるものの、 無効な添え字を渡せば、ここでRustが返せて正しいと思われる要素は何もないわけです。 他の言語(Cなど)では、この場面で欲しいものではないにもかかわらず、まさしく要求したものを返そうとしてきます: メモリがベクタに属していないにもかかわらず、ベクタ内のその要素に対応するメモリ上の箇所にあるものを何か返してくるのです。 これは、 バッファー外読み出し (buffer overread; 訳注 : バッファー読みすぎとも解釈できるか)と呼ばれ、 攻撃者が、配列の後に格納された読めるべきでないデータを読み出せるように添え字を操作できたら、 セキュリティ脆弱性につながる可能性があります。 この種の脆弱性からプログラムを保護するために、存在しない添え字の要素を読もうとしたら、 Rustは実行を中止し、継続を拒みます。試して確認してみましょう: $ cargo run Compiling panic v0.1.0 (file:///projects/panic) Finished dev [unoptimized + debuginfo] target(s) in 0.27 secs Running `target/debug/panic`\nthread 'main' panicked at 'index out of bounds: the len is 3 but the index is\n99', /checkout/src/liballoc/vec.rs:1555:10\n('main'スレッドは、/checkout/src/liballoc/vec.rs:1555:10の\n「境界外番号: 長さは3なのに、添え字は99です」でパニックしました)\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. このエラーは、自分のファイルではない vec.rs ファイルを指しています。 標準ライブラリの Vec の実装です。ベクタ v に対して [] を使った時に走るコードは、 vec.rs に存在し、ここで実際に panic! が発生しているのです。 その次の注釈行は、 RUST_BACKTRACE 環境変数をセットして、まさしく何が起き、 エラーが発生したのかのバックトレースを得られることを教えてくれています。 バックトレース とは、ここに至るまでに呼び出された全関数の一覧です。Rustのバックトレースも、 他の言語同様に動作します: バックトレースを読むコツは、頭からスタートして自分のファイルを見つけるまで読むことです。 そこが、問題の根源になるのです。自分のファイルを言及している箇所以前は、自分のコードで呼び出したコードになります; 以後は、自分のコードを呼び出しているコードになります。これらの行には、Rustの核となるコード、標準ライブラリのコード、 使用しているクレートなどが含まれるかもしれません。 RUST_BACKTRACE 環境変数を0以外の値にセットして、 バックトレースを出力してみましょう。リスト9-2のような出力が得られるでしょう。 $ RUST_BACKTRACE=1 cargo run Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/panic`\nthread 'main' panicked at 'index out of bounds: the len is 3 but the index is 99', /checkout/src/liballoc/vec.rs:1555:10\nstack backtrace: 0: std::sys::imp::backtrace::tracing::imp::unwind_backtrace at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49 1: std::sys_common::backtrace::_print at /checkout/src/libstd/sys_common/backtrace.rs:71 2: std::panicking::default_hook::{{closure}} at /checkout/src/libstd/sys_common/backtrace.rs:60 at /checkout/src/libstd/panicking.rs:381 3: std::panicking::default_hook at /checkout/src/libstd/panicking.rs:397 4: std::panicking::rust_panic_with_hook at /checkout/src/libstd/panicking.rs:611 5: std::panicking::begin_panic at /checkout/src/libstd/panicking.rs:572 6: std::panicking::begin_panic_fmt at /checkout/src/libstd/panicking.rs:522 7: rust_begin_unwind at /checkout/src/libstd/panicking.rs:498 8: core::panicking::panic_fmt at /checkout/src/libcore/panicking.rs:71 9: core::panicking::panic_bounds_check at /checkout/src/libcore/panicking.rs:58 10: as core::ops::index::Index>::index at /checkout/src/liballoc/vec.rs:1555 11: panic::main at src/main.rs:4 12: __rust_maybe_catch_panic at /checkout/src/libpanic_unwind/lib.rs:99 13: std::rt::lang_start at /checkout/src/libstd/panicking.rs:459 at /checkout/src/libstd/panic.rs:361 at /checkout/src/libstd/rt.rs:61 14: main 15: __libc_start_main 16: リスト9-2: RUST_BACKTRACE 環境変数をセットした時に表示される、 panic! 呼び出しが生成するバックトレース 出力が多いですね!OSやRustのバージョンによって、出力の詳細は変わる可能性があります。この情報とともに、 バックトレースを得るには、デバッグシンボルを有効にしなければなりません。デバッグシンボルは、 --release オプションなしで cargo build や cargo run を使用していれば、標準で有効になり、 ここではそうなっています。 リスト9-2の出力で、バックトレースの11行目が問題発生箇所を指し示しています: src/main.rs の4行目です。 プログラムにパニックしてほしくなければ、自分のファイルについて言及している最初の行で示されている箇所が、 どのようにパニックを引き起こす値でこの箇所にたどり着いたか割り出すために調査を開始すべき箇所になります。 バックトレースの使用法を模擬するためにわざとパニックするコードを書いたリスト9-1において、 パニックを解消する方法は、3つしか要素のないベクタの添え字99の要素を要求しないことです。 将来コードがパニックしたら、パニックを引き起こすどんな値でコードがどんな動作をしているのかと、 代わりにコードは何をすべきなのかを算出する必要があるでしょう。 この章の後ほど、「 panic! するか panic! するまいか」節で panic! とエラー状態を扱うのに panic! を使うべき時と使わぬべき時に戻ってきます。 次は、 Result を使用してエラーから回復する方法を見ましょう。","breadcrumbs":"エラー処理 » panic! バックトレースを使用する","id":"ch09-01-unrecoverable-errors-with-panic.html#panic-バックトレースを使用する","title":"panic! バックトレースを使用する"},"ch09-02-recoverable-errors-with-result.html#aエラーを委譲する":{"body":"失敗する可能性のある何かを呼び出す実装をした関数を書く際、関数内でエラーを処理する代わりに、 呼び出し元がどうするかを決められるようにエラーを返すことができます。これはエラーの 委譲 として認知され、 自分のコードの文脈で利用可能なものよりも、 エラーの処理法を規定する情報やロジックがより多くある呼び出し元のコードに制御を明け渡します。 例えば、リスト9-6の関数は、ファイルからユーザ名を読み取ります。ファイルが存在しなかったり、読み込みできなければ、 この関数はそのようなエラーを呼び出し元のコードに返します。 ファイル名: src/main.rs use std::io;\nuse std::io::Read;\nuse std::fs::File; fn read_username_from_file() -> Result { let f = File::open(\"hello.txt\"); let mut f = match f { Ok(file) => file, Err(e) => return Err(e), }; let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), }\n} リスト9-6: match でエラーを呼び出し元のコードに返す関数 まずは、関数の戻り値型に注目してください: Result です。つまり、この関数は、 Result 型の値を返しているということです。ここでジェネリック引数の T は、具体型 String で埋められ、 ジェネリック引数の E は具体型 io::Error で埋められています。この関数が何の問題もなく成功すれば、 この関数を呼び出したコードは、 String (関数がファイルから読み取ったユーザ名)を保持する Ok 値を受け取ります。 この関数が何か問題に行き当たったら、呼び出し元のコードは io::Error のインスタンスを保持する Err 値を受け取り、 この io::Error は問題の内容に関する情報をより多く含んでいます。関数の戻り値の型に io::Error を選んだのは、 この関数本体で呼び出している失敗する可能性のある処理が両方とも偶然この型をエラー値として返すからです: File::open 関数と read_to_string メソッドです。 関数の本体は、 File::open 関数を呼び出すところから始まります。そして、リスト9-4の match に似た match で返ってくる Result 値を扱い、 Err ケースに panic! を呼び出すだけの代わりに、この関数から早期リターンしてこの関数のエラー値として、 File::open から得たエラー値を呼び出し元に渡し戻します。 File::open が成功すれば、 ファイルハンドルを変数 f に保管して継続します。 さらに、変数 s に新規 String を生成し、 f のファイルハンドルに対して read_to_string を呼び出して、 ファイルの中身を s に読み出します。 File::open が成功しても、失敗する可能性があるので、 read_to_string メソッドも、 Result を返却します。その Result を処理するために別の match が必要になります: read_to_string が成功したら、 関数は成功し、今は Ok に包まれた s に入っているファイルのユーザ名を返却します。 read_to_string が失敗したら、 File::open の戻り値を扱った match でエラー値を返したように、エラー値を返します。 しかし、明示的に return を述べる必要はありません。これが関数の最後の式だからです。 そうしたら、呼び出し元のコードは、ユーザ名を含む Ok 値か、 io::Error を含む Err 値を得て扱います。 呼び出し元のコードがそれらの値をどうするかはわかりません。呼び出しコードが Err 値を得たら、 例えば、 panic! を呼び出してプログラムをクラッシュさせたり、デフォルトのユーザ名を使ったり、 ファイル以外の場所からユーザ名を検索したりできるでしょう。呼び出し元のコードが実際に何をしようとするかについて、 十分な情報がないので、成功や失敗情報を全て委譲して適切に扱えるようにするのです。 Rustにおいて、この種のエラー委譲は非常に一般的なので、Rustにはこれをしやすくする ? 演算子が用意されています。 エラー委譲のショートカット: ? 演算子 リスト9-7もリスト9-6と同じ機能を有する read_username_from_file の実装ですが、 こちらは ? 演算子を使用しています: ファイル名: src/main.rs use std::io;\nuse std::io::Read;\nuse std::fs::File; fn read_username_from_file() -> Result { let mut f = File::open(\"hello.txt\")?; let mut s = String::new(); f.read_to_string(&mut s)?; Ok(s)\n} リスト9-7: ? 演算子でエラーを呼び出し元に返す関数 Result 値の直後に置かれた ? は、リスト9-6で Result 値を処理するために定義した match 式とほぼ同じように動作します。 Result の値が Ok なら、 Ok の中身がこの式から返ってきて、プログラムは継続します。値が Err なら、 return キーワードを使ったかのように関数全体から Err の中身が返ってくるので、 エラー値は呼び出し元のコードに委譲されます。 リスト9-6の match 式と ? 演算子には違いがあります: ? を使ったエラー値は、 標準ライブラリの From トレイトで定義され、エラーの型を別のものに変換する from 関数を通ることです。 ? 演算子が from 関数を呼び出すと、受け取ったエラー型が現在の関数の戻り値型で定義されているエラー型に変換されます。これは、 個々がいろんな理由で失敗する可能性があるのにも関わらず、関数が失敗する可能性を全て一つのエラー型で表現して返す時に有用です。 各エラー型が from 関数を実装して返り値のエラー型への変換を定義している限り、 ? 演算子が変換の面倒を自動的に見てくれます。 リスト9-7の文脈では、 File::open 呼び出し末尾の ? は Ok の中身を変数 f に返します。 エラーが発生したら、 ? 演算子により関数全体から早期リターンし、あらゆる Err 値を呼び出し元に与えます。 同じ法則が read_to_string 呼び出し末尾の ? にも適用されます。 ? 演算子により定型コードの多くが排除され、この関数の実装を単純にしてくれます。 リスト9-8で示したように、 ? の直後のメソッド呼び出しを連結することでさらにこのコードを短くすることさえもできます。 ファイル名: src/main.rs use std::io;\nuse std::io::Read;\nuse std::fs::File; fn read_username_from_file() -> Result { let mut s = String::new(); File::open(\"hello.txt\")?.read_to_string(&mut s)?; Ok(s)\n} リスト9-8: ? 演算子の後のメソッド呼び出しを連結する s の新規 String の生成を関数の冒頭に移動しました; その部分は変化していません。変数 f を生成する代わりに、 read_to_string の呼び出しを直接 File::open(\"hello.txt\")? の結果に連結させました。 それでも、 read_to_string 呼び出しの末尾には ? があり、 File::open と read_to_string 両方が成功したら、 エラーを返すというよりもそれでも、 s にユーザ名を含む Ok 値を返します。機能もまたリスト9-6及び、9-7と同じです; ただ単に異なるバージョンのよりエルゴノミックな書き方なのです。 ? 演算子は、 Result を返す関数でしか使用できない ? 演算子は戻り値に Result を持つ関数でしか使用できません。というのも、リスト9-6で定義した match 式と同様に動作するよう、 定義されているからです。 Result の戻り値型を要求する match の部品は、 return Err(e) なので、 関数の戻り値はこの return と互換性を保つために Result でなければならないのです。 main 関数で ? 演算子を使用したらどうなるか見てみましょう。 main 関数は、戻り値が () でしたね: use std::fs::File; fn main() { let f = File::open(\"hello.txt\")?;\n} このコードをコンパイルすると、以下のようなエラーメッセージが得られます: error[E0277]: the trait bound `(): std::ops::Try` is not satisfied\n(エラー: `(): std::ops::Try`というトレイト境界が満たされていません) --> src/main.rs:4:13 |\n4 | let f = File::open(\"hello.txt\")?; | ------------------------ | | | the `?` operator can only be used in a function that returns `Result` (or another type that implements `std::ops::Try`) | in this macro invocation | (このマクロ呼び出しの`Result`(かまたは`std::ops::Try`を実装する他の型)を返す関数でしか`?`演算子は使用できません) | = help: the trait `std::ops::Try` is not implemented for `()` (助言: `std::ops::Try`トレイトは`()`には実装されていません) = note: required by `std::ops::Try::from_error` (注釈: `std::ops::Try::from_error`で要求されています) このエラーは、 ? 演算子は Result を返す関数でしか使用が許可されないと指摘しています。 Result を返さない関数では、 Result を返す別の関数を呼び出した時、 ? 演算子を使用してエラーを呼び出し元に委譲する可能性を生み出す代わりに、 match か Result のメソッドのどれかを使う必要があるでしょう。 さて、 panic! 呼び出しや Result を返す詳細について議論し終えたので、 どんな場合にどちらを使うのが適切か決める方法についての話に戻りましょう。","breadcrumbs":"エラー処理 » エラーを委譲する","id":"ch09-02-recoverable-errors-with-result.html#aエラーを委譲する","title":"エラーを委譲する"},"ch09-02-recoverable-errors-with-result.html#aエラー時にパニックするショートカット--unwrap-と-expect":{"body":"match の使用は、十分に仕事をしてくれますが、いささか冗長になり得る上、必ずしも意図をよく伝えるとは限りません。 Result 型には、色々な作業をするヘルパーメソッドが多く定義されています。それらの関数の一つは、 unwrap と呼ばれますが、リスト9-4で書いた match 式と同じように実装された短絡メソッドです。 Result 値が Ok 列挙子なら、 unwrap は Ok の中身を返します。 Result が Err 列挙子なら、 unwrap は panic! マクロを呼んでくれます。こちらが実際に動作している unwrap の例です: ファイル名: src/main.rs use std::fs::File; fn main() { let f = File::open(\"hello.txt\").unwrap();\n} このコードを hello.txt ファイルなしで走らせたら、 unwrap メソッドが行う panic! 呼び出しからのエラーメッセージを目の当たりにするでしょう: thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error {\nrepr: Os { code: 2, message: \"No such file or directory\" } }',\nsrc/libcore/result.rs:906:4\n('main'スレッドは、src/libcore/result.rs:906:4の\n「`Err`値に対して`Result::unwrap()`が呼び出されました: Error{\nrepr: Os { code: 2, message: \"そのようなファイルまたはディレクトリはありません\" } }」でパニックしました) 別のメソッド expect は、 unwrap に似ていますが、 panic! のエラーメッセージも選択させてくれます。 unwrap の代わりに expect を使用して、いいエラーメッセージを提供すると、意図を伝え、 パニックの原因をたどりやすくしてくれます。 expect の表記はこんな感じです: ファイル名: src/main.rs use std::fs::File; fn main() { // hello.txtを開くのに失敗しました let f = File::open(\"hello.txt\").expect(\"Failed to open hello.txt\");\n} expect を unwrap と同じように使用してます: ファイルハンドルを返したり、 panic! マクロを呼び出しています。 expect が panic! 呼び出しで使用するエラーメッセージは、 unwrap が使用するデフォルトの panic! メッセージではなく、 expect に渡した引数になります。以下のようになります: thread 'main' panicked at 'Failed to open hello.txt: Error { repr: Os { code:\n2, message: \"No such file or directory\" } }', src/libcore/result.rs:906:4 このエラーメッセージは、指定したテキストの hello.txtを開くのに失敗しました で始まっているので、 コード内のどこでエラーメッセージが出力されたのかより見つけやすくなるでしょう。複数箇所で unwrap を使用していたら、 ズバリどの unwrap がパニックを引き起こしているのか理解するのは、より時間がかかる可能性があります。 パニックする unwrap 呼び出しは全て、同じメッセージを出力するからです。","breadcrumbs":"エラー処理 » エラー時にパニックするショートカット: unwrap と expect","id":"ch09-02-recoverable-errors-with-result.html#aエラー時にパニックするショートカット--unwrap-と-expect","title":"エラー時にパニックするショートカット: unwrap と expect"},"ch09-02-recoverable-errors-with-result.html#a色々なエラーにマッチする":{"body":"リスト9-4のコードは、 File::open が失敗した理由にかかわらず panic! します。代わりにしたいことは、 失敗理由によって動作を変えることです: ファイルが存在しないために File::open が失敗したら、 ファイルを作成し、その新しいファイルへのハンドルを返したいです。他の理由(例えばファイルを開く権限がなかったなど)で、 File::open が失敗したら、リスト9-4のようにコードには panic! してほしいのです。 リスト9-5を眺めてください。ここでは match に別のアームを追加しています。 ファイル名: src/main.rs use std::fs::File;\nuse std::io::ErrorKind; fn main() { let f = File::open(\"hello.txt\"); let f = match f { Ok(file) => file, Err(ref error) if error.kind() == ErrorKind::NotFound => { match File::create(\"hello.txt\") { Ok(fc) => fc, Err(e) => { panic!( //ファイルを作成しようとしましたが、問題がありました \"Tried to create file but there was a problem: {:?}\", e ) }, } }, Err(error) => { panic!( \"There was a problem opening the file: {:?}\", error ) }, };\n} リスト9-5: 色々な種類のエラーを異なる方法で扱う File::open が Err 列挙子に含めて返す値の型は、 io::Error であり、これは標準ライブラリで提供されている構造体です。 この構造体には、呼び出すと io::ErrorKind 値が得られる kind メソッドがあります。 io::ErrorKind というenumは、 標準ライブラリで提供されていて、 io 処理の結果発生する可能性のある色々な種類のエラーを表す列挙子があります。 使用したい列挙子は、 ErrorKind::NotFound で、これは開こうとしているファイルがまだ存在しないことを示唆します。 if error.kind() == ErrorKind::Notfound という条件式は、 マッチガード と呼ばれます: アームのパターンをさらに洗練する match アーム上のおまけの条件式です。この条件式は、 そのアームのコードが実行されるには真でなければいけないのです; そうでなければ、 パターンマッチングは継続し、 match の次のアームを考慮します。パターンの ref は、 error がガード条件式にムーブされないように必要ですが、ただ単にガード式に参照されます。 ref を使用して & の代わりにパターン内で参照を作っている理由は、第18章で詳しく講義します。 手短に言えば、パターンの文脈において、 & は参照にマッチし、その値を返しますが、 ref は値にマッチし、それへの参照を返すということなのです。 マッチガードで精査したい条件は、 error.kind() により返る値が、 ErrorKind enumの NotFound 列挙子であるかということです。 もしそうなら、 File::create でファイル作成を試みます。ところが、 File::create も失敗する可能性があるので、 内部にも match 式を追加する必要があるのです。ファイルが開けないなら、異なるエラーメッセージが出力されるでしょう。 外側の match の最後のアームは同じままなので、ファイルが存在しないエラー以外ならプログラムはパニックします。","breadcrumbs":"エラー処理 » 色々なエラーにマッチする","id":"ch09-02-recoverable-errors-with-result.html#a色々なエラーにマッチする","title":"色々なエラーにマッチする"},"ch09-02-recoverable-errors-with-result.html#result-で回復可能なエラー":{"body":"多くのエラーは、プログラムを完全にストップさせるほど深刻ではありません。時々、関数が失敗した時に、 容易に解釈し、対応できる理由によることがあります。例えば、ファイルを開こうとして、 ファイルが存在しないために処理が失敗したら、プロセスを停止するのではなく、ファイルを作成したいことがあります。 第2章の 「 Result 型で失敗する可能性に対処する」 で Result enumが以下のように、 Ok と Err の2列挙子からなるよう定義されていることを思い出してください: enum Result { Ok(T), Err(E),\n} T と E は、ジェネリックな型引数です: ジェネリクスについて詳しくは、第10章で議論します。 たった今知っておく必要があることは、 T が成功した時に Ok 列挙子に含まれて返される値の型を表すことと、 E が失敗した時に Err 列挙子に含まれて返されるエラーの型を表すことです。 Result はこのようなジェネリックな型引数を含むので、 標準ライブラリ上に定義されている Result 型や関数などを、成功した時とエラーの時に返したい値が異なるような様々な場面で使用できるのです。 関数が失敗する可能性があるために Result 値を返す関数を呼び出しましょう: リスト9-3では、 ファイルを開こうとしています。 ファイル名: src/main.rs use std::fs::File; fn main() { let f = File::open(\"hello.txt\");\n} リスト9-3: ファイルを開く File::open が Result を返すとどう知るのでしょうか?標準ライブラリのAPIドキュメントを参照することもできますし、 コンパイラに尋ねることもできます! f に関数の戻り値では ない と判明している型注釈を与えて、 コードのコンパイルを試みれば、コンパイラは型が合わないと教えてくれるでしょう。そして、エラーメッセージは、 f の 実際の 型を教えてくれるでしょう。試してみましょう! File::open の戻り値の型は u32 ではないと判明しているので、 let f 文を以下のように変更しましょう: let f: u32 = File::open(\"hello.txt\"); これでコンパイルしようとすると、以下のような出力が得られます: error[E0308]: mismatched types\n(エラー: 型が合いません) --> src/main.rs:4:18 |\n4 | let f: u32 = File::open(\"hello.txt\"); | ^^^^^^^^^^^^^^^^^^^^^^^ expected u32, found enum\n`std::result::Result` | = note: expected type `u32` (注釈: 予期した型は`u32`です) found type `std::result::Result` (実際の型は`std::result::Result`です) これにより、 File::open 関数の戻り値の型は、 Result であることがわかります。ジェネリック引数の T は、 ここでは成功値の型 std::fs::File で埋められていて、これはファイルハンドルです。 エラー値で使用されている E の型は、 std::io::Error です。 この戻り値型は、 File::open の呼び出しが成功し、読み込みと書き込みを行えるファイルハンドルを返す可能性があることを意味します。 また、関数呼び出しは失敗もする可能性があります: 例えば、ファイルが存在しない可能性、ファイルへのアクセス権限がない可能性です。 File::open には成功したか失敗したかを知らせる方法とファイルハンドルまたは、エラー情報を与える方法が必要なのです。 この情報こそが Result enumが伝達するものなのです。 File::open が成功した場合、変数 f の値はファイルハンドルを含む Ok インスタンスになります。 失敗した場合には、発生したエラーの種類に関する情報をより多く含む Err インスタンスが f の値になります。 リスト9-3のコードに追記をして File::open が返す値に応じて異なる動作をする必要があります。 リスト9-4に基礎的な道具を使って Result を扱う方法を一つ示しています。第6章で議論した match 式です。 ファイル名: src/main.rs use std::fs::File; fn main() { let f = File::open(\"hello.txt\"); let f = match f { Ok(file) => file, Err(error) => { // ファイルを開く際に問題がありました panic!(\"There was a problem opening the file: {:?}\", error) }, };\n} リスト9-4: match 式を使用して返却される可能性のある Result 列挙子を処理する Option enumのように、 Result enumとその列挙子は、初期化処理でインポートされているので、 match アーム内で Ok と Err 列挙子の前に Result:: を指定する必要がないことに注目してください。 ここでは、結果が Ok の時に、 Ok 列挙子から中身の file 値を返すように指示し、 それからそのファイルハンドル値を変数 f に代入しています。 match の後には、 ファイルハンドルを使用して読み込んだり書き込むことができるわけです。 match のもう一つのアームは、 File::open から Err 値が得られたケースを処理しています。 この例では、 panic! マクロを呼び出すことを選択しています。カレントディレクトリに hello.txt というファイルがなく、 このコードを走らせたら、 panic! マクロからの以下のような出力を目の当たりにするでしょう: thread 'main' panicked at 'There was a problem opening the file: Error { repr:\nOs { code: 2, message: \"No such file or directory\" } }', src/main.rs:9:12\n('main'スレッドは、src/main.rs:9:12の「ファイルを開く際に問題がありました: Error{ repr:\nOs { code: 2, message: \"そのような名前のファイルまたはディレクトリはありません\"}}」でパニックしました) 通常通り、この出力は、一体何がおかしくなったのかを物語っています。","breadcrumbs":"エラー処理 » Result で回復可能なエラー","id":"ch09-02-recoverable-errors-with-result.html#result-で回復可能なエラー","title":"Result で回復可能なエラー"},"ch09-03-to-panic-or-not-to-panic.html#aまとめ":{"body":"Rustのエラー処理機能は、プログラマがより頑健なコードを書く手助けをするように設計されています。 panic! マクロは、プログラムが処理できない状態にあり、無効だったり不正な値で処理を継続するのではなく、 プロセスに処理を中止するよう指示することを通知します。 Result enumは、Rustの型システムを使用して、 コードが回復可能な方法で処理が失敗するかもしれないことを示唆します。 Result を使用して、 呼び出し側のコードに成功や失敗する可能性を処理する必要があることも教えます。 適切な場面で panic! や Result を使用することで、必然的な問題の眼前でコードの信頼性を上げてくれます。 今や、標準ライブラリが Option や Result enumなどでジェネリクスを有効活用するところを目の当たりにしたので、 ジェネリクスの動作法と自分のコードでの使用方法について語りましょう。","breadcrumbs":"エラー処理 » まとめ","id":"ch09-03-to-panic-or-not-to-panic.html#aまとめ","title":"まとめ"},"ch09-03-to-panic-or-not-to-panic.html#aエラー処理のガイドライン":{"body":"コードが悪い状態に陥る可能性があるときにパニックさせるのは、推奨されることです。この文脈において、 悪い状態 とは、何らかの前提、保証、契約、不変性が破られたことを言い、例を挙げれば、無効な値、 矛盾する値、行方不明な値がコードに渡されることと、さらに以下のいずれか一つ以上の状態であります: 悪い状態がときに起こるとは 予想 されないとき。 この時点以降、この悪い状態にないことを頼りにコードが書かれているとき。 使用している型にこの情報をコード化するいい手段がないとき。 誰かが自分のコードを呼び出して筋の通らない値を渡してきたら、最善の選択肢は panic! し、 開発段階で修正できるように自分たちのコードにバグがあることをライブラリ使用者に通知することかもしれません。 同様に自分の制御下にない外部コードを呼び出し、修正しようのない無効な状態を返すときに panic! はしばしば適切です。 しかし、どんなにコードをうまく書いても起こると予想されますが、悪い状態に達したとき、それでも panic! 呼び出しをするよりも、 Result を返すほうがより適切です。例には、不正なデータを渡されたパーサとか、 訪問制限に引っかかったことを示唆するステータスを返すHTTPリクエストなどが挙げられます。 このような場合には、呼び出し側が問題の処理方法を決定できるように Result を返してこの悪い状態を委譲して、 失敗が予想される可能性であることを示唆するべきです。 panic! を呼び出すことは、 これらのケースでは最善策ではないでしょう。 コードが値に対して処理を行う場合、コードはまず値が合法であることを確認し、 値が合法でなければパニックするべきです。これはほぼ安全性上の理由によるものです: 不正なデータの処理を試みると、 コードを脆弱性に晒す可能性があります。これが、境界外へのメモリアクセスを試みたときに標準ライブラリが panic! を呼び出す主な理由です: 現在のデータ構造に属しないメモリにアクセスを試みることは、ありふれたセキュリティ問題なのです。 関数にはしばしば 契約 が伴います: 入力が特定の条件を満たすときのみ、振る舞いが保証されるのです。 契約が侵されたときにパニックすることは、道理が通っています。なぜなら、契約侵害は常に呼び出し側のバグを示唆し、 呼び出し側に明示的に処理してもらう必要のある種類のエラーではないからです。実際に、 呼び出し側が回復する合理的な手段はありません; 呼び出し側の プログラマ がコードを修正する必要があるのです。 関数の契約は、特に侵害がパニックを引き起こす際には、関数のAPIドキュメント内で説明されているべきです。 ですが、全ての関数でたくさんのエラーチェックを行うことは冗長で煩わしいことでしょう。幸運にも、 Rustの型システム(故にコンパイラが行う型精査)を使用して多くの検査を行ってもらうことができます。 関数の引数に特定の型があるなら、合法な値があるとコンパイラがすでに確認していることを把握して、 コードのロジックに進むことができます。例えば、 Option 以外の型がある場合、プログラムは、 何もない ではなく 何かある と想定します。そうしたらコードは、 Some と None 列挙子の2つの場合を処理する必要がなくなるわけです: 確実に値があるという可能性しかありません。関数に何もないことを渡そうとしてくるコードは、 コンパイルが通りもしませんので、その場合を実行時に検査する必要はないわけです。 別の例は、 u32 のような符号なし整数を使うことであり、この場合、引数は負には絶対にならないことが確認されます。","breadcrumbs":"エラー処理 » エラー処理のガイドライン","id":"ch09-03-to-panic-or-not-to-panic.html#aエラー処理のガイドライン","title":"エラー処理のガイドライン"},"ch09-03-to-panic-or-not-to-panic.html#aコンパイラよりもプログラマがより情報を持っている場合":{"body":"Result が Ok 値であると確認する何らかの別のロジックがある場合、 unwrap を呼び出すことは適切でしょうが、 コンパイラは、そのロジックを理解はしません。それでも、処理する必要のある Result は存在するでしょう: 呼び出している処理が何であれ、自分の特定の場面では論理的に起こり得なくても、一般的にまだ失敗する可能性はあるわけです。 手動でコードを調査して Err 列挙子は存在しないと確認できたら、 unwrap を呼び出すことは完全に受容できることです。 こちらが例です: use std::net::IpAddr; let home: IpAddr = \"127.0.0.1\".parse().unwrap(); ハードコードされた文字列を構文解析することで IpAddr インスタンスを生成しています。 プログラマには 127.0.0.1 が合法なIPアドレスであることがわかるので、ここで unwrap を使用することは、 受容可能なことです。しかしながら、ハードコードされた合法な文字列が存在することは、 parse メソッドの戻り値型を変えることにはなりません: それでも得られるのは、 Result 値であり、 コンパイラはまだ Err 列挙子になる可能性があるかのように Result を処理することを強制してきます。 コンパイラは、この文字列が常に合法なIPアドレスであると把握できるほど利口ではないからです。 プログラムにハードコードされるのではなく、IPアドレス文字列がユーザ起源でそれ故に 確かに 失敗する可能性がある場合、 Result をもっと頑健な方法で処理したほうが絶対にいいでしょう。","breadcrumbs":"エラー処理 » コンパイラよりもプログラマがより情報を持っている場合","id":"ch09-03-to-panic-or-not-to-panic.html#aコンパイラよりもプログラマがより情報を持っている場合","title":"コンパイラよりもプログラマがより情報を持っている場合"},"ch09-03-to-panic-or-not-to-panic.html#a例プロトタイプコードテスト":{"body":"例を記述して何らかの概念を具体化している時、頑健なエラー処理コードも例に含むことは、例の明瞭さを欠くことになりかねません。 例において、 unwrap などのパニックする可能性のあるメソッド呼び出しは、 アプリケーションにエラーを処理してほしい方法へのプレースホルダーを意味していると理解され、 これは残りのコードがしていることによって異なる可能性があります。 同様に、 unwrap や expect メソッドは、エラーの処理法を決定する準備ができる前、プロトタイプの段階では、 非常に便利です。それらにより、コードにプログラムをより頑健にする時の明らかなマーカーが残されるわけです。 メソッド呼び出しがテスト内で失敗したら、そのメソッドがテスト下に置かれた機能ではなかったとしても、 テスト全体が失敗してほしいでしょう。 panic! が、テストが失敗と印づけられる手段なので、 unwrap や expect の呼び出しはスバリ起こるべきことです。","breadcrumbs":"エラー処理 » 例、プロトタイプコード、テスト","id":"ch09-03-to-panic-or-not-to-panic.html#a例プロトタイプコードテスト","title":"例、プロトタイプコード、テスト"},"ch09-03-to-panic-or-not-to-panic.html#a検証のために独自の型を作る":{"body":"Rustの型システムを使用して合法な値があると確認するというアイディアを一歩先に進め、 検証のために独自の型を作ることに目を向けましょう。第2章の数当てゲームで、 コードがユーザに1から100までの数字を推測するよう求めたことを思い出してください。 秘密の数字と照合する前にユーザの推測がそれらの値の範囲にあることを全く確認しませんでした; 推測が正であることしか確認しませんでした。この場合、結果はそれほど悲惨なものではありませんでした: 「大きすぎ」、「小さすぎ」という出力は、それでも正しかったでしょう。ユーザを合法な推測に導き、 ユーザが範囲外の数字を推測したり、例えばユーザが文字を代わりに入力したりしたときに別の挙動をするようにしたら、 有益な改善になるでしょう。 これをする一つの方法は、ただの u32 の代わりに i32 として推測をパースし、負の数になる可能性を許可し、 それから数字が範囲に収まっているというチェックを追加することでしょう。そう、以下のように: loop { // --snip-- let guess: i32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, }; if guess < 1 || guess > 100 { println!(\"The secret number will be between 1 and 100.\"); continue; } match guess.cmp(&secret_number) { // --snip--\n} この if 式が、値が範囲外かどうかをチェックし、ユーザに問題を告知し、 continue を呼び出してループの次の繰り返しを始め、 別の推測を求めます。 if 式の後、 guess は1から100の範囲にあると把握して、 guess と秘密の数字の比較に進むことができます。 ところが、これは理想的な解決策ではありません: プログラムが1から100の範囲の値しか処理しないことが間違いなく、 肝要であり、この要求がある関数の数が多ければ、このようなチェックを全関数で行うことは、 面倒でパフォーマンスにも影響を及ぼす可能性があるでしょう。 代わりに、新しい型を作って検証を関数内に閉じ込め、検証を全箇所で繰り返すのではなく、 その型のインスタンスを生成することができます。そうすれば、関数がその新しい型をシグニチャに用い、 受け取った値を自信を持って使用することは安全になります。リスト9-9に、 new 関数が1から100までの値を受け取った時のみ、 Guess のインスタンスを生成する Guess 型を定義する一つの方法を示しました。 pub struct Guess { value: u32,\n} impl Guess { pub fn new(value: u32) -> Guess { if value < 1 || value > 100 { // 予想の値は1から100の範囲でなければなりませんが、{}でした panic!(\"Guess value must be between 1 and 100, got {}.\", value); } Guess { value } } pub fn value(&self) -> u32 { self.value }\n} リスト9-9: 値が1から100の場合のみ処理を継続する Guess 型 まず、 u32 型の value をフィールドに持つ Guess という名前の構造体を定義しています。 ここに数値が保管されます。 それから Guess に Guess 値のインスタンスを生成する new という名前の関連関数を実装しています。 new 関数は、 u32 型の value という引数を取り、 Guess を返すように定義されています。 new 関数の本体のコードは、 value をふるいにかけ、1から100の範囲であることを確かめます。 value がふるいに引っかかったら、 panic! 呼び出しを行います。これにより、呼び出しコードを書いているプログラマに、 修正すべきバグがあると警告します。というのも、この範囲外の value で Guess を生成することは、 Guess::new が頼りにしている契約を侵害するからです。 Guess::new がパニックするかもしれない条件は、 公開されているAPIドキュメントで議論されるべきでしょう; あなたが作成するAPIドキュメントで panic! の可能性を示唆する、 ドキュメントの規約は、第14章で講義します。 value が確かにふるいを通ったら、 value フィールドが value 引数にセットされた新しい Guess を作成して返します。 次に、 self を借用し、他に引数はなく、 u32 を返す value というメソッドを実装します。 この類のメソッドは時に ゲッター と呼ばれます。目的がフィールドから何らかのデータを得て返すことだからです。 この公開メソッドは、 Guess 構造体の value フィールドが非公開なので、必要になります。 value フィールドが非公開なことは重要であり、そのために Guess 構造体を使用するコードは、 直接 value をセットすることが叶わないのです: モジュール外のコードは、 Guess::new 関数を使用して Guess のインスタンスを生成し なければならず 、 それにより、 Guess::new 関数の条件式でチェックされていない value が Guess に存在する手段はないことが保証されるわけです。 そうしたら、引数を一つ持つか、1から100の範囲の数値のみを返す関数は、シグニチャで u32 ではなく、 Guess を取るか返し、本体内で追加の確認を行う必要はなくなると宣言できるでしょう。","breadcrumbs":"エラー処理 » 検証のために独自の型を作る","id":"ch09-03-to-panic-or-not-to-panic.html#a検証のために独自の型を作る","title":"検証のために独自の型を作る"},"ch09-03-to-panic-or-not-to-panic.html#panic-すべきかするまいか":{"body":"では、 panic! すべき時と Result を返すべき時はどう決定すればいいのでしょうか?コードがパニックしたら、 回復する手段はありません。回復する可能性のある手段の有る無しに関わらず、どんなエラー場面でも panic! を呼ぶことはできますが、 そうすると、呼び出す側のコードの立場に立ってこの場面は回復不能だという決定を下すことになります。 Result 値を返す決定をすると、決断を下すのではなく、呼び出し側に選択肢を与えることになります。 呼び出し側は、場面に合わせて回復を試みることを決定したり、この場合の Err 値は回復不能と断定して、 panic! を呼び出し、回復可能だったエラーを回復不能に変換することもできます。故に、 Result を返却することは、 失敗する可能性のある関数を定義する際には、いい第一選択肢になります。 稀な場面では、 Result を返すよりもパニックするコードを書く方がより適切になることもあります。 例やプロトタイプコード、テストでパニックするのが適切な理由を探ってみましょう。 それからコンパイラではありえない失敗だと気づけなくとも、人間なら気づける場面を議論しましょう。 そして、ライブラリコードでパニックするか決定する方法についての一般的なガイドラインで結論づけましょう。","breadcrumbs":"エラー処理 » panic! すべきかするまいか","id":"ch09-03-to-panic-or-not-to-panic.html#panic-すべきかするまいか","title":"panic! すべきかするまいか"},"ch10-00-generics.html#aジェネリック型トレイトライフタイム":{"body":"全てのプログラミング言語には、概念の重複を効率的に扱う道具があります。Rustにおいて、そのような道具の一つが ジェネリクス です。 ジェネリクスは、具体型や他のプロパティの抽象的な代役です。コード記述の際、コンパイルやコード実行時に、 ジェネリクスの位置に何が入るかを知ることなく、ジェネリクスの振る舞いや他のジェネリクスとの関係を表現できるのです。 関数が未知の値の引数を取り、同じコードを複数の具体的な値に対して走らせるように、 i32 や String などの具体的な型の代わりに何かジェネリックな型の引数を取ることができます。 実際、第6章で Option 、第8章で Vec と HashMap 、第9章で Result を既に使用しました。 この章では、独自の型、関数、メソッドをジェネリクスとともに定義する方法を探究します! まず、関数を抽出して、コードの重複を減らす方法を確認しましょう。次に同じテクニックを活用して、 引数の型のみが異なる2つの関数からジェネリックな関数を生成します。また、 ジェネリックな型を構造体やenum定義で使用する方法も説明します。 それから、トレイトを使用して、ジェネリックな方法で振る舞いを定義する方法を学びます。 ジェネリックな型にトレイトを組み合わせることで、ジェネリックな型を、単にあらゆる型に対してではなく、特定の振る舞いのある型のみに制限できます。 最後に、ライフタイムを議論します。ライフタイムとは、コンパイラに参照がお互いにどう関係しているかの情報を与える一種のジェネリクスです。 ライフタイムのおかげでコンパイラに参照が有効であることを確認してもらうことを可能にしつつ、多くの場面で値を借用できます。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム","id":"ch10-00-generics.html#aジェネリック型トレイトライフタイム","title":"ジェネリック型、トレイト、ライフタイム"},"ch10-00-generics.html#a関数を抽出することで重複を取り除く":{"body":"ジェネリクスの記法に飛び込む前にまずは、関数を抽出することでジェネリックな型が関わらない重複を取り除く方法を見ましょう。 そして、このテクニックを適用してジェネリックな関数を抽出するのです!重複したコードを認識して関数に抽出できるのと同じように、 ジェネリクスを使用できる重複コードも認識し始めるでしょう。 リスト10-1に示したように、リスト内の最大値を求める短いプログラムを考えてください。 ファイル名: src/main.rs fn main() { let number_list = vec![34, 50, 25, 100, 65]; let mut largest = number_list[0]; for number in number_list { if number > largest { largest = number; } } // 最大値は{}です println!(\"The largest number is {}\", largest);\n# assert_eq!(largest, 100);\n} リスト10-1: 数字のリストから最大値を求めるコード このコードは、整数のリストを変数 number_list に格納し、リストの最初の数字を largest という変数に配置しています。 それからリストの数字全部を走査し、現在の数字が largest に格納された数値よりも大きければ、 その変数の値を置き換えます。ですが、現在の数値が今まで見た最大値よりも小さければ、 変数は変わらず、コードはリストの次の数値に移っていきます。リストの数値全てを吟味した後、 largest は最大値を保持しているはずで、今回は100になります。 2つの異なる数値のリストから最大値を発見するには、リスト10-1のコードを複製し、 プログラムの異なる2箇所で同じロジックを使用できます。リスト10-2のようにですね。 ファイル名: src/main.rs fn main() { let number_list = vec![34, 50, 25, 100, 65]; let mut largest = number_list[0]; for number in number_list { if number > largest { largest = number; } } println!(\"The largest number is {}\", largest); let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8]; let mut largest = number_list[0]; for number in number_list { if number > largest { largest = number; } } println!(\"The largest number is {}\", largest);\n} リスト10-2: 2つ の数値のリストから最大値を探すコード このコードは動くものの、コードを複製することは退屈ですし、間違いも起きやすいです。また、 コードを変更したい時に複数箇所、更新しなければなりません。 この重複を排除するには、引数で与えられた整数のどんなリストに対しても処理が行える関数を定義して抽象化できます。 この解決策によりコードがより明確になり、リストの最大値を探すという概念を抽象的に表現させてくれます。 リスト10-3では、最大値を探すコードを largest という関数に抽出しました。リスト10-1のコードは、 たった1つの特定のリストからだけ最大値を探せますが、それとは異なり、このプログラムは2つの異なるリストから最大値を探せます。 ファイル名: src/main.rs fn largest(list: &[i32]) -> i32 { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest\n} fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest(&number_list); println!(\"The largest number is {}\", result);\n# assert_eq!(result, 100); let number_list = vec![102, 34, 6000, 89, 54, 2, 43, 8]; let result = largest(&number_list); println!(\"The largest number is {}\", result);\n# assert_eq!(result, 6000);\n} リスト10-3: 2つのリストから最大値を探す抽象化されたコード largest 関数には list と呼ばれる引数があり、これは、関数に渡す可能性のある、あらゆる i32 値の具体的なスライスを示します。 結果的に、関数呼び出しの際、コードは渡した特定の値に対して走るのです。 まとめとして、こちらがリスト10-2のコードからリスト10-3に変更するのに要したステップです: 重複したコードを見分ける。 重複コードを関数本体に抽出し、コードの入力と戻り値を関数シグニチャで指定する。 重複したコードの2つの実体を代わりに関数を呼び出すように更新する。 次は、この同じ手順をジェネリクスでも踏んで異なる方法でコードの重複を減らします。 関数本体が特定の値ではなく抽象的な list に対して処理できたのと同様に、 ジェネリクスは抽象的な型に対して処理するコードを可能にしてくれます。 例えば、関数が2つあるとしましょう: 1つは i32 値のスライスから最大の要素を探し、1つは char 値のスライスから最大要素を探します。 この重複はどう排除するのでしょうか?答えを見つけましょう!","breadcrumbs":"関数を抽出することで重複を取り除く","id":"ch10-00-generics.html#a関数を抽出することで重複を取り除く","title":"関数を抽出することで重複を取り除く"},"ch10-01-syntax.html#aジェネリクスを使用したコードのパフォーマンス":{"body":"ジェネリックな型引数を使用すると、実行時にコストが発生するのかな、と思うかもしれません。 嬉しいことにRustでは、ジェネリクスを、具体的な型があるコードよりもジェネリックな型を使用したコードを実行するのが遅くならないように実装しています。 コンパイラはこれを、ジェネリクスを使用しているコードの単相化をコンパイル時に行うことで達成しています。 単相化 (monomorphization)は、コンパイル時に使用されている具体的な型を入れることで、 ジェネリックなコードを特定のコードに変換する過程のことです。 この過程において、コンパイラは、リスト10-5でジェネリックな関数を生成するために使用した手順と真逆のことをしています: コンパイラは、ジェネリックなコードが呼び出されている箇所全部を見て、 ジェネリックなコードが呼び出されている具体的な型のコードを生成するのです。 標準ライブラリの Option enumを使用する例でこれが動作する方法を見ましょう: let integer = Some(5);\nlet float = Some(5.0); コンパイラがこのコードをコンパイルすると、単相化を行います。その過程で、コンパイラは Option のインスタンスに使用された値を読み取り、 2種類の Option を識別します: 一方は i32 で、もう片方は f64 です。そのように、 コンパイラは、 Option のジェネリックな定義を Option_i32 と Option_f64 に展開し、 それにより、ジェネリックな定義を特定の定義と置き換えます。 単相化されたバージョンのコードは、以下のようになります。ジェネリックな Option が、 コンパイラが生成した特定の定義に置き換えられています: ファイル名: src/main.rs enum Option_i32 { Some(i32), None,\n} enum Option_f64 { Some(f64), None,\n} fn main() { let integer = Option_i32::Some(5); let float = Option_f64::Some(5.0);\n} Rustでは、ジェネリックなコードを各インスタンスで型を指定したコードにコンパイルするので、 ジェネリクスを使用することに対して実行時コストを払うことはありません。コードを実行すると、 それぞれの定義を手作業で複製した時のように振る舞います。単相化の過程により、 Rustのジェネリクスは実行時に究極的に効率的になるのです。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » ジェネリクスを使用したコードのパフォーマンス","id":"ch10-01-syntax.html#aジェネリクスを使用したコードのパフォーマンス","title":"ジェネリクスを使用したコードのパフォーマンス"},"ch10-01-syntax.html#aジェネリックなデータ型":{"body":"関数シグニチャや構造体などの要素の定義を生成するのにジェネリクスを使用することができ、 それはさらに他の多くの具体的なデータ型と使用することもできます。まずは、 ジェネリクスで関数、構造体、enum、メソッドを定義する方法を見ましょう。それから、 ジェネリクスがコードのパフォーマンスに与える影響を議論します。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » ジェネリックなデータ型","id":"ch10-01-syntax.html#aジェネリックなデータ型","title":"ジェネリックなデータ型"},"ch10-01-syntax.html#aメソッド定義では":{"body":"(第5章のように、)定義にジェネリックな型を使うメソッドを構造体やenumに実装することもできます。リスト10-9は、 リスト10-6で定義した Point 構造体に x というメソッドを実装したものを示しています。 ファイル名: src/main.rs struct Point { x: T, y: T,\n} impl Point { fn x(&self) -> &T { &self.x }\n} fn main() { let p = Point { x: 5, y: 10 }; println!(\"p.x = {}\", p.x());\n} リスト10-9: 型 T の x フィールドへの参照を返す x というメソッドを Point 構造体に実装する ここで、フィールド x のデータへの参照を返す x というメソッドを Point に定義しました。 impl の直後に T を宣言しなければならないことに注意してください。こうすることで、型 Point にメソッドを実装していることを指定するために、 T を使用することができます。 impl の後に T をジェネリックな型として宣言することで、コンパイラは、 Point の山カッコ内の型が、 具体的な型ではなくジェネリックな型であることを認識できるのです。 例えば、ジェネリックな型を持つ Point インスタンスではなく、 Point だけにメソッドを実装することもできるでしょう。 リスト10-10では、具体的な型 f32 を使用しています。つまり、 impl の後に型を宣言しません。 # struct Point {\n# x: T,\n# y: T,\n# }\n#\nimpl Point { fn distance_from_origin(&self) -> f32 { (self.x.powi(2) + self.y.powi(2)).sqrt() }\n} リスト10-10: ジェネリックな型引数 T に対して特定の具体的な型がある構造体にのみ適用される impl ブロック このコードは、 Point には distance_from_origin というメソッドが存在するが、 T が f32 ではない Point の他のインスタンスにはこのメソッドが定義されないことを意味します。 このメソッドは、この点が座標(0.0, 0.0)の点からどれだけ離れているかを測定し、 浮動小数点数にのみ利用可能な数学的処理を使用します。 構造体定義のジェネリックな型引数は、必ずしもその構造体のメソッドシグニチャで使用するものと同じにはなりません。 例を挙げれば、リスト10-11は、リスト10-8の Point にメソッド mixup を定義しています。 このメソッドは、他の Point を引数として取り、この引数は mixup を呼び出している self の Point とは異なる型の可能性があります。 このメソッドは、(型 T の) self の Point の x 値と渡した(型 W の) Point の y 値から新しい Point インスタンスを生成します。 ファイル名: src/main.rs struct Point { x: T, y: U,\n} impl Point { fn mixup(self, other: Point) -> Point { Point { x: self.x, y: other.y, } }\n} fn main() { let p1 = Point { x: 5, y: 10.4 }; let p2 = Point { x: \"Hello\", y: 'c'}; let p3 = p1.mixup(p2); println!(\"p3.x = {}, p3.y = {}\", p3.x, p3.y);\n} リスト10-11: 構造体定義とは異なるジェネリックな型を使用するメソッド main で、 x (値は 5 )に i32 、 y (値は 10.4 )に f64 を持つ Point を定義しました。 p2 変数は、 x (値は \"Hello\" )に文字列スライス、 y (値は c )に char を持つ Point 構造体です。 引数 p2 で p1 に mixup を呼び出すと、 p3 が得られ、 x は i32 になります。 x は p1 由来だからです。 p3 変数の y は、 char になります。 y は p2 由来だからです。 println! マクロの呼び出しは、 p3.x = 5, p3.y = c と出力するでしょう。 この例の目的は、一部のジェネリックな引数は impl で宣言され、他の一部はメソッド定義で宣言される場面をデモすることです。 ここで、ジェネリックな引数 T と U は impl の後に宣言されています。構造体定義にはまるからです。 ジェネリックな引数 V と W は fn mixup の後に宣言されています。何故なら、このメソッドにしか関係ないからです。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » メソッド定義では","id":"ch10-01-syntax.html#aメソッド定義では","title":"メソッド定義では"},"ch10-01-syntax.html#a構造体定義では":{"body":"構造体を定義して <> 記法で1つ以上のフィールドにジェネリックな型引数を使用することもできます。 リスト10-6は、 Point 構造体を定義してあらゆる型の x と y 座標を保持する方法を示しています。 ファイル名: src/main.rs struct Point { x: T, y: T,\n} fn main() { let integer = Point { x: 5, y: 10 }; let float = Point { x: 1.0, y: 4.0 };\n} リスト10-6: 型 T の x と y 値を保持する Point 構造体 構造体定義でジェネリクスを使用する記法は、関数定義のものと似ています。まず、山カッコ内に型引数の名前を構造体名の直後に宣言します。 そうすると、本来具体的なデータ型を記述する構造体定義の箇所に、ジェネリックな型を使用できます。 ジェネリックな型を1つだけ使用して Point を定義したので、この定義は、 Point 構造体がなんらかの型 T に関して、 ジェネリックであると述べていて、その型がなんであれ、 x と y のフィールドは 両方 その同じ型になっていることに注意してください。 リスト10-7のように、異なる型の値のある Point のインスタンスを生成すれば、コードはコンパイルできません。 ファイル名: src/main.rs struct Point { x: T, y: T,\n} fn main() { let wont_work = Point { x: 5, y: 4.0 };\n} リスト10-7: どちらも同じジェネリックなデータ型 T なので、 x と y というフィールドは同じ型でなければならない この例で、 x に整数値5を代入すると、この Point のインスタンスに対するジェネリックな型 T は整数になるとコンパイラに知らせます。 それから y に4.0を指定する時に、このフィールドは x と同じ型と定義したはずなので、このように型不一致エラーが出ます: error[E0308]: mismatched types --> src/main.rs:7:38 |\n7 | let wont_work = Point { x: 5, y: 4.0 }; | ^^^ expected integral variable, found\nfloating-point variable | = note: expected type `{integer}` found type `{float}` x と y が両方ジェネリックだけれども、異なる型になり得る Point 構造体を定義するには、 複数のジェネリックな型引数を使用できます。例えば、リスト10-8では、 Point の定義を変更して、 型 T と U に関してジェネリックにし、 x が型 T で、 y が型 U になります。 ファイル名: src/main.rs struct Point { x: T, y: U,\n} fn main() { let both_integer = Point { x: 5, y: 10 }; let both_float = Point { x: 1.0, y: 4.0 }; let integer_and_float = Point { x: 5, y: 4.0 };\n} リスト10-8: Point は2つの型に関してジェネリックなので、 x と y は異なる型の値になり得る これで、示された Point インスタンスは全部使用可能です!所望の数だけ定義でジェネリックな型引数を使用できますが、 数個以上使用すると、コードが読みづらくなります。コードで多くのジェネリックな型が必要な時は、 コードの小分けが必要なサインかもしれません。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » 構造体定義では","id":"ch10-01-syntax.html#a構造体定義では","title":"構造体定義では"},"ch10-01-syntax.html#a関数定義では":{"body":"ジェネリクスを使用する関数を定義する時、通常、引数や戻り値のデータ型を指定する関数のシグニチャにジェネリクスを配置します。 そうすることでコードがより柔軟になり、コードの重複を阻止しつつ、関数の呼び出し元により多くの機能を提供します。 largest 関数を続けます。リスト10-4はどちらもスライスから最大値を探す2つの関数を示しています。 ファイル名: src/main.rs fn largest_i32(list: &[i32]) -> i32 { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest\n} fn largest_char(list: &[char]) -> char { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest\n} fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest_i32(&number_list); println!(\"The largest number is {}\", result);\n# assert_eq!(result, 100); let char_list = vec!['y', 'm', 'a', 'q']; let result = largest_char(&char_list); println!(\"The largest char is {}\", result);\n# assert_eq!(result, 'y');\n} リスト10-4: 名前とシグニチャの型のみが異なる2つの関数 largest_i32 関数は、リスト10-3で抽出したスライスから最大の i32 を探す関数です。 largest_char 関数は、スライスから最大の char を探します。関数本体には同じコードがあるので、 単独の関数にジェネリックな型引数を導入してこの重複を排除しましょう。 これから定義する新しい関数の型を引数にするには、ちょうど関数の値引数のように型引数に名前をつける必要があります。 型引数の名前にはどんな識別子も使用できますが、 T を使用します。というのも、慣習では、 Rustの引数名は短く(しばしばたった1文字になります)、Rustの型の命名規則がキャメルケースだからです。 \"type\"の省略形なので、 T が多くのRustプログラマの既定の選択なのです。 関数の本体で引数を使用するとき、コンパイラがその名前の意味を把握できるようにシグニチャでその引数名を宣言しなければなりません。 同様に、型引数名を関数シグニチャで使用する際には、使用する前に型引数名を宣言しなければなりません。 ジェネリックな largest 関数を定義するために、型名宣言を山カッコ( <> )内、関数名と引数リストの間に配置してください。 こんな感じに: fn largest(list: &[T]) -> T { この定義は以下のように解読します: 関数 largest は、なんらかの型 T に関してジェネリックであると。 この関数には list という引数が1つあり、これは型 T の値のスライスです。 largest 関数は同じ T 型の値を返します。 リスト10-5は、シグニチャにジェネリックなデータ型を使用して largest 関数定義を組み合わせたものを示しています。 このリストはさらに、この関数を i32 値か char 値のどちらかで呼べる方法も表示しています。 このコードはまだコンパイルできないことに注意してください。ですが、この章の後ほど修正します。 ファイル名: src/main.rs fn largest(list: &[T]) -> T { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest\n} 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);\n} リスト10-5: ジェネリックな型引数を使用するものの、まだコンパイルできない largest 関数の定義 直ちにこのコードをコンパイルしたら、以下のようなエラーが出ます: error[E0369]: binary operation `>` cannot be applied to type `T`\n(エラー: 2項演算`>`は、型`T`に適用できません) --> src/main.rs:5:12 |\n5 | if item > largest { | ^^^^^^^^^^^^^^ | = note: an implementation of `std::cmp::PartialOrd` might be missing for `T` (注釈: `std::cmp::PartialOrd`の実装が`T`に対して存在しない可能性があります) 注釈が std::cmp::PartialOrd に触れています。これは、 トレイト です。トレイトについては、次の節で語ります。 とりあえず、このエラーは、 largest の本体は、 T がなりうる全ての可能性のある型に対して動作しないと述べています。 本体で型 T の値を比較したいので、値が順序付け可能な型のみしか使用できないのです。比較を可能にするために、 標準ライブラリには型に実装できる std::cmp::PartialOrd トレイトがあります(このトレイトについて詳しくは付録Cを参照されたし)。 ジェネリックな型が特定のトレイトを持つと指定する方法は「トレイト境界」節で習うでしょうが、 先にジェネリックな型引数を使用する他の方法を探究しましょう。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » 関数定義では","id":"ch10-01-syntax.html#a関数定義では","title":"関数定義では"},"ch10-01-syntax.html#enum定義では":{"body":"構造体のように、列挙子にジェネリックなデータ型を保持するenumを定義することができます。 標準ライブラリが提供している Option enumをもう一度見ましょう。このenumは第6章で使用しました: enum Option { Some(T), None,\n} この定義はもう、あなたにとってより道理が通っているはずです。ご覧の通り、 Option は、 型 T に関してジェネリックで2つの列挙子のあるenumです: その列挙子は、型 T の値を保持する Some と、 値を何も保持しない None です。 Option enumを使用することで、オプショナルな値があるという抽象的な概念を表現でき、 Option はジェネリックなので、オプショナルな値の型に関わらず、この抽象を使用できます。 enumも複数のジェネリックな型を使用できます。第9章で使用した Result enumの定義が一例です: enum Result { Ok(T), Err(E),\n} Result enumは2つの型 T 、 E に関してジェネリックで、2つの列挙子があります: 型 T の値を保持する Ok と、 型 E の値を保持する Err です。この定義により、 Result enumを、成功する(なんらかの型 T の値を返す)か、 失敗する(なんらかの型 E のエラーを返す)可能性のある処理がある、あらゆる箇所に使用するのが便利になります。 事実、ファイルを開くのに成功した時に T に型 std::fs::File が入り、ファイルを開く際に問題があった時に E に型 std::io::Error が入ったものが、 リスト9-3でファイルを開くのに使用したものです。 自分のコード内で、保持している値の型のみが異なる構造体やenum定義の場面を認識したら、 代わりにジェネリックな型を使用することで重複を避けることができます。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » enum定義では","id":"ch10-01-syntax.html#enum定義では","title":"enum定義では"},"ch10-02-traits.html#aデフォルト実装":{"body":"時として、全ての型の全メソッドに対して実装を必要とするのではなく、トレイトの全てあるいは一部のメソッドに対してデフォルトの振る舞いがあると有用です。 そうすれば、特定の型にトレイトを実装する際、各メソッドのデフォルト実装を保持するかオーバーライドできるわけです。 リスト10-14は、リスト10-12のように、メソッドシグニチャだけを定義するのではなく、 Summary トレイトの summarize メソッドにデフォルトの文字列を指定する方法を示しています: ファイル名: src/lib.rs pub trait Summary { fn summarize(&self) -> String { // (もっと読む) String::from(\"(Read more...)\") }\n} リスト10-14: summarize メソッドのデフォルト実装がある Summary トレイトの定義 独自の実装を定義するのではなく、デフォルト実装を使用して NewsArticle のインスタンスをまとめるには、 impl Summary for NewsArticle {} と空の impl ブロックを指定します。 たとえ、最早 NewsArticle に直接 summarize メソッドを定義することはなくても、デフォルト実装を提供し、 NewsArticle は Summary トレイトを実装すると指定しました。結果的に、それでも、 NewsArticle のインスタンスに対して summarize メソッドを呼び出すことができます。 このように: let article = NewsArticle { // ペンギンチームがスタンレーカップチャンピオンシップを勝ち取る! headline: String::from(\"Penguins win the Stanley Cup Championship!\"), // ピッツバーグ、ペンシルベニア州、アメリカ location: String::from(\"Pittsburgh, PA, USA\"), // アイスバーグ author: String::from(\"Iceburgh\"), // ピッツバーグ・ペンギンが再度NHL(National Hockey League)で最強のホッケーチームになった content: String::from(\"The Pittsburgh Penguins once again are the best hockey team in the NHL.\"),\n}; // 新しい記事が利用可能です! {}\nprintln!(\"New article available! {}\", article.summarize()); このコードは、 New article available! (Read more...) と出力します。 summarize にデフォルト実装を用意しても、リスト10-13の Tweet の Summary 実装を変える必要はありません。 理由は、デフォルト実装をオーバーライドする記法がデフォルト実装のないトレイトメソッドを実装する記法と同じだからです。 デフォルト実装は、他のデフォルト実装がないメソッドでも呼び出すことができます。 このように、トレイトは多くの有用な機能を提供しつつ、実装者に僅かな部分だけ指定してもらう必要しかないのです。 例えば、 Summary トレイトを実装が必須の summarize_author メソッドを持つように定義し、 それから summarize_author メソッドを呼び出すデフォルト実装のある summarize メソッドを定義することもできます: pub trait Summary { fn summarize_author(&self) -> String; fn summarize(&self) -> String { // {}さんからもっと読む format!(\"(Read more from {}...)\", self.summarize_author()) }\n} このバージョンの Summary を使用するには、型にトレイトを実装する際に summarize_author を定義する必要しかありません: impl Summary for Tweet { fn summarize_author(&self) -> String { format!(\"@{}\", self.username) }\n} summarize_author 定義後、 Tweet 構造体のインスタンスに対して summarize を呼び出せ、 summarize のデフォルト実装は、提供済みの summarize_author の定義を呼び出すでしょう。 summarize_author を実装したので、追加のコードを書く必要なく、 Summary トレイトは、 summarize メソッドの振る舞いを与えてくれました。 let tweet = Tweet { username: String::from(\"horse_ebooks\"), content: String::from(\"of course, as you probably already know, people\"), reply: false, retweet: false,\n}; println!(\"1 new tweet: {}\", tweet.summarize()); このコードは、 1 new tweet: (Read more from @horse_ebooks...) と出力します。 同じメソッドのオーバーライドした実装からは、デフォルト実装を呼び出すことができないことに注意してください。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » デフォルト実装","id":"ch10-02-traits.html#aデフォルト実装","title":"デフォルト実装"},"ch10-02-traits.html#aトレイト-共通の振る舞いを定義する":{"body":"トレイト により、Rustコンパイラに特定の型に存在し、他の型と共有できる機能について知らせます。 トレイトを使用して共通の振る舞いを抽象的に定義できます。トレイト境界を使用して、 あるジェネリックが特定の振る舞いのあるあらゆる型になり得ることを指定できます。 注釈: 違いはあるものの、トレイトは他の言語でよくインターフェイスと呼ばれる機能に類似しています。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » トレイト: 共通の振る舞いを定義する","id":"ch10-02-traits.html#aトレイト-共通の振る舞いを定義する","title":"トレイト: 共通の振る舞いを定義する"},"ch10-02-traits.html#aトレイトを型に実装する":{"body":"今や Summary トレイトを使用して目的の動作を定義できたので、メディア アグリゲータで型に実装できます。 リスト10-13は、 Summary トレイトを NewsArticle 構造体上に実装したもので、ヘッドライン、著者、そして summarize の戻り値を示しています。 Tweet 構造体に関しては、ツイートの内容が既に280文字に制限されていると仮定して、ユーザー名の後にツイートのテキスト全体が続くものとして summarize を定義します。 ファイル名: src/lib.rs # pub trait Summary {\n# fn summarize(&self) -> String;\n# }\n#\npub struct NewsArticle { pub headline: String, pub location: String, pub author: String, pub content: String,\n} impl Summary for NewsArticle { fn summarize(&self) -> String { format!(\"{}, by {} ({})\", self.headline, self.author, self.location) }\n} pub struct Tweet { pub username: String, pub content: String, pub reply: bool, pub retweet: bool,\n} impl Summary for Tweet { fn summarize(&self) -> String { format!(\"{}: {}\", self.username, self.content) }\n} リスト10-13: Summary トレイトを NewsArticle と Tweet 型に実装する 型にトレイトを実装することは、普通のメソッドを実装することに似ています。違いは、 impl の後に、 実装したいトレイトの名前を置き、それから for キーワード、さらにトレイトの実装対象の型の名前を指定することです。 impl ブロック内に、トレイト定義で定義したメソッドシグニチャを置きます。各シグニチャの後にセミコロンを追記するのではなく、 波括弧を使用し、メソッド本体に特定の型のトレイトのメソッドに欲しい特定の振る舞いを入れます。 トレイトを実装後、普通のメソッド同様に NewsArticle や Tweet のインスタンスに対してこのメソッドを呼び出せます。 こんな感じで: let tweet = Tweet { username: String::from(\"horse_ebooks\"), // もちろん、ご存知かもしれないようにね、みなさん content: String::from(\"of course, as you probably already know, people\"), reply: false, retweet: false,\n}; println!(\"1 new tweet: {}\", tweet.summarize()); このコードは、 1 new tweet: horse_ebooks: of course, as you probably already know, people と出力します。 リスト10-13で Summary トレイトと NewArticle 、 Tweet 型を同じ lib.rs に定義したので、 全部同じスコープにあることに注目してください。この lib.rs を aggregator と呼ばれるクレート専用にして、 誰か他の人が私たちのクレートの機能を活用して自分のライブラリのスコープに定義された構造体に Summary トレイトを実装したいとしましょう。 まず、トレイトをスコープにインポートする必要があるでしょう。 use aggregator::Summary; と指定してそれを行い、 これにより、自分の型に Summary を実装することが可能になるでしょう。 Summary トレイトは、 他のクレートが実装するためには、公開トレイトである必要があり、ここでは、リスト10-12の trait の前に、 pub キーワードを置いたのでそうなっています。 トレイト実装で注意すべき制限の1つは、トレイトか対象の型が自分のクレートに固有(local)である時のみ、 型に対してトレイトを実装できるということです。例えば、 Display のような標準ライブラリのトレイトを aggregator クレートの機能の一部として、 Tweet のような独自の型に実装できます。型 Tweet が aggregator クレートに固有だからです。 また、 Summary を aggregator クレートで Vec に対して実装することもできます。 トレイト Summary は、 aggregator クレートに固有だからです。 しかし、外部のトレイトを外部の型に対して実装することはできません。例として、 aggregator クレート内で Vec に対して Display トレイトを実装することはできません。 Display と Vec は標準ライブラリで定義され、 aggregator クレートに固有ではないからです。 この制限は、 コヒーレンス (coherence)あるいは、具体的に オーファンルール (orphan rule)と呼ばれるプログラムの特性の一部で、 親の型が存在しないためにそう命名されました。この規則により、他の人のコードが自分のコードを壊したり、 その逆が起きないことを保証してくれます。この規則がなければ、2つのクレートが同じ型に対して同じトレイトを実装できてしまい、 コンパイラはどちらの実装を使うべきかわからなくなってしまうでしょう。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » トレイトを型に実装する","id":"ch10-02-traits.html#aトレイトを型に実装する","title":"トレイトを型に実装する"},"ch10-02-traits.html#aトレイトを定義する":{"body":"型の振る舞いは、その型に対して呼び出せるメソッドから構成されます。異なる型は、それらの型全部に対して同じメソッドを呼び出せたら、 同じ振る舞いを共有します。トレイト定義は、メソッドシグニチャを一緒くたにしてなんらかの目的を達成するのに必要な一連の振る舞いを定義する手段です。 例えば、いろんな種類や量のテキストを保持する複数の構造体があるとしましょう: 特定の場所で送られる新しいニュースを保持する NewsArticle と、 新規ツイートか、リツイートか、はたまた他のツイートへのリプライなのかを示すメタデータを伴う最大で280文字までの Tweet です。 NewsArticle または Tweet インスタンスに保存されているデータのサマリを表示できるメディア アグリゲータ ライブラリを作成します。 これをするには、各型のサマリーが必要で、インスタンスで summarize メソッドを呼び出してサマリーを要求する必要があります。 リスト10-12は、この振る舞いを表現する Summary トレイトの定義を表示しています。 ファイル名: src/lib.rs pub trait Summary { fn summarize(&self) -> String;\n} リスト10-12: summarize メソッドで提供される振る舞いからなる Summary トレイト ここでは、 trait キーワード、それからトレイト名を使用してトレイトを定義していて、その名前は今回の場合、 Summary です。波括弧の中にこのトレイトを実装する型の振る舞いを記述するメソッドシグニチャを定義し、 今回の場合は、 fn summarize(&self) -> String です。 メソッドシグニチャの後に、波括弧内に実装を提供する代わりに、セミコロンを使用しています。 このトレイトを実装する型はそれぞれ、メソッドの本体に独自の振る舞いを提供しなければなりません。 コンパイラにより、 Summary トレイトを保持するあらゆる型に、このシグニチャと全く同じメソッド summarize が定義されていることが、 強制されます。 トレイトには、本体に複数のメソッドを含むことができます: メソッドシグニチャは行ごとに列挙され、 各行はセミコロンで終止します。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » トレイトを定義する","id":"ch10-02-traits.html#aトレイトを定義する","title":"トレイトを定義する"},"ch10-02-traits.html#aトレイト境界":{"body":"これでトレイトの定義とトレイトを型に実装する方法を知ったので、ジェネリックな型引数でトレイトを使用する方法を探究できます。 トレイト境界 を使用してジェネリックな型を制限し、型が特定のトレイトや振る舞いを実装するものに制限されることを保証できます。 例として、リスト10-13で、 Summary トレイトを型 NewsArticle と Tweet に実装しました。 引数 item に対して summarize メソッドを呼び出す関数 notify を定義でき、この引数はジェネリックな型 T です。 item の summarize を呼ぶときにジェネリックな型 T がメソッド summarize を実装してないというエラーが出ないように、 T のトレイト境界を使って item が Summary トレイトを実装する型でなければならないと指定できます。 pub fn notify(item: T) { // 新ニュース! {} println!(\"Breaking news! {}\", item.summarize());\n} トレイト境界をジェネリックな型引数宣言とともにコロンの後、山カッコ内に配置しています。 T に付けられたトレイト境界のため、 notify を呼び出して NewsArticle か Tweet のどんなインスタンスも渡すことができます。 あらゆる他の型、 String や i32 などでこの関数を呼び出すコードは、型が Summary を実装しないので、 コンパイルできません。 + 記法でジェネリックな型に複数のトレイト境界を指定できます。例えば、関数で T に対してフォーマット表示と、 summarize メソッドを使用するには、 T: Summary + Display を使用して、 T は Summary と Display を実装するどんな型にもなると宣言できます。 しかしながら、トレイト境界が多すぎると欠点もあります。各ジェネリックには、特有のトレイト境界があるので、 複数のジェネリックな型引数がある関数には、関数名と引数リストの間に多くのトレイト境界の情報が付くこともあり、 関数シグニチャが読みづらくなる原因になります。このため、Rustには関数シグニチャの後、 where 節内にトレイト境界を指定する対立的な記法があります。従って、こう書く代わりに: fn some_function(t: T, u: U) -> i32 { こんな感じに where 節を活用できます: fn some_function(t: T, u: U) -> i32 where T: Display + Clone, U: Clone + Debug\n{ この関数シグニチャは、多くのトレイト境界のない関数のように、関数名、引数リスト、戻り値の型が一緒になって近いという点でごちゃごちゃしていません。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » トレイト境界","id":"ch10-02-traits.html#aトレイト境界","title":"トレイト境界"},"ch10-02-traits.html#aトレイト境界で-largest-関数を修正する":{"body":"ジェネリックな型引数の境界で使用したい振る舞いを指定する方法を知ったので、リスト10-5に戻って、 ジェネリックな型引数を使用する largest 関数の定義を修正しましょう!最後にそのコードを実行しようとしたら、 こんなエラーが出ました: error[E0369]: binary operation `>` cannot be applied to type `T` --> src/main.rs:5:12 |\n5 | if item > largest { | ^^^^^^^^^^^^^^ | = note: an implementation of `std::cmp::PartialOrd` might be missing for `T` largest の本体で、大なり演算子( > )を使用して型 T の2つの値を比較したかったのです。その演算子は、 標準ライブラリトレイトの std::cmp::PartialOrd でデフォルトメソッドとして定義されているので、 largest 関数が、比較できるあらゆる型のスライスに対して動くように、 T のトレイト境界に PartialOrd を指定する必要があります。 初期化処理に含まれているので、 PartialOrd をスコープに導入する必要はありません。 largest のシグニチャを以下のような見た目に変えてください: fn largest(list: &[T]) -> T { 今度コードをコンパイルすると、異なる一連のエラーが出ます: error[E0508]: cannot move out of type `[T]`, a non-copy slice\n(エラー: `[T]`、コピーでないスライスからムーブできません。) --> src/main.rs:2:23 |\n2 | let mut largest = list[0]; | ^^^^^^^ | | | cannot move out of here | help: consider using a reference instead: `&list[0]` (助言: 代わりに参照の使用を考慮してください: `&list[0]`) error[E0507]: cannot move out of borrowed content\n(エラー: 借用された内容からムーブできません) --> src/main.rs:4:9 |\n4 | for &item in list.iter() { | ^---- | || | |hint: to prevent move, use `ref item` or `ref mut item` | cannot move out of borrowed content (ヒント: ムーブを避けるには、`ref item`か`ref mut item`を使用してください) このエラーの鍵となる行は、 cannot move out of type [T], a non-copy slice です。 ジェネリックでないバージョンの largest 関数では、最大の i32 か char を探そうとするだけでした。 第4章の「スタックだけのデータ: コピー」節で議論したように、 i32 や char のような既知のサイズの型は、 スタックに格納できるので、 Copy トレイトを実装しています。しかし、 largest 関数をジェネリックにすると、 list 引数が Copy トレイトを実装しない型を含む可能性も出てきたのです。結果として、 list[0] から値を largest にムーブできず、このエラーに陥ったのです。 このコードを Copy トレイトを実装する型とだけで呼び出すには、 T のトレイト境界に Copy を追加できます! リスト10-15は、関数に渡したスライスの値の型が i32 や char などのように、 PartialOrd と Copy を実装する限り、 コンパイルできるジェネリックな largest 関数の完全なコードを示しています。 ファイル名: src/main.rs fn largest(list: &[T]) -> T { let mut largest = list[0]; for &item in list.iter() { if item > largest { largest = item; } } largest\n} 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);\n} リスト10-15: PartialOrd と Copy トレイトを実装するあらゆるジェネリックな型に対して動く、 largest 関数の動く定義 もし largest 関数を Copy を実装する型だけに制限したくなかったら、 Copy ではなく、 T が Clone というトレイト境界を持つと指定することもできます。そうしたら、 largest 関数に所有権が欲しい時にスライスの各値をクローンできます。 clone 関数を使用するということは、 String のようなヒープデータを所有する型の場合にもっとヒープ確保が発生する可能性があることを意味し、 大きなデータを取り扱っていたら、ヒープ確保は遅いこともあります。 largest の別の実装方法は、関数がスライスの T 値への参照を返すようにすることです。 戻り値の型を T ではなく &T に変え、それにより関数の本体を参照を返すように変更したら、 Clone か Copy トレイト境界は必要なくなり、ヒープ確保も避けられるでしょう。 試しにこれらの対立的な解決策もご自身で実装してみてください!","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » トレイト境界で largest 関数を修正する","id":"ch10-02-traits.html#aトレイト境界で-largest-関数を修正する","title":"トレイト境界で largest 関数を修正する"},"ch10-02-traits.html#aトレイト境界を使用してメソッド実装を条件分けする":{"body":"ジェネリックな型引数を持つ impl ブロックにトレイト境界を与えることで、 特定のトレイトを実装する型に対するメソッド実装を条件分けできます。例えば、 リスト10-16の型 Pair は、常に new 関数を実装します。しかし、 Pair は、 内部の型 T が比較を可能にする PartialOrd トレイト と 出力を可能にする Display トレイトを実装している時のみ、 cmp_display メソッドを実装します。 use std::fmt::Display; struct Pair { x: T, y: T,\n} impl Pair { fn new(x: T, y: T) -> Self { Self { x, y, } }\n} impl Pair { 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); } }\n} リスト10-16: トレイト境界によってジェネリックな型に対するメソッド実装を条件分けする また、別のトレイトを実装するあらゆる型に対するトレイト実装を条件分けすることもできます。 トレイト境界を満たすあらゆる型にトレイトを実装することは、 ブランケット実装 (blanket implementation)と呼ばれ、 Rustの標準ライブラリで広く使用されています。例を挙げれば、標準ライブラリは、 Display トレイトを実装するあらゆる型に ToString トレイトを実装しています。 標準ライブラリの impl ブロックは以下のような見た目です: impl ToString for T { // --snip--\n} 標準ライブラリにはこのブランケット実装があるので、 Display トレイトを実装する任意の型に対して、 ToString トレイトで定義された to_string メソッドを呼び出せるのです。 例えば、整数は Display を実装するので、このように整数値を対応する String 値に変換できます: let s = 3.to_string(); ブランケット実装は、「実装したもの」節のトレイトのドキュメンテーションに出現します。 トレイトとトレイト境界により、ジェネリックな型引数を使用して重複を減らしつつ、コンパイラに対して、 そのジェネリックな型に特定の振る舞いが欲しいことを指定するコードを書くことができます。 それからコンパイラは、トレイト境界の情報を活用してコードに使用された具体的な型が正しい振る舞いを提供しているか確認できます。 動的型付け言語では、型が実装しない型のメソッドを呼び出せば、実行時にエラーが出るでしょう。 しかし、Rustはこの種のエラーをコンパイル時に移したので、コードが動かせるようにさえなる以前に問題を修正することを強制されるのです。 加えて、コンパイル時に既に確認したので、実行時に振る舞いがあるかどうか確認するコードを書かなくても済みます。 そうすることでジェネリクスの柔軟性を諦める必要なく、パフォーマンスを向上させます。 もう使用してきたことのある別の種のジェネリクスは、ライフタイムと呼ばれます。 型が欲しい振る舞いを保持していることを保証するのではなく、必要な間だけ参照が有効であることをライフタイムは保証します。 ライフタイムがどうやってそれを行うかを見ましょう。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » トレイト境界を使用して、メソッド実装を条件分けする","id":"ch10-02-traits.html#aトレイト境界を使用してメソッド実装を条件分けする","title":"トレイト境界を使用して、メソッド実装を条件分けする"},"ch10-03-lifetime-syntax.html#aまとめ":{"body":"いろんなことをこの章では講義しましたね!今やジェネリックな型引数、トレイトとトレイト境界、そしてジェネリックなライフタイム引数を知ったので、 多くの異なる場面で動くコードを繰り返しなく書く準備ができました。ジェネリックな型引数により、 コードを異なる型に適用させてくれます。トレイトとトレイト境界は、型がジェネリックであっても、 コードが必要とする振る舞いを持つことを保証します。ライフタイム注釈を活用して、 この柔軟なコードにダングリング参照が存在しないことを保証する方法を学びました。 さらにこの解析は全てコンパイル時に起こり、実行時のパフォーマンスには影響しません! 信じるかどうかは自由ですが、この章で議論した話題にはもっともっと学ぶべきことがあります: 第17章ではトレイトオブジェクトを議論します。これはトレイトを使用する別の手段です。 第19章では、ライフタイム注釈が関わるもっと複雑な筋書きと何か高度な型システムの機能を講義します。 ですが次は、コードがあるべき通りに動いていることを確かめられるように、Rustでテストを書く方法を学びます。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » まとめ","id":"ch10-03-lifetime-syntax.html#aまとめ","title":"まとめ"},"ch10-03-lifetime-syntax.html#aジェネリックな型引数トレイト境界ライフタイムを一度に":{"body":"ジェネリックな型引数、トレイト境界、ライフタイムを指定する記法を全て1関数でちょっと眺めましょう! use std::fmt::Display; fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str where T: Display\n{ // アナウンス! println!(\"Announcement! {}\", ann); if x.len() > y.len() { x } else { y }\n} これがリスト10-22からの2つの文字列のうち長い方を返す longest 関数ですが、 ジェネリックな型 T の ann という追加の引数があり、これは where 節で指定されているように、 Display トレイトを実装するあらゆる型で埋めることができます。 この追加の引数は、関数が文字列スライスの長さを比較する前に出力されるので、 Display トレイト境界が必要なのです。ライフタイムは一種のジェネリックなので、 ライフタイム引数 'a とジェネリックな型引数 T が関数名の後、山カッコ内の同じリストに収まっています。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » ジェネリックな型引数、トレイト境界、ライフタイムを一度に","id":"ch10-03-lifetime-syntax.html#aジェネリックな型引数トレイト境界ライフタイムを一度に","title":"ジェネリックな型引数、トレイト境界、ライフタイムを一度に"},"ch10-03-lifetime-syntax.html#aメソッド定義におけるライフタイム注釈":{"body":"構造体にライフタイムのあるメソッドを実装する際、リスト10-11で示したジェネリックな型引数と同じ記法を使用します。 ライフタイム引数を宣言し使用する場所は、構造体フィールドかメソッド引数と戻り値に関係するかによります。 構造体のフィールド用のライフタイム名は、 impl キーワードの後に宣言する必要があり、 それから構造体名の後に使用されます。そのようなライフタイムは構造体の型の一部になるからです。 impl ブロック内のメソッドシグニチャでは、参照は構造体のフィールドの参照のライフタイムに紐づくか、 独立している可能性があります。加えて、ライフタイム省略規則により、メソッドシグニチャでライフタイム注釈が必要なくなることがよくあります。 リスト10-25で定義した ImportantExcerpt という構造体を使用して、何か例を見ましょう。 まず、唯一の引数が self への参照で戻り値が i32 という何かへの参照ではない level というメソッドを使用します: # struct ImportantExcerpt<'a> {\n# part: &'a str,\n# }\n#\nimpl<'a> ImportantExcerpt<'a> { fn level(&self) -> i32 { 3 }\n} impl 後のライフタイム引数宣言と型名の後に使用するのは必須ですが、最初の省略規則のため、 self への参照のライフタイムを注釈する必要はありません。 3番目のライフタイム省略規則が適用される例はこちらです: # struct ImportantExcerpt<'a> {\n# part: &'a str,\n# }\n#\nimpl<'a> ImportantExcerpt<'a> { fn announce_and_return_part(&self, announcement: &str) -> &str { // お知らせします println!(\"Attention please: {}\", announcement); self.part }\n} 2つ入力ライフタイムがあるので、コンパイラは最初のライフタイム省略規則を適用し、 &self と announcement に独自のライフタイムを与えます。それから、 引数の1つが &self なので、戻り値型は &self のライフタイムを得て、 全てのライフタイムが説明されました。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » メソッド定義におけるライフタイム注釈","id":"ch10-03-lifetime-syntax.html#aメソッド定義におけるライフタイム注釈","title":"メソッド定義におけるライフタイム注釈"},"ch10-03-lifetime-syntax.html#aライフタイムでダングリング参照を回避する":{"body":"ライフタイムの主な目的は、ダングリング参照を回避することです。ダングリング参照によりプログラムは、 参照するつもりだったデータ以外のデータを参照してしまいます。リスト10-17のプログラムを考えてください。 これには、外側のスコープと内側のスコープが含まれています。 { let r; { let x = 5; r = &x; } println!(\"r: {}\", r);\n} リスト10-17: 値がスコープを抜けてしまった参照を使用しようとする 注釈: リスト10-17や10-18、10-24では、変数に初期値を与えずに宣言しているので、変数名は外側のスコープに存在します。 初見では、これはRustにはnull値が存在しないということと衝突しているように見えるかもしれません。 しかしながら、値を与える前に変数を使用しようとすれば、コンパイルエラーになり、 これは、確かにRustではnull値は許可されないことを示します。 外側のスコープで初期値なしの r という変数を宣言し、内側のスコープで初期値5の x という変数を宣言しています。 内側のスコープ内で、 r の値を x への参照にセットしようとしています。それから内側のスコープが終わり、 r の値を出力しようとしています。 r が参照している値が使おうとする前にスコープを抜けるので、 このコードはコンパイルできません。こちらがエラーメッセージです: error[E0597]: `x` does not live long enough\n(エラー: `x`の生存期間が短すぎます) --> src/main.rs:7:5 |\n6 | r = &x; | - borrow occurs here | (借用はここで起きています)\n7 | } | ^ `x` dropped here while still borrowed | (`x`は借用されている間にここでドロップされました)\n...\n10 | } | - borrowed value needs to live until here | (借用された値はここまで生きる必要があります) 変数 x の「生存期間が短すぎます」。原因は、内側のスコープが7行目で終わった時点で x がスコープを抜けるからです。 ですが、 r はまだ、外側のスコープに対して有効です; スコープが大きいので、「長生きする」と言います。 Rustで、このコードが動くことを許可していたら、 r は x がスコープを抜けた時に解放されるメモリを参照していることになり、 r で行おうとするいかなることもちゃんと動作しないでしょう。では、どうやってコンパイラはこのコードが無効であると決定しているのでしょうか? 借用チェッカーを使用しています。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » ライフタイムでダングリング参照を回避する","id":"ch10-03-lifetime-syntax.html#aライフタイムでダングリング参照を回避する","title":"ライフタイムでダングリング参照を回避する"},"ch10-03-lifetime-syntax.html#aライフタイムで参照を検証する":{"body":"第4章の「参照と借用」節で議論しなかった詳細の一つに、Rustにおいて参照は全てライフタイムを保持するということがあります。 ライフタイムとは、その参照が有効になるスコープのことです。多くの場合、型が推論されるように、 大体の場合、ライフタイムも暗黙的に推論されます。複数の型の可能性があるときには、型を注釈しなければなりません。 同様に、参照のライフタイムがいくつか異なる方法で関係することがある場合には注釈しなければなりません。 コンパイラは、ジェネリックライフタイム引数を使用して関係を注釈し、実行時に実際の参照が確かに有効であることを保証することを要求するのです。 ライフタイムの概念は、他のプログラミング言語の道具とはどこか異なり、間違いなく、 Rustで一番際立った機能になっています。この章では、ライフタイムの全てを講義しないものの、 ライフタイム記法と遭遇する可能性のある一般的な手段を議論するので、その概念に馴染めます。 もっと詳しく知るには、第19章の「高度なライフタイム」節を参照されたし。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » ライフタイムで参照を検証する","id":"ch10-03-lifetime-syntax.html#aライフタイムで参照を検証する","title":"ライフタイムで参照を検証する"},"ch10-03-lifetime-syntax.html#aライフタイムの観点で思考する":{"body":"ライフタイム引数を指定する必要のある手段は、関数が行っていることによります。例えば、 longest 関数の実装を最長の文字列スライスではなく、常に最初の引数を返すように変更したら、 y 引数に対してライフタイムを指定する必要はなくなるでしょう。以下のコードはコンパイルできます: ファイル名: src/main.rs fn longest<'a>(x: &'a str, y: &str) -> &'a str { x\n} この例では、引数 x と戻り値に対してライフタイム引数 'a を指定しましたが、引数 y には指定していません。 y のライフタイムは x や戻り値のライフタイムとは何の関係もないからです。 関数から参照を返す際、戻り値型のライフタイム引数は、引数のうちどれかのライフタイム引数と一致する必要があります。 返される参照が引数のどれかを参照してい なけれ ば、この関数内で生成された値を参照しているに違いなく、 これは、その値が関数の末端でスコープを抜けるので、ダングリング参照になるでしょう。 コンパイルできないこの longest 関数の未遂の実装を考えてください: ファイル名: src/main.rs fn longest<'a>(x: &str, y: &str) -> &'a str { // 本当に長い文字列 let result = String::from(\"really long string\"); result.as_str()\n} ここでは、たとえ、戻り値型にライフタイム引数 'a を指定していても、戻り値のライフタイムは、 引数のライフタイムと全く関係がないので、この実装はコンパイルできないでしょう。 こちらが、得られるエラーメッセージです: error[E0597]: `result` does not live long enough --> src/main.rs:3:5 |\n3 | result.as_str() | ^^^^^^ does not live long enough\n4 | } | - borrowed value only lives until here |\nnote: borrowed value must be valid for the lifetime 'a as defined on the\nfunction body at 1:1...\n(注釈: 借用された値は、関数本体1行目1文字目で定義されているようにライフタイム'aに対して有効でなければなりません) --> src/main.rs:1:1 |\n1 | / fn longest<'a>(x: &str, y: &str) -> &'a str {\n2 | | let result = String::from(\"really long string\");\n3 | | result.as_str()\n4 | | } | |_^ 問題は、 result が longest 関数の末端でスコープを抜け、片付けられてしまうことです。 また、関数から result を返そうともしています。ダングリング参照を変えるであろうライフタイム引数を指定する手段はなく、 コンパイラは、ダングリング参照を生成させてくれません。今回の場合、最善の修正案は、 呼び出し元の関数が値の片付けに責任を持てるよう、参照ではなく所有されたデータ型を返すことでしょう。 究極的にライフタイム記法は、関数のいろんな引数と戻り値のライフタイムを接続することに関するのです。 一旦、繋がりができたら、メモリ安全な処理を許可するのに十分な情報がコンパイラにはあり、 ダングリングポインタを生成するであろう処理を不認可し、さもなくばメモリ安全性を侵害するのです。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » ライフタイムの観点で思考する","id":"ch10-03-lifetime-syntax.html#aライフタイムの観点で思考する","title":"ライフタイムの観点で思考する"},"ch10-03-lifetime-syntax.html#aライフタイム注釈記法":{"body":"ライフタイム注釈は、いかなる参照の生存期間も変えることはありません。シグニチャがジェネリックな型引数を指定していると、 関数があらゆる型を受け入れるのと全く同様に、ジェネリックなライフタイム引数を指定することで関数は、 あらゆるライフタイムの参照を受け入れるのです。ライフタイム注釈は、ライフタイムに影響することなく、 複数の参照のライフタイムのお互いの関係を記述します。 ライフタイム注釈は、少し不自然な記法です: ライフタイム引数の名前はアポストロフィー( ' )で始まらなければならず、 通常全部小文字で、ジェネリック型のようにとても短いです。多くの人は、 'a という名前を使います。 ライフタイム引数注釈は、参照の & の後に配置し、注釈と参照の型を区別するために空白を1つ使用します。 例を挙げましょう: ライフタイム引数なしの i32 への参照、 'a というライフタイム引数付きの i32 への参照、 そしてこれもライフタイム 'a 付きの i32 への可変参照です。 &i32 // a reference // (ただの)参照\n&'a i32 // a reference with an explicit lifetime // 明示的なライフタイム付きの参照\n&'a mut i32 // a mutable reference with an explicit lifetime // 明示的なライフタイム付きの可変参照 1つのライフタイム注釈それだけでは、大して意味はありません。注釈は、複数の参照のジェネリックなライフタイム引数が、 お互いにどう関係するかをコンパイラに指示することを意図しているからです。例えば、 ライフタイム 'a 付きの i32 への参照となる引数 first のある関数があるとしましょう。 この関数にはさらに、 'a のライフタイム付きの i32 への別の参照となる second という別の引数もあります。 ライフタイム注釈は、 first と second の参照がどちらもジェネリックなライフタイムと同じだけ生きることを示唆します。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » ライフタイム注釈記法","id":"ch10-03-lifetime-syntax.html#aライフタイム注釈記法","title":"ライフタイム注釈記法"},"ch10-03-lifetime-syntax.html#aライフタイム省略":{"body":"全参照にはライフタイムがあり、参照を使用する関数や構造体にはライフタイム引数を指定する必要があることを学びました。 ですが、リスト4-9にとある関数があり、リスト10-26に再度示しましたが、 これは、ライフタイム注釈なしでコンパイルできました。 ファイル名: src/lib.rs fn first_word(s: &str) -> &str { let bytes = s.as_bytes(); for (i, &item) in bytes.iter().enumerate() { if item == b' ' { return &s[0..i]; } } &s[..]\n} リスト10-26: 引数と戻り値型が参照であるにも関わらず、ライフタイム注釈なしでコンパイルできた リスト4-9で定義した関数 この関数がライフタイム注釈なしでコンパイルできた理由は、歴史的なものです: 昔のバージョンのRust(1.0以前)では、 全参照に明示的なライフタイムが必要だったので、このコードはコンパイルできませんでした。 その頃、関数シグニチャはこのように記述されていたのです: fn first_word<'a>(s: &'a str) -> &'a str { 多くのRustコードを書いた後、Rustチームは、Rustプログラマが特定の場面では、 何度も何度も同じライフタイム注釈を入力することを発見しました。これらの場面は予測可能で、 いくつかの決定的なパターンに従っていました。開発者はこのパターンをコンパイラのコードに落とし込んだので、 このような場面には借用チェッカーがライフタイムを推論できるようになり、明示的な注釈を必要としなくなったのです。 他の決定的なパターンが出現し、コンパイラに追加されることもあり得るので、このRustの歴史は関係があります。 将来的に、さらに少数のライフタイム注釈しか必要にならない可能性もあります。 コンパイラの参照解析に落とし込まれたパターンは、 ライフタイム省略規則 と呼ばれます。 これらはプログラマが従う規則ではありません; コンパイラが考慮する一連の特定のケースであり、 自分のコードがこのケースに当てはまれば、ライフタイムを明示的に書く必要はなくなります。 省略規則は、完全な推論を提供しません。コンパイラが決定的に規則を適用できるけれども、 参照が保持するライフタイムに関してそれでも曖昧性があるなら、コンパイラは、残りの参照がなるべきライフタイムを推測しません。 この場合コンパイラは、それらを推測するのではなくエラーを与えます。 これらは、参照がお互いにどう関係するかを指定するライフタイム注釈を追記することで解決できます。 関数やメソッドの引数のライフタイムは、 入力ライフタイム と呼ばれ、 戻り値のライフタイムは 出力ライフタイム と称されます。 コンパイラは3つの規則を活用し、明示的な注釈がない時に、参照がどんなライフタイムになるかを計算します。 最初の規則は入力ライフタイムに適用され、2番目と3番目の規則は出力ライフタイムに適用されます。 コンパイラが3つの規則の最後まで到達し、それでもライフタイムを割り出せない参照があったら、 コンパイラはエラーで停止します。 最初の規則は、参照である各引数は、独自のライフタイム引数を得るというものです。換言すれば、 1引数の関数は、1つのライフタイム引数を得るということです: fn foo<'a>(x: &'a i32) ; 2つ引数のある関数は、2つの個別のライフタイム引数を得ます: fn foo<'a, 'b>(x: &'a i32, y: &'b i32) ; 以下同様。 2番目の規則は、1つだけ入力ライフタイム引数があるなら、そのライフタイムが全ての出力ライフタイム引数に代入されるというものです: fn foo<'a>(x: &'a i32) -> &'a i32 。 3番目の規則は、複数の入力ライフタイム引数があるけれども、メソッドなのでそのうちの一つが &self や &mut self だったら、 self のライフタイムが全出力ライフタイム引数に代入されるというものです。 この3番目の規則により、必要なシンボルの数が減るので、メソッドが遥かに読み書きしやすくなります。 コンパイラになってみましょう。これらの規則を適用して、リスト10-26の first_word 関数のシグニチャの参照のライフタイムが何か計算します。 シグニチャは、参照に紐づけられるライフタイムがない状態から始まります: fn first_word(s: &str) -> &str { そうして、コンパイラは最初の規則を適用し、各引数が独自のライフタイムを得ると指定します。 それを通常通り 'a と呼ぶので、シグニチャはこうなります: fn first_word<'a>(s: &'a str) -> &str { 1つだけ入力ライフタイムがあるので、2番目の規則を適用します。2番目の規則は、1つの入力引数のライフタイムが、 出力引数に代入されると指定するので、シグニチャはこうなります: fn first_word<'a>(s: &'a str) -> &'a str { もうこの関数シグニチャの全ての参照にライフタイムが付いたので、コンパイラは、 プログラマにこの関数シグニチャのライフタイムを注釈してもらう必要なく、解析を続行できます。 別の例に目を向けましょう。今回は、リスト10-21で取り掛かったときにはライフタイム引数がなかった longest 関数です: fn longest(x: &str, y: &str) -> &str { 最初の規則を適用しましょう: 各引数が独自のライフタイムを得るのです。今回は、 1つではなく2つ引数があるので、ライフタイムも2つです: fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &str { 2つ以上入力ライフタイムがあるので、2番目の規則は適用されないとわかります。また3番目の規則も適用されません。 longest はメソッドではなく関数なので、どの引数も self ではないのです。3つの規則全部を適用した後、 まだ戻り値型のライフタイムが判明していません。このために、リスト10-21でこのコードをコンパイルしようとしてエラーになったのです: コンパイラは、ライフタイム省略規則全てを適用したけれども、シグニチャの参照全部のライフタイムを計算できなかったのです。 3番目の規則は本当にメソッドシグニチャでしか適用されないので、次にその文脈でライフタイムを観察し、 3番目の規則が、メソッドシグニチャであまり頻繁にライフタイムを注釈しなくても済むことを意味する理由を確認します。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » ライフタイム省略","id":"ch10-03-lifetime-syntax.html#aライフタイム省略","title":"ライフタイム省略"},"ch10-03-lifetime-syntax.html#a借用精査機":{"body":"Rustコンパイラには、スコープを比較して全ての借用が有効であるかを決定する 借用チェッカー があります。 リスト10-18は、リスト10-17と同じコードを示していますが、変数のライフタイムを表示する注釈が付いています: { let r; // ---------+-- 'a // | { // | let x = 5; // -+-- 'b | r = &x; // | | } // -+ | // | println!(\"r: {}\", r); // |\n} // ---------+ リスト10-18: それぞれ 'a と 'b と名付けられた r と x のライフタイムの注釈 ここで、 r のライフタイムは 'a 、 x のライフタイムは 'b で注釈しました。ご覧の通り、 内側の 'b ブロックの方が、外側の 'a ライフタイムブロックよりはるかに小さいです。 コンパイル時に、コンパイラは2つのライフタイムのサイズを比較し、 r は 'a のライフタイムだけれども、 'b のライフタイムのメモリを参照していると確認します。 'b は 'a よりも短いので、プログラムは拒否されます: 参照の対象が参照ほど長生きしないのです。 リスト10-19でコードを修正したので、ダングリング参照はなくなり、エラーなくコンパイルできます。 { let x = 5; // ----------+-- 'b // | let r = &x; // --+-- 'a | // | | println!(\"r: {}\", r); // | | // --+ |\n} // ----------+ リスト10-19: データのライフタイムが参照より長いので、有効な参照 ここで x のライフタイムは 'b になり、今回の場合 'a よりも大きいです。つまり、 コンパイラは x が有効な間、 r の参照も常に有効になることを把握しているので、 r は x を参照できます。 今や、参照のライフタイムがどれだけあり、コンパイラがライフタイムを解析して参照が常に有効であることを保証する仕組みがわかったので、 関数の文脈でジェネリックな引数と戻り値のライフタイムを探究しましょう。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » 借用精査機","id":"ch10-03-lifetime-syntax.html#a借用精査機","title":"借用精査機"},"ch10-03-lifetime-syntax.html#a構造体定義のライフタイム注釈":{"body":"ここまで、所有された型を保持する構造体だけを定義してきました。構造体に参照を保持させることもできますが、 その場合、構造体定義の全参照にライフタイム注釈を付け加える必要があるでしょう。 リスト10-25には、文字列スライスを保持する ImportantExcerpt (重要な一節)という構造体があります。 ファイル名: src/main.rs struct ImportantExcerpt<'a> { part: &'a str,\n} fn main() { // 僕をイシュマエルとお呼び。何年か前・・・ let novel = String::from(\"Call me Ishmael. Some years ago...\"); let first_sentence = novel.split('.') .next() .expect(\"Could not find a '.'\"); // '.'が見つかりませんでした let i = ImportantExcerpt { part: first_sentence };\n} リスト10-25: 参照を含む構造体なので、定義にライフタイム注釈が必要 この構造体には文字列スライスを保持する1つのフィールド、 part があり、これは参照です。 ジェネリックなデータ型同様、構造体名の後、山カッコの中にジェネリックなライフタイム引数の名前を宣言するので、 構造体定義の本体でライフタイム引数を使用できます。この注釈は、 ImportantExcerpt のインスタンスが、 part フィールドに保持している参照よりも長生きしないことを意味します。 ここの main 関数は、変数 novel に所有される String の、最初の文への参照を保持する ImportantExcerpt インスタンスを生成しています。 novel のデータは、 ImportantExcerpt インスタンスが作られる前に存在しています。 加えて、 ImportantExcerpt がスコープを抜けるまで novel はスコープを抜けないので、 ImportantExcerpt インスタンスの参照は有効なのです。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » 構造体定義のライフタイム注釈","id":"ch10-03-lifetime-syntax.html#a構造体定義のライフタイム注釈","title":"構造体定義のライフタイム注釈"},"ch10-03-lifetime-syntax.html#a関数のジェネリックなライフタイム":{"body":"2つの文字列スライスのうち、長い方を返す関数を書きましょう。この関数は、 2つの文字列スライスを取り、1つの文字列スライスを返します。 longest 関数の実装完了後、 リスト10-20のコードは、 The logest string is abcd と出力するはずです。 ファイル名: src/main.rs fn main() { let string1 = String::from(\"abcd\"); let string2 = \"xyz\"; let result = longest(string1.as_str(), string2); // 最長の文字列は、{}です println!(\"The longest string is {}\", result);\n} リスト10-20: longest 関数を呼び出して2つの文字列スライスのうち長い方を探す main 関数 関数に取ってほしい引数が文字列スライス、つまり参照であることに注意してください。 何故なら、 longest 関数に引数の所有権を奪ってほしくないからです。 この関数に String のスライス(変数 string1 に格納されている型)と文字列リテラル(変数 string2 が含むもの)を受け取らせたいのです。 リスト10-20で使用している引数が、我々が必要としているものである理由についてもっと詳しい議論は、 第4章の「引数としての文字列スライス」節をご参照ください。 リスト10-21に示したように longest 関数を実装しようとしたら、コンパイルできないでしょう。 ファイル名: src/main.rs fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y }\n} リスト10-21: 2つの文字列スライスのうち長い方を返すけれども、コンパイルできない longest 関数の実装 代わりに、以下のようなライフタイムに言及するエラーが出ます: error[E0106]: missing lifetime specifier\n(エラー: ライフタイム指定子が不足しています) --> src/main.rs:1:33 |\n1 | fn longest(x: &str, y: &str) -> &str { | ^ expected lifetime parameter | (ライフタイム引数が予想されます) | = help: this function's return type contains a borrowed value, but the\nsignature does not say whether it is borrowed from `x` or `y` (助言: この関数の戻り値型は借用された値を含んでいますが、\nシグニチャは、それが`x`か`y`由来のものなのか宣言していません) 助言テキストが、戻り値の型はジェネリックなライフタイム引数である必要があると明かしています。 というのも、返している参照が x か y のどちらを参照しているか、コンパイラにはわからないからです。 この関数の本体の if ブロックは x への参照を返し、 else ブロックは y への参照を返すので、 実際、どちらか私たちにもわかりません! この関数を定義する際、この関数に渡される具体的な値がわからないので、 if ケースか、 else ケースが実行されるか、わからないのです。 また、渡される参照の具体的なライフタイムもわからないので、リスト10-18と10-19で、 返す参照が常に有効であるかを決定したように、スコープを見ることもできないのです。 借用チェッカーもこれを決定することはできません。 x と y のライフタイムがどう戻り値のライフタイムと関係するかわからないからです。 このエラーを修正するには、借用チェッカーが解析を実行できるように、参照間の関係を定義するジェネリックなライフタイム引数を追加します。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » 関数のジェネリックなライフタイム","id":"ch10-03-lifetime-syntax.html#a関数のジェネリックなライフタイム","title":"関数のジェネリックなライフタイム"},"ch10-03-lifetime-syntax.html#a関数シグニチャにおけるライフタイム注釈":{"body":"さて、 longest 関数の文脈でライフタイム注釈を調査しましょう。ジェネリックな型引数同様、 関数名と引数リストの間、山カッコの中にジェネリックなライフタイム引数を宣言する必要があります。 このシグニチャで表現したい制約は、引数の全参照と戻り値が同じライフタイムになることです。 ライフタイムを 'a と名付け、それから各参照に追記します。リスト10-22に示したように。 ファイル名: src/main.rs fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y }\n} リスト10-22: シグニチャの全参照が同じライフタイム 'a になると指定した longest 関数の定義 このコードはコンパイルでき、リスト10-20の main 関数とともに使用したら、欲しい結果になるはずです。 これで関数シグニチャは、何らかのライフタイム 'a に対して、関数は2つの引数を取り、 どちらも少なくともライフタイム 'a と同じだけ生きる文字列スライスであるとコンパイラに教えるようになりました。 また、この関数シグニチャは、関数から返る文字列スライスも少なくともライフタイム 'a と同じだけ生きると、 コンパイラに教えています。これらの制約は、コンパイラに強制してほしいものです。 この関数シグニチャでライフタイム引数を指定する時、渡されたり、返したりした、いかなる値のライフタイムも変更していないことを思い出してください。 むしろ、借用チェッカーは、これらの制約を守らない値全てを拒否するべきと指定しています。 longest 関数は、正確に x と y の生存期間を知る必要はなく、何かのスコープが 'a に代替され、 このシグニチャを満足することだけ知っている必要があることに注意してください。 関数でライフタイムを注釈する際、注釈は関数シグニチャに嵌(はま)り、 関数本体には嵌りません。コンパイラは、なんの助けもなく、関数内のコードを解析できます。しかしながら、 関数に、関数外からの参照や、関数外への参照がある場合、コンパイラは引数や戻り値のライフタイムをそれだけで解決することはほとんど不可能になります。 ライフタイムは、関数が呼び出される度に異なる可能性があります。このために、手動でライフタイムを注釈する必要があるのです。 具体的な参照を longest に渡すと、 'a を代替する具体的なライフタイムは、 y のスコープと被さる x のスコープの一部になります。 言い換えると、ジェネリックなライフタイム 'a は、 x と y のライフタイムのうち、小さい方に等しい具体的なライフタイムになるのです。 返却される参照を同じライフタイム引数 'a で注釈したので、返却される参照も x か y のライフタイムの小さい方と同じだけ有効になるでしょう。 ライフタイム注釈が異なる具体的なライフタイムになる参照を渡すことで longest 関数を制限する方法を見ましょう。 リスト10-23は、率直な例です。 ファイル名: src/main.rs # fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n# if x.len() > y.len() {\n# x\n# } else {\n# y\n# }\n# }\n#\nfn main() { // 長い文字列は長い let string1 = String::from(\"long string is long\"); { let string2 = String::from(\"xyz\"); let result = longest(string1.as_str(), string2.as_str()); println!(\"The longest string is {}\", result); }\n} リスト10-23: 異なる具体的なライフタイムの String 値への参照で longest 関数を使用する この例において、 string1 は外側のスコープの終わりまで有効で、 string2 は内側のスコープの終わりまで有効、 そして result は内側のスコープの終わりまで有効な何かを参照しています。このコードを実行すると、 借用チェッカーがこのコードを良しとするのがわかるでしょう。要するに、コンパイルでき、 The longest string is long string is long と出力するのです。 次に、 result の参照のライフタイムが2つの引数の小さい方のライフタイムになることを示す例を試しましょう。 result 変数の宣言を内側のスコープの外に移すものの、 result 変数への代入は string2 のスコープ内に残したままにします。 それから result を使用する println! を内側のスコープの外、内側のスコープが終わった後に移動します。 リスト10-24のコードはコンパイルできません。 ファイル名: src/main.rs fn main() { let string1 = String::from(\"long string is long\"); let result; { let string2 = String::from(\"xyz\"); result = longest(string1.as_str(), string2.as_str()); } println!(\"The longest string is {}\", result);\n} リスト10-24: string2 がスコープを抜けてから result を使用しようとする このコードのコンパイルを試みると、こんなエラーになります: error[E0597]: `string2` does not live long enough --> src/main.rs:15:5 |\n14 | result = longest(string1.as_str(), string2.as_str()); | ------- borrow occurs here\n15 | } | ^ `string2` dropped here while still borrowed\n16 | println!(\"The longest string is {}\", result);\n17 | } | - borrowed value needs to live until here このエラーは、 result が println! 文に対して有効になるために、 string2 が外側のスコープの終わりまで有効である必要があることを示しています。 関数引数と戻り値のライフタイムを同じライフタイム引数 'a で注釈したので、コンパイラはこのことを知っています。 人間からしたら、このコードを見て string1 は string2 よりも長いことが確認でき、 故に result は string1 への参照を含んでいます。まだ string1 はスコープを抜けていないので、 それでも string1 への参照は println! にとって有効でしょう。ですが、コンパイラはこの場合、 参照が有効であると見なせません。 longest 関数から返ってくる参照のライフタイムは、 渡した参照のうちの小さい方と同じだとコンパイラに指示しました。それ故に、 借用チェッカーは、リスト10-24のコードを無効な参照がある可能性があるとして許可しないのです。 試しに値や、 longest 関数に渡される参照のライフタイムや、返される参照の使用法が異なる実験をもっとしてみてください。 自分の実験がコンパイル前に借用チェッカーを通るかどうか仮説を立ててください; そして、正しいか確かめてください!","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » 関数シグニチャにおけるライフタイム注釈","id":"ch10-03-lifetime-syntax.html#a関数シグニチャにおけるライフタイム注釈","title":"関数シグニチャにおけるライフタイム注釈"},"ch10-03-lifetime-syntax.html#a静的ライフタイム":{"body":"議論する必要のある1種の特殊なライフタイムが、 'static であり、これはプログラム全体の期間を示します。 文字列リテラルは全て 'static ライフタイムになり、次のように注釈できます: // 静的ライフタイムを持ってるよ\nlet s: &'static str = \"I have a static lifetime.\"; この文字列のテキストは、プログラムのバイナリに直接格納され、常に利用可能です。故に、全文字列リテラルのライフタイムは、 'static なのです。 エラーメッセージで 'static ライフタイムを使用する提言を目撃する可能性があります。 ですが、参照に対してライフタイムとして 'static を指定する前に、今ある参照が本当にプログラムの全期間生きるかどうか考えてください。 可能であっても、参照がそれだけの期間生きてほしいかどうか考慮する可能性があります。 ほとんどの場合、問題は、ダングリング参照を生成しようとしているか、利用可能なライフタイムの不一致が原因です。 そのような場合、解決策はその問題を修正することであり、 'static ライフタイムを指定することではありません。","breadcrumbs":"ジェネリック型、トレイト、ライフタイム » 静的ライフタイム","id":"ch10-03-lifetime-syntax.html#a静的ライフタイム","title":"静的ライフタイム"},"ch11-00-testing.html#a自動テストを書く":{"body":"1972年のエッセイ「謙虚なプログラマ」でエドガー・W・ダイクストラは以下のように述べています。 「プログラムのテストは、バグの存在を示すには非常に効率的な手法であるが、 バグの不在を示すには望み薄く不適切である」と。これは、できるだけテストを試みるべきではないということではありません。 プログラムの正当性は、どこまで自分のコードが意図していることをしているかなのです。 Rustは、プログラムの正当性に重きを置いて設計されていますが、 正当性は複雑で、単純に証明することはありません。Rustの型システムは、 この重荷の多くの部分を肩代わりしてくれますが、型システムはあらゆる種類の不当性を捕捉してはくれません。 ゆえに、Rustでは、言語内で自動化されたソフトウェアテストを書くことをサポートしているのです。 例として、渡された何かの数値に2を足す add_two という関数を書くとしましょう。 この関数のシグニチャは、引数に整数を取り、結果として整数を返します。 この関数を実装してコンパイルすると、コンパイラはこれまでに学んできた型チェックと借用チェックを全て行い、 例えば、 String の値や無効な参照をこの関数に渡していないかなどを確かめるのです。 ところが、コンパイラはプログラマがまさしく意図したことを関数が実行しているかどうかは確かめ られません 。 つまり、そうですね、引数に10を足したり、50を引いたりするのではなく、引数に2を足していることです。 そんな時に、テストは必要になるのです。 例えば、 add_two 関数に 3 を渡した時に、戻り値は5であることをアサーションするようなテストを書くことができます。 コードに変更を加えた際にこれらのテストを走らせ、既存の正当な振る舞いが変わっていないことを確認できます。 テストは、複雑なスキルです: いいテストの書き方をあらゆる方面から講義することは1章だけではできないのですが、 Rustのテスト機構のメカニズムについて議論します。テストを書く際に利用可能になるアノテーションとマクロについて、 テストを実行するのに提供されているオプションと標準の動作、さらにテストをユニットテストや統合テストに体系化する方法について語ります。","breadcrumbs":"自動テストを書く","id":"ch11-00-testing.html#a自動テストを書く","title":"自動テストを書く"},"ch11-01-writing-tests.html#assert-マクロで結果を確認する":{"body":"assert! マクロは、標準ライブラリで提供されていますが、テスト内の何らかの条件が true と評価されることを確かめたいときに有効です。 assert! マクロには、論理値に評価される引数を与えます。その値が true なら、 assert! は何もせず、テストは通ります。その値が false なら、 assert! マクロは panic! マクロを呼び出し、 テストは失敗します。 assert! マクロを使用することで、コードが意図した通りに機能していることを確認する助けになるわけです。 第5章のリスト5-15で、 Rectangle 構造体と can_hold メソッドを使用しました。リスト11-5でもそれを繰り返しています。 このコードを src/lib.rs ファイルに放り込み、 assert! マクロでそれ用のテストを何か書いてみましょう。 ファイル名: src/lib.rs # fn main() {}\n#[derive(Debug)]\npub struct Rectangle { length: u32, width: u32,\n} impl Rectangle { pub fn can_hold(&self, other: &Rectangle) -> bool { self.length > other.length && self.width > other.width }\n} リスト11-5: 第5章から Rectangle 構造体とその can_hold メソッドを使用する can_hold メソッドは論理値を返すので、 assert! マクロの完璧なユースケースになるわけです。 リスト11-6で、長さが8、幅が7の Rectangle インスタンスを生成し、これが長さ5、 幅1の別の Rectangle インスタンスを保持できるとアサーションすることで can_hold を用いるテストを書きます。 ファイル名: src/lib.rs # fn main() {}\n#[cfg(test)]\nmod tests { use super::*; #[test] fn larger_can_hold_smaller() { let larger = Rectangle { length: 8, width: 7 }; let smaller = Rectangle { length: 5, width: 1 }; assert!(larger.can_hold(&smaller)); }\n} リスト11-6: より大きな四角形がより小さな四角形を確かに保持できるかを確認する can_hold 用のテスト tests モジュール内に新しい行を加えたことに注目してください: use super::* です。 tests モジュールは、第7章の「プライバシー規則」節で講義した通常の公開ルールに従う普通のモジュールです。 tests モジュールは、内部モジュールなので、外部モジュール内のテスト配下にあるコードを内部モジュールのスコープに持っていく必要があります。 ここではglobを使用して、外部モジュールで定義したもの全てがこの tests モジュールでも使用可能になるようにしています。 テストは larger_can_hold_smaller と名付け、必要な Rectangle インスタンスを2つ生成しています。 そして、 assert! マクロを呼び出し、 larger.can_hold(&smaller) の呼び出し結果を渡しました。 この式は、 true を返すと考えられるので、テストは通るはずです。確かめましょう! running 1 test\ntest tests::larger_can_hold_smaller ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 通ります!別のテストを追加しましょう。今回は、小さい四角形は、より大きな四角形を保持できないことをアサーションします。 ファイル名: src/lib.rs # fn main() {}\n#[cfg(test)]\nmod tests { use super::*; #[test] fn larger_can_hold_smaller() { // --snip-- } #[test] fn smaller_cannot_hold_larger() { let larger = Rectangle { length: 8, width: 7 }; let smaller = Rectangle { length: 5, width: 1 }; assert!(!smaller.can_hold(&larger)); }\n} 今回の場合、 can_hold 関数の正しい結果は false なので、その結果を assert! マクロに渡す前に反転させる必要があります。 結果として、 can_hold が false を返せば、テストは通ります。 running 2 tests\ntest tests::smaller_cannot_hold_larger ... ok\ntest tests::larger_can_hold_smaller ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 通るテストが2つ!さて、コードにバグを導入したらテスト結果がどうなるか確認してみましょう。 長さを比較する大なり記号を小なり記号で置き換えて can_hold メソッドの実装を変更しましょう: # fn main() {}\n# #[derive(Debug)]\n# pub struct Rectangle {\n# length: u32,\n# width: u32,\n# }\n// --snip-- impl Rectangle { pub fn can_hold(&self, other: &Rectangle) -> bool { self.length < other.length && self.width > other.width }\n} テストを実行すると、以下のような出力をします: running 2 tests\ntest tests::smaller_cannot_hold_larger ... ok\ntest tests::larger_can_hold_smaller ... FAILED failures: ---- tests::larger_can_hold_smaller stdout ---- thread 'tests::larger_can_hold_smaller' panicked at 'assertion failed: larger.can_hold(&smaller)', src/lib.rs:22:8 (スレッド'tests::larger_can_hold_smallerはsrc/lib.rs:22:8の'assertion failed: larger.can_hold(&smaller)' でパニックしました)\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. failures: tests::larger_can_hold_smaller test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out テストによりバグが捕捉されました! larger.length が8、 smaller.length が5なので、 can_hold 内の長さの比較が今は false を返すようになったのです: 8は5より小さくないですからね。","breadcrumbs":"テスト » assert! マクロで結果を確認する","id":"ch11-01-writing-tests.html#assert-マクロで結果を確認する","title":"assert! マクロで結果を確認する"},"ch11-01-writing-tests.html#assert_eq-と-assert_ne-マクロで等値性をテストする":{"body":"機能をテストする一般的な方法は、テスト下にあるコードの結果をコードが返すと期待される値と比較して、 等しいと確かめることです。これを assert マクロを使用して == 演算子を使用した式を渡すことで行うこともできます。 しかしながら、これはありふれたテストなので、標準ライブラリには1組のマクロ( assert_eq! と assert_ne! )が提供され、 このテストをより便利に行うことができます。これらのマクロはそれぞれ、二つの引数を等値性と非等値性のために比較します。 また、アサーションが失敗したら二つの値の出力もし、テストが失敗した 原因 を確認しやすくなります。 一方で assert! マクロは、 == 式の値が false 値になったことしか示唆せず、 false 値に導いた値は出力しません。 リスト11-7において、引数に 2 を加えて結果を返す add_two という名前の関数を書いています。 そして、 assert_eq! マクロでこの関数をテストしています。 ファイル名: src/lib.rs # fn main() {}\npub fn add_two(a: i32) -> i32 { a + 2\n} #[cfg(test)]\nmod tests { use super::*; #[test] fn it_adds_two() { assert_eq!(4, add_two(2)); }\n} リスト11-7: assert_eq! マクロで add_two 関数をテストする テストが通ることを確認しましょう! running 1 test\ntest tests::it_adds_two ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out assert_eq! マクロに与えた第1引数の 4 は、 add_two(2) の呼び出し結果と等しいです。 このテストの行は test tests::it_adds_two ... ok であり、 ok というテキストはテストが通ったことを示しています! コードにバグを仕込んで、 assert_eq! を使ったテストが失敗した時にどんな見た目になるのか確認してみましょう。 add_two 関数の実装を代わりに 3 を足すように変えてください: # fn main() {}\npub fn add_two(a: i32) -> i32 { a + 3\n} テストを再度実行します: running 1 test\ntest tests::it_adds_two ... FAILED failures: ---- tests::it_adds_two stdout ---- thread 'tests::it_adds_two' panicked at 'assertion failed: `(left == right)` left: `4`, right: `5`', src/lib.rs:11:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. failures: tests::it_adds_two test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out テストがバグを捕捉しました! it_adds_two のテストは失敗し、 assertion failed: `(left == right)` というメッセージを表示し、 left は 4 で、 right は 5 だったと示しています。このメッセージは有用で、デバッグを開始する助けになります: assert_eq! の left 引数は 4 だったが、 add_two(2) がある right 引数は 5 だったことを意味しています。 二つの値が等しいとアサーションを行う関数の引数は、 expected と actual と呼ばれ、引数を指定する順序が問題になる言語やテストフレームワークもあることに注意してください。 ですがRustでは、 left と right と呼ばれ、期待する値とテスト下のコードが生成する値を指定する順序は、 問題になりません。 assert_eq!(add_two(2), 4) と今回のテストのアサーションを書くこともでき、 そうすると失敗メッセージは、 assertion failed: `(left == right)` となり、 left が 5 で right が 4 と表示されるわけです。 assert_ne! マクロは、与えた2つの値が等しくなければ通り、等しければ失敗します。 このマクロは、値が何になる だろう か確信が持てないけれども、コードが意図した通りに動いていれば、 確実にこの値にはなら ないだろう とわかっているような場合に最も有用になります。例えば、 入力を何らかの手段で変えることが保障されているけれども、入力が変更される方法がテストを実行する曜日に依存する関数をテストしているなら、 アサーションすべき最善の事柄は、関数の出力が入力と等しくないことかもしれません。 表面下では、 assert_eq! と assert_ne! マクロはそれぞれ、 == と != 演算子を使用しています。 アサーションが失敗すると、これらのマクロは引数をデバッグフォーマットを使用して出力するので、 比較対象の値は PartialEq と Debug トレイトを実装していなければなりません。 組み込み型の全部と、標準ライブラリの型はほぼ全てこれらのトレイトを実装しています。 自分で定義した構造体とenumについては、 PartialEq を実装して、 その型の値が等しいか等しくないかアサーションする必要があるでしょう。 Debug を実装して、 アサーションが失敗した時に値を出力する必要もあるでしょう。 第5章のリスト5-12で触れたように、どちらのトレイトも導出可能なトレイトなので、 これは通常、構造体やenum定義に #[derive(PartialEq, Debug)] という注釈を追加するくらい単純になります。 これらや他の導出可能なトレイトに関する詳細については、付録Cをご覧ください。","breadcrumbs":"テスト » assert_eq! と assert_ne! マクロで等値性をテストする","id":"ch11-01-writing-tests.html#assert_eq-と-assert_ne-マクロで等値性をテストする","title":"assert_eq! と assert_ne! マクロで等値性をテストする"},"ch11-01-writing-tests.html#aカスタムの失敗メッセージを追加する":{"body":"さらに、 assert! 、 assert_eq! 、 assert_ne! の追加引数として、失敗メッセージと共にカスタムのメッセージが表示されるよう、 追加することもできます。 assert! の1つの必須引数、 あるいは assert_eq! と assert_ne! の2つの必須引数の後に指定された引数はどれも format! マクロに明け渡されるので、 (format!マクロについては第8章の「 + 演算子または、 format! マクロで連結する」節で議論しました)、 {} プレースホルダーを含むフォーマット文字列とこのプレースホルダーに置き換えられる値を渡すことができます。 カスタムメッセージは、アサーションがどんな意味を持つかドキュメント化するのに役に立ちます; テストが失敗した時、問題が何なのかコードと共により良い考えを持てるでしょう。 例として、人々に名前で挨拶をする関数があり、関数に渡した名前が出力に出現することをテストしたいとしましょう: ファイル名: src/lib.rs # fn main() {}\npub fn greeting(name: &str) -> String { // こんにちは、{}さん! format!(\"Hello {}!\", name)\n} #[cfg(test)]\nmod tests { use super::*; #[test] fn greeting_contains_name() { let result = greeting(\"Carol\"); assert!(result.contains(\"Carol\")); }\n} このプログラムの必要事項はまだ合意が得られておらず、挨拶の先頭の Hello というテキストは変わるだろうということは極めて確かです。 要件が変わった時にテストを更新しなくてもよいようにしたいと決定したので、 greeting 関数から返る値と正確な等値性を確認するのではなく、出力が入力引数のテキストを含むことをアサーションするだけにします。 greeting が name を含まないように変更してこのコードにバグを仕込み、このテストの失敗がどんな見た目になるのか確かめましょう: # fn main() {}\npub fn greeting(name: &str) -> String { String::from(\"Hello!\")\n} このテストを実行すると、以下のように出力されます: running 1 test\ntest tests::greeting_contains_name ... FAILED failures: ---- tests::greeting_contains_name stdout ---- thread 'tests::greeting_contains_name' panicked at 'assertion failed:\nresult.contains(\"Carol\")', src/lib.rs:12:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. failures: tests::greeting_contains_name この結果は、アサーションが失敗し、どの行にアサーションがあるかを示しているだけです。 より有用な失敗メッセージは今回の場合、 greeting 関数から得た値を出力することでしょう。 greeting 関数から得た実際の値で埋められるプレースホルダーを含むフォーマット文字列からなるカスタムの失敗メッセージを与え、 テスト関数を変更しましょう: #[test]\nfn greeting_contains_name() { let result = greeting(\"Carol\"); assert!( result.contains(\"Carol\"), //挨拶は名前を含んでいません。値は`{}`でした \"Greeting did not contain name, value was `{}`\", result );\n} これでテストを実行したら、より有益なエラーメッセージが得られるでしょう: ---- tests::greeting_contains_name stdout ---- thread 'tests::greeting_contains_name' panicked at 'Greeting did not\ncontain name, value was `Hello!`', src/lib.rs:12:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. 実際に得られた値がテスト出力に見られ、起こると想定していたものではなく、 起こったものをデバッグするのに役に立ちます。","breadcrumbs":"テスト » カスタムの失敗メッセージを追加する","id":"ch11-01-writing-tests.html#aカスタムの失敗メッセージを追加する","title":"カスタムの失敗メッセージを追加する"},"ch11-01-writing-tests.html#aテストの記述法":{"body":"テストは、非テストコードが想定された方法で機能していることを実証するRustの関数です。 テスト関数の本体は、典型的には以下の3つの動作を行います: 必要なデータや状態をセットアップする。 テスト対象のコードを走らせる。 結果が想定通りかアサーションする。 Rustが、特にこれらの動作を行うテストを書くために用意している機能を見ていきましょう。 これには、 test 属性、いくつかのマクロ、 should_panic 属性が含まれます。","breadcrumbs":"テスト » テストの記述法","id":"ch11-01-writing-tests.html#aテストの記述法","title":"テストの記述法"},"ch11-01-writing-tests.html#aテスト関数の解剖":{"body":"最も単純には、Rustにおけるテストは test 属性で注釈された関数のことです。属性とは、 Rustコードの欠片に関するメタデータです; 一例を挙げれば、構造体とともに第5章で使用した derive 属性です。 関数をテスト関数に変えるには、 fn の前に #[test] を付け加えるのです。 cargo test コマンドでテストを実行したら、コンパイラは test 属性で注釈された関数を走らせるテスト用バイナリをビルドし、 各テスト関数が通過したか失敗したかを報告します。 第7章で、Cargoで新規ライブラリプロジェクトを作成した時に、テスト関数が含まれるテストモジュールが自動で生成されたことを見かけました。 このモジュールのおかげでテストを書き始めることができるので、新しいプロジェクトを立ち上げる度に、 テスト関数の正確な構造と記法を調べる必要がなくなるわけです。必要なだけ追加のテスト関数とテストモジュールは追記することができます。 実際にテストすることなしにテンプレートのテストが生成されるのを実験することでテストの動作法の一部の側面を探究しましょう。 それから、自分で書いた何らかのコードを呼び出し、振る舞いが正しいかアサーションする現実世界のテストを書きましょう。 adder という新しいライブラリプロジェクトを生成しましょう: $ cargo new adder --lib Created library `adder` project\n$ cd adder adder ライブラリの src/lib.rs ファイルの中身は、リスト11-1のような見た目のはずです。 ファイル名: src/lib.rs # fn main() {}\n#[cfg(test)]\nmod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); }\n} リスト11-1: cargo new で自動生成されたテストモジュールと関数 とりあえず、最初の2行は無視し、関数に集中してその動作法を見ましょう。 fn 行の #[test] 注釈に注目してください: この属性は、これがテスト関数であることを示唆しますので、 テスト実行機はこの関数をテストとして扱うとわかるのです。さらに、 tests モジュール内には非テスト関数を入れ込み、 一般的なシナリオをセットアップしたり、共通の処理を行う手助けをしたりもできるので、 #[test] 属性でどの関数がテストかを示唆する必要があるのです。 関数本体は、 assert_eq! マクロを使用して、2 + 2が4に等しいことをアサーションしています。 このアサーションは、典型的なテストのフォーマット例をなしているわけです。走らせてこのテストが通ることを確かめましょう。 cargo test コマンドでプロジェクトにあるテストが全て実行されます。リスト11-2に示したようにですね。 $ cargo test Compiling adder v0.1.0 (file:///projects/adder) Finished dev [unoptimized + debuginfo] target(s) in 0.22 secs Running target/debug/deps/adder-ce99bcc2479f4607 running 1 test\ntest tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Doc-tests adder running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out リスト11-2: 自動生成されたテストを走らせた出力 Cargoがテストをコンパイルし、走らせました。 Compiling , Finished , Running の行の後に running 1 test の行があります。 次行が、生成されたテスト関数の it_works という名前とこのテストの実行結果、 ok を示しています。 テスト実行の総合的なまとめが次に出現します。 test result:ok. というテキストは、 全テストが通ったことを意味し、 1 passed; 0 failed と読める部分は、通過または失敗したテストの数を合計しているのです。 無視すると指定したテストは何もなかったため、まとめは 0 ignored と示しています。 また、実行するテストにフィルタをかけもしなかったので、まとめの最後に 0 filtered out と表示されています。 テストを無視することとフィルタすることに関しては次の節、「テストの実行され方を制御する」で語ります。 0 measured という統計は、パフォーマンスを測定するベンチマークテスト用です。 ベンチマークテストは、本書記述の時点では、ナイトリ版のRustでのみ利用可能です。 詳しくは、 ベンチマークテストのドキュメンテーション を参照されたし。 テスト出力の次の部分、つまり Doc-tests adder で始まる部分は、ドキュメンテーションテストの結果用のものです。 まだドキュメンテーションテストは何もないものの、コンパイラは、APIドキュメントに現れたどんなコード例もコンパイルできます。 この機能により、ドキュメントとコードを同期することができるわけです。ドキュメンテーションテストの書き方については、 第14章の「テストとしてのドキュメンテーションコメント」節で議論しましょう。今は、 Doc-tests 出力は無視します。 テストの名前を変更してどうテスト出力が変わるか確かめましょう。 it_works 関数を違う名前、 exploration などに変えてください。 そう、以下のように: ファイル名: src/lib.rs # fn main() {}\n#[cfg(test)]\nmod tests { #[test] fn exploration() { assert_eq!(2 + 2, 4); }\n} そして、 cargo test を再度走らせます。これで出力が it_works の代わりに exploration と表示しています: running 1 test\ntest tests::exploration ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 別のテストを追加しますが、今回は失敗するテストにしましょう!テスト関数内の何かがパニックすると、 テストは失敗します。各テストは、新規スレッドで実行され、メインスレッドが、テストスレッドが死んだと確認した時、 テストは失敗と印づけられます。第9章でパニックを引き起こす最も単純な方法について語りました。 要するに、 panic! マクロを呼び出すことです。 src/lib.rs ファイルがリスト11-3のような見た目になるよう、 新しいテスト another を入力してください。 ファイル名: src/lib.rs # fn main() {}\n#[cfg(test)]\nmod tests { #[test] fn exploration() { assert_eq!(2 + 2, 4); } #[test] fn another() { //このテストを失敗させる panic!(\"Make this test fail\"); }\n} リスト11-3: panic! マクロを呼び出したために失敗する2番目のテストを追加する cargo test で再度テストを走らせてください。出力はリスト11-4のようになるはずであり、 exploration テストは通り、 another は失敗したと表示されます。 running 2 tests\ntest tests::exploration ... ok\ntest tests::another ... FAILED failures: ---- tests::another stdout ---- thread 'tests::another' panicked at 'Make this test fail', src/lib.rs:10:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. failures: tests::another test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out error: test failed リスト11-4: 一つのテストが通り、一つが失敗するときのテスト結果 ok の代わりに test test::another の行は、 FAILED を表示しています。個々の結果とまとめの間に、 2つ新たな区域ができました: 最初の区域は、失敗したテスト各々の具体的な理由を表示しています。 今回の場合、 another は 'Make this test fail'でパニックした ために失敗し、 これは、 src/lib.rs ファイルの10行で起きました。次の区域は失敗したテストの名前だけを列挙しています。 これは、テストがたくさんあり、失敗したテストの詳細がたくさん表示されるときに有用になります。 失敗したテストの名前を使用してそのテストだけを実行し、より簡単にデバッグすることができます。 テストの実行方法については、「テストの実行され方を制御する」節でもっと語りましょう。 サマリー行が最後に出力されています: 総合的に言うと、テスト結果は 失敗 でした。 一つのテストが通り、一つが失敗したわけです。 異なる筋書きでのテスト結果がどんな風になるか見てきたので、テストを行う際に有用になる panic! 以外のマクロに目を向けましょう。","breadcrumbs":"テスト » テスト関数の解剖","id":"ch11-01-writing-tests.html#aテスト関数の解剖","title":"テスト関数の解剖"},"ch11-01-writing-tests.html#should_panic-でパニックを確認する":{"body":"期待する正しい値をコードが返すことを確認することに加えて、想定通りにコードがエラー状態を扱っていることを確認するのも重要です。 例えば、第9章のリスト9-9で生成した Guess 型を考えてください。 Guess を使用する他のコードは、 Guess のインスタンスは1から100の範囲の値しか含まないという保証に依存しています。 その範囲外の値で Guess インスタンスを生成しようとするとパニックすることを確認するテストを書くことができます。 これは、テスト関数に should_panic という別の属性を追加することで達成できます。 この属性は、関数内のコードがパニックしたら、テストを通過させます。つまり、 関数内のコードがパニックしなかったら、テストは失敗するわけです。 リスト11-8は、予想した時に Guess::new のエラー条件が発生していることを確認するテストを示しています。 ファイル名: src/lib.rs # fn main() {}\npub struct Guess { value: u32,\n} impl Guess { pub fn new(value: u32) -> Guess { if value < 1 || value > 100 { //予想値は1から100の間でなければなりません panic!(\"Guess value must be between 1 and 100, got {}.\", value); } Guess { value } }\n} #[cfg(test)]\nmod tests { use super::*; #[test] #[should_panic] fn greater_than_100() { Guess::new(200); }\n} リスト11-8: 状況が panic! を引き起こすとテストする #[test] 属性の後、適用するテスト関数の前に #[should_panic] 属性を配置しています。 このテストが通るときの結果を見ましょう: running 1 test\ntest tests::greater_than_100 ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out よさそうですね!では、値が100より大きいときに new 関数がパニックするという条件を除去することでコードにバグを導入しましょう: # fn main() {}\n# pub struct Guess {\n# value: u32,\n# }\n#\n// --snip-- impl Guess { pub fn new(value: u32) -> Guess { if value < 1 { panic!(\"Guess value must be between 1 and 100, got {}.\", value); } Guess { value } }\n} リスト11-8のテストを実行すると、失敗するでしょう: running 1 test\ntest tests::greater_than_100 ... FAILED failures: failures: tests::greater_than_100 test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out この場合、それほど役に立つメッセージは得られませんが、テスト関数に目を向ければ、 #[should_panic] で注釈されていることがわかります。得られた失敗は、 テスト関数のコードがパニックを引き起こさなかったことを意味するのです。 should_panic を使用するテストは不正確なこともあります。なぜなら、コードが何らかのパニックを起こしたことしか示さないからです。 should_panic のテストは、起きると想定していたもの以外の理由でテストがパニックしても通ってしまうのです。 should_panic のテストの正確を期すために、 should_panic 属性の省略可能な expected 引数を追加できます。 このテストの拘束具が、失敗メッセージに与えられたテキストが含まれていることを確かめてくれるでしょう。 例えば、リスト11-9の Guess の変更されたコードを考えてください。ここでは、 new 関数は、値の大小によって異なるメッセージでパニックします。 ファイル名: src/lib.rs # fn main() {}\n# pub struct Guess {\n# value: u32,\n# }\n#\n// --snip-- impl Guess { pub fn new(value: u32) -> Guess { if value < 1 { //予想値は、1以上でなければなりませんが、{}でした panic!(\"Guess value must be greater than or equal to 1, got {}.\", value); } else if value > 100 { //予想値は100以下でなければなりませんが、{}でした panic!(\"Guess value must be less than or equal to 100, got {}.\", value); } Guess { value } }\n} #[cfg(test)]\nmod tests { use super::*; #[test] // 予想値は100以下でなければなりません #[should_panic(expected = \"Guess value must be less than or equal to 100\")] fn greater_than_100() { Guess::new(200); }\n} リスト11-9: 状況が特定のパニックメッセージで panic! を引き起こすことをテストする should_panic 属性の expected 引数に置いた値が Guess::new 関数がパニックしたメッセージの一部になっているので、 このテストは通ります。予想されるパニックメッセージ全体を指定することもでき、今回の場合、 Guess value must be less than or equal to 100, got 200. となります。 should_panic の予想される引数に指定すると決めたものは、パニックメッセージの固有性や活動性、 テストの正確性によります。今回の場合、パニックメッセージの一部でも、テスト関数内のコードが、 else if value > 100 ケースを実行していると確認するのに事足りるのです。 expected メッセージありの should_panic テストが失敗すると何が起きるのが確かめるために、 if value < 1 と else if value > 100 ブロックの本体を入れ替えることで再度コードにバグを仕込みましょう: if value < 1 { panic!(\"Guess value must be less than or equal to 100, got {}.\", value);\n} else if value > 100 { panic!(\"Guess value must be greater than or equal to 1, got {}.\", value);\n} should_panic テストを実行すると、今回は失敗するでしょう: running 1 test\ntest tests::greater_than_100 ... FAILED failures: ---- tests::greater_than_100 stdout ---- thread 'tests::greater_than_100' panicked at 'Guess value must be\ngreater than or equal to 1, got 200.', src/lib.rs:11:12\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\nnote: Panic did not include expected string 'Guess value must be less than or\nequal to 100'\n(注釈: パニックには'Guess value must be less than or equal to 100'という予想される文字列が含まれませんでした) failures: tests::greater_than_100 test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out この失敗メッセージは、このテストが確かにまさしく予想通りパニックしたことを示唆していますが、 パニックメッセージは、予想される文字列の 'Guess value must be less than or equal to 100' を含んでいませんでした。 実際に得られたパニックメッセージは今回の場合、 Guess value must be greater than or equal to 1, got 200 でした。 そうしてバグの所在地を割り出し始めることができるわけです! 今やテスト記法を複数知ったので、テストを走らせる際に起きていることに目を向け、 cargo test で使用できるいろんなオプションを探究しましょう。","breadcrumbs":"テスト » should_panic でパニックを確認する","id":"ch11-01-writing-tests.html#should_panic-でパニックを確認する","title":"should_panic でパニックを確認する"},"ch11-02-running-tests.html#aテストの実行され方を制御する":{"body":"cargo run がコードをコンパイルし、出来上がったバイナリを走らせるのと全く同様に、 cargo test はコードをテストモードでコンパイルし、出来上がったテストバイナリを実行します。 コマンドラインオプションを指定して cargo test の既定動作を変更することができます。 例えば、 cargo test で生成されるバイナリの既定動作は、テストを全て並行に実行し、 テスト実行中に生成された出力をキャプチャして出力が表示されるのを防ぎ、 テスト結果に関係する出力を読みやすくすることです。 コマンドラインオプションの中には cargo test にかかるものや、出来上がったテストバイナリにかかるものがあります。 この2種の引数を区別するために、 cargo test にかかる引数を -- という区分記号の後に列挙し、 それからテストバイナリにかかる引数を列挙します。 cargo test --help を走らせると、 cargo test で使用できるオプションが表示され、 cargo test -- --help を走らせると、 -- という区分記号の後に使えるオプションが表示されます。","breadcrumbs":"テスト » テストの実行され方を制御する","id":"ch11-02-running-tests.html#aテストの実行され方を制御する","title":"テストの実行され方を制御する"},"ch11-02-running-tests.html#aテストを並行または連続して実行する":{"body":"複数のテストを実行するとき、標準では、スレッドを使用して並行に走ります。これはつまり、 テストが早く実行し終わり、コードが機能しているいかんにかかわらず、反応をより早く得られることを意味します。 テストは同時に実行されているので、テストが相互や共有された環境を含む他の共通の状態に依存してないことを確かめてください。 現在の作業対象ディレクトリや環境変数などですね。 例えば、各テストがディスクに test_output.txt というファイルを作成し、何らかのデータを書き込むコードを走らせるとしてください。 そして、各テストはそのファイルのデータを読み取り、ファイルが特定の値を含んでいるとアサーションし、 その値は各テストで異なります。テストが同時に走るので、あるテストが、 他のテストが書き込んだり読み込んだりする間隙にファイルを上書きするかもしれません。 それから2番目のテストが失敗します。コードが不正だからではなく、 並行に実行されている間にテストがお互いに邪魔をしてしまったせいです。 各テストが異なるファイルに書き込むことを確かめるのが一つの解決策です; 別の解決策では、 一度に一つのテストを実行します。 並行にテストを実行したくなかったり、使用されるスレッド数をよりきめ細かく制御したい場合、 --test-threads フラグと使用したいスレッド数をテストバイナリに送ることができます。 以下の例に目を向けてください: $ cargo test -- --test-threads=1 テストスレッドの数を 1 にセットし、並行性を使用しないようにプログラムに指示しています。 1スレッドのみを使用してテストを実行すると、並行に実行するより時間がかかりますが、 状態を共有していても、お互いに邪魔をすることはありません。","breadcrumbs":"テスト » テストを並行または連続して実行する","id":"ch11-02-running-tests.html#aテストを並行または連続して実行する","title":"テストを並行または連続して実行する"},"ch11-02-running-tests.html#a名前でテストの一部を実行する":{"body":"時々、全テストを実行すると時間がかかってしまうことがあります。特定の部分のコードしか対象にしていない場合、 そのコードに関わるテストのみを走らせたいかもしれません。 cargo test に走らせたいテストの名前を引数として渡すことで、 実行するテストを選ぶことができます。 テストの一部を走らせる方法を模擬するために、リスト11-11に示したように、 add_two 関数用に3つテストを作成し、走らせるテストを選択します。 ファイル名: src/lib.rs pub fn add_two(a: i32) -> i32 { a + 2\n} #[cfg(test)]\nmod tests { use super::*; #[test] fn add_two_and_two() { assert_eq!(4, add_two(2)); } #[test] fn add_three_and_two() { assert_eq!(5, add_two(3)); } #[test] fn one_hundred() { assert_eq!(102, add_two(100)); }\n} リスト11-11: 異なる名前の3つのテスト 以前見かけたように、引数なしでテストを走らせたら、全テストが並行に走ります: running 3 tests\ntest tests::add_two_and_two ... ok\ntest tests::add_three_and_two ... ok\ntest tests::one_hundred ... ok test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 単独のテストを走らせる あらゆるテスト関数の名前を cargo test に渡して、そのテストのみを実行することができます: $ cargo test one_hundred Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running target/debug/deps/adder-06a75b4a1f2515e9 running 1 test\ntest tests::one_hundred ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 2 filtered out one_hundred という名前のテストだけが走りました; 他の2つのテストはその名前に合致しなかったのです。 まとめ行の最後に 2 filtered out と表示することでテスト出力は、このコマンドが走らせた以上のテストがあることを知らせてくれています。 この方法では、複数のテストの名前を指定することはできません; cargo test に与えられた最初の値のみが使われるのです。 ですが、複数のテストを走らせる方法もあります。 複数のテストを実行するようフィルターをかける テスト名の一部を指定でき、その値に合致するあらゆるテストが走ります。例えば、 我々のテストの2つが add という名前を含むので、 cargo test add を実行することで、その二つを走らせることができます: $ cargo test add Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running target/debug/deps/adder-06a75b4a1f2515e9 running 2 tests\ntest tests::add_two_and_two ... ok\ntest tests::add_three_and_two ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out このコマンドは名前に add を含むテストを全て実行し、 one_hundred という名前のテストを除外しました。 また、テストが出現するモジュールがテスト名の一部になっていて、 モジュール名でフィルターをかけることで、あるモジュール内のテスト全てを実行できることに注目してください。","breadcrumbs":"テスト » 名前でテストの一部を実行する","id":"ch11-02-running-tests.html#a名前でテストの一部を実行する","title":"名前でテストの一部を実行する"},"ch11-02-running-tests.html#a特に要望のない限りテストを無視する":{"body":"時として、いくつかの特定のテストが実行するのに非常に時間がかかることがあり、 cargo test の実行のほとんどで除外したくなるかもしれません。引数として確かに実行したいテストを全て列挙するのではなく、 ここに示したように代わりに時間のかかるテストを ignore 属性で除外すると注釈することができます。 ファイル名: src/lib.rs #[test]\nfn it_works() { assert_eq!(2 + 2, 4);\n} #[test]\n#[ignore]\nfn expensive_test() { // 実行に1時間かかるコード // code that takes an hour to run\n} #[test] の後の除外したいテストに #[ignore] 行を追加しています。これで、 テストを実行したら、 it_works は実行されるものの、 expensive_test は実行されません: $ cargo test Compiling adder v0.1.0 (file:///projects/adder) Finished dev [unoptimized + debuginfo] target(s) in 0.24 secs Running target/debug/deps/adder-ce99bcc2479f4607 running 2 tests\ntest expensive_test ... ignored\ntest it_works ... ok test result: ok. 1 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out expensive_test 関数は、 ignored と列挙されています。無視されるテストのみを実行したかったら、 cargo test -- --ignored を使うことができます: $ cargo test -- --ignored Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running target/debug/deps/adder-ce99bcc2479f4607 running 1 test\ntest expensive_test ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 1 filtered out どのテストを走らせるか制御することで、結果が早く出ることを確かめることができるのです。 ignored テストの結果を確認することが道理に合い、結果を待つだけの時間ができたときに、 代わりに cargo test -- --ignored を走らせることができます。","breadcrumbs":"テスト » 特に要望のない限りテストを無視する","id":"ch11-02-running-tests.html#a特に要望のない限りテストを無視する","title":"特に要望のない限りテストを無視する"},"ch11-02-running-tests.html#a関数の出力を表示する":{"body":"標準では、テストが通ると、Rustのテストライブラリは標準出力に出力されたものを全てキャプチャします。例えば、 テストで println! を呼び出してテストが通ると、 println! の出力は、端末に表示されません; テストが通ったことを示す行しか見られないでしょう。テストが失敗すれば、 残りの失敗メッセージと共に、標準出力に出力されたものが全て見えるでしょう。 例として、リスト11-10は引数の値を出力し、10を返す馬鹿げた関数と通過するテスト1つ、失敗するテスト1つです。 ファイル名: src/lib.rs fn prints_and_returns_10(a: i32) -> i32 { //{}という値を得た println!(\"I got the value {}\", a); 10\n} #[cfg(test)]\nmod tests { use super::*; #[test] fn this_test_will_pass() { let value = prints_and_returns_10(4); assert_eq!(10, value); } #[test] fn this_test_will_fail() { let value = prints_and_returns_10(8); assert_eq!(5, value); }\n} リスト11-10: println! を呼び出す関数用のテスト これらのテストを cargo test で実行すると、以下のような出力を目の当たりにするでしょう: running 2 tests\ntest tests::this_test_will_pass ... ok\ntest tests::this_test_will_fail ... FAILED failures: ---- tests::this_test_will_fail stdout ---- I got the value 8\nthread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)` left: `5`, right: `10`', src/lib.rs:19:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. failures: tests::this_test_will_fail test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out この出力のどこにも I got the value 4 と表示されていないことに注意してください。 これは、テストに合格した場合に出力されるものです。 その出力はキャプチャされてしまいました。 失敗したテストのからの出力 I got the value 8 がテストサマリー出力のセクションに表示され、テストが失敗した原因も示されます。 通過するテストについても出力される値が見たかったら、出力キャプチャ機能を --nocapture フラグで無効化することができます: $ cargo test -- --nocapture リスト11-10のテストを --nocapture フラグと共に再度実行したら、以下のような出力を目の当たりにします: running 2 tests\nI got the value 4\nI got the value 8\ntest tests::this_test_will_pass ... ok\nthread 'tests::this_test_will_fail' panicked at 'assertion failed: `(left == right)` left: `5`, right: `10`', src/lib.rs:19:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace.\ntest tests::this_test_will_fail ... FAILED failures: failures: tests::this_test_will_fail test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out テスト用の出力とテスト結果の出力がまぜこぜになっていることに注意してください; その理由は、前節で語ったようにテストが並行に実行されているからです。 -test-threads=1 オプションと --nocapture フラグを使ってみて、 その時、出力がどうなるか確かめてください!","breadcrumbs":"テスト » 関数の出力を表示する","id":"ch11-02-running-tests.html#a関数の出力を表示する","title":"関数の出力を表示する"},"ch11-03-test-organization.html#aまとめ":{"body":"Rustのテスト機能は、変更を加えた後でさえ想定通りにコードが機能し続けることを保証して、 コードが機能すべき方法を指定する手段を提供します。単体テストはライブラリの異なる部分を個別に用い、 非公開の実装詳細をテストすることができます。結合テストは、ライブラリのいろんな部分が共同で正常に動作することを確認し、 ライブラリの公開APIを使用して外部コードが使用するのと同じ方法でコードをテストします。 Rustの型システムと所有権ルールにより防がれるバグの種類もあるものの、それでもテストは、 コードが振る舞うと予想される方法に関するロジックのバグを減らすのに重要なのです。 この章と以前の章で学んだ知識を結集して、とあるプロジェクトに取り掛かりましょう!","breadcrumbs":"テスト » まとめ","id":"ch11-03-test-organization.html#aまとめ","title":"まとめ"},"ch11-03-test-organization.html#aテストの体系化":{"body":"章の初めで触れたように、テストは複雑な鍛錬であり、人によって専門用語や体系化が異なります。 Rustのコミュニティでは、テストを2つの大きなカテゴリで捉えています: 単体テスト と 結合テスト です。 単体テストは小規模でより集中していて、個別に1回に1モジュールをテストし、非公開のインターフェイスもテストすることがあります。 結合テストは、完全にライブラリ外になり、他の外部コード同様に自分のコードを使用し、公開インターフェイスのみ使用し、 1テストにつき複数のモジュールを用いることもあります。 どちらのテストを書くのも、ライブラリの一部が個別かつ共同でしてほしいことをしていることを確認するのに重要なのです。","breadcrumbs":"テスト » テストの体系化","id":"ch11-03-test-organization.html#aテストの体系化","title":"テストの体系化"},"ch11-03-test-organization.html#a単体テスト":{"body":"単体テストの目的は、残りのコードから切り離して各単位のコードをテストし、 コードが想定通り、動いたり動いていなかったりする箇所を迅速に特定することです。 単体テストは、テスト対象となるコードと共に、 src ディレクトリの各ファイルに置きます。 慣習は、各ファイルに tests という名前のモジュールを作り、テスト関数を含ませ、 そのモジュールを cfg(test) で注釈することです。 テストモジュールと #[cfg(test)] testsモジュールの #[cfg(test)] という注釈は、コンパイラに cargo build を走らせた時ではなく、 cargo test を走らせた時にだけ、 テストコードをコンパイルし走らせるよう指示します。これにより、ライブラリをビルドしたいだけの時にはコンパイルタイムを節約し、 テストが含まれないので、コンパイル後の成果物のサイズも節約します。結合テストは別のディレクトリに存在することになるので、 #[cfg(test)] 注釈は必要ないとわかるでしょう。しかしながら、単体テストはコードと同じファイルに存在するので、 #[cfg(test)] を使用してコンパイル結果に含まれないよう指定するのです。 この章の最初の節で新しい adder プロジェクトを生成した時に、Cargoがこのコードも生成してくれたことを思い出してください: ファイル名: src/lib.rs #[cfg(test)]\nmod tests { #[test] fn it_works() { assert_eq!(2 + 2, 4); }\n} このコードが自動生成されたテストモジュールです。 cfg という属性は、 configuration を表していて、 コンパイラに続く要素が、ある特定の設定オプションを与えられたら、含まれるように指示します。 今回の場合、設定オプションは、 test であり、言語によって提供されているテストをコンパイルし、 走らせるためのものです。 cfg 属性を使用することで、 cargo test で積極的にテストを実行した場合のみ、 Cargoがテストコードをコンパイルします。これには、このモジュールに含まれるかもしれないヘルパー関数全ても含まれ、 #[test] で注釈された関数だけにはなりません。 非公開関数をテストする テストコミュニティ内で非公開関数を直接テストするべきかについては議論があり、 他の言語では非公開関数をテストするのは困難だったり、不可能だったりします。 あなたがどちらのテストイデオロギーを支持しているかに関わらず、Rustの公開性規則により、 非公開関数をテストすることが確かに可能です。リスト11-12の非公開関数 internal_adder を含むコードを考えてください。 ファイル名: src/lib.rs pub fn add_two(a: i32) -> i32 { internal_adder(a, 2)\n} fn internal_adder(a: i32, b: i32) -> i32 { a + b\n} #[cfg(test)]\nmod tests { use super::*; #[test] fn internal() { assert_eq!(4, internal_adder(2, 2)); }\n} リスト11-12: 非公開関数をテストする internal_adder 関数は pub とマークされていないものの、テストも単なるRustのコードであり、 tests モジュールもただのモジュールでしかないので、テスト内で internal_adder を普通にインポートし呼び出すことができます。 非公開関数はテストするべきではないとお考えなら、Rustにはそれを強制するものは何もありません。","breadcrumbs":"テスト » 単体テスト","id":"ch11-03-test-organization.html#a単体テスト","title":"単体テスト"},"ch11-03-test-organization.html#a結合テスト":{"body":"Rustにおいて、結合テストは完全にライブラリ外のものです。他のコードと全く同様にあなたのライブラリを使用するので、 ライブラリの公開APIの一部である関数しか呼び出すことはできません。その目的は、 ライブラリのいろんな部分が共同で正常に動作しているかをテストすることです。 単体では正常に動くコードも、結合した状態だと問題を孕む可能性もあるので、 結合したコードのテストの範囲も同様に重要になるのです。結合テストを作成するには、 まず tests ディレクトリが必要になります。 tests ディレクトリ プロジェクトディレクトリのトップ階層、 src の隣に tests ディレクトリを作成します。 Cargoは、このディレクトリに結合テストのファイルを探すことを把握しています。 そして、このディレクトリ内にいくらでもテストファイルを作成することができ、 Cargoはそれぞれのファイルを個別のクレートとしてコンパイルします。 結合テストを作成しましょう。リスト11-12のコードが src/lib.rs ファイルにあるまま、 tests ディレクトリを作成し、 tests/integration_test.rs という名前の新しいファイルを生成し、 リスト11-13のコードを入力してください。 ファイル名: tests/integration_test.rs extern crate adder; #[test]\nfn it_adds_two() { assert_eq!(4, adder::add_two(2));\n} リスト11-13: adder クレートの関数の結合テスト コードの頂点に extern crate adder を追記しましたが、これは単体テストでは必要なかったものです。 理由は、 tests ディレクトリのテストはそれぞれ個別のクレートであるため、 各々ライブラリをインポートする必要があるためです。 tests/integration_test.rs のどんなコードも #[cfg(test)] で注釈する必要はありません。 Cargoは tests ディレクトリを特別に扱い、 cargo test を走らせた時にのみこのディレクトリのファイルをコンパイルするのです。 さあ、 cargo test を実行してください: $ cargo test Compiling adder v0.1.0 (file:///projects/adder) Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs Running target/debug/deps/adder-abcabcabc running 1 test\ntest tests::internal ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Running target/debug/deps/integration_test-ce99bcc2479f4607 running 1 test\ntest it_adds_two ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Doc-tests adder running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 3つの区域の出力が単体テスト、結合テスト、ドックテストを含んでいます。単体テスト用の最初の区域は、 今まで見てきたものと同じです: 各単体テストに1行(リスト11-12で追加した internal という名前のもの)と、 単体テストのサマリー行です。 結合テストの区域は、 Running target/debug/deps/integration-test-ce99bcc2479f4607 という行で始まっています(最後のハッシュはあなたの出力とは違うでしょう)。 次に、この結合テストの各テスト関数用の行があり、 Doc-tests adder 区域が始まる直前に、 結合テストの結果用のサマリー行があります。 単体テスト関数を追加することで単体テスト区域のテスト結果の行が増えたように、 作成した結合テストファイルにテスト関数を追加することでそのファイルの区域に結果の行が増えることになります。 結合テストファイルはそれぞれ独自の区域があるため、 tests ディレクトリにさらにファイルを追加すれば、 結合テストの区域が増えることになるでしょう。 それでも、テスト関数の名前を引数として cargo test に指定することで、特定の結合テスト関数を走らせることができます。 特定の結合テストファイルにあるテストを全て走らせるには、 cargo test に --test 引数、 その後にファイル名を続けて使用してください: $ cargo test --test integration_test Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running target/debug/integration_test-952a27e0126bb565 running 1 test\ntest it_adds_two ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out このコマンドは、 tests/integration_test.rs ファイルにあるテストのみを実行します。 結合テスト内のサブモジュール 結合テストを追加するにつれて、 tests ディレクトリに2つ以上のファイルを作成して体系化したくなるかもしれません; 例えば、テスト対象となる機能でテスト関数をグループ化することができます。前述したように、 tests ディレクトリの各ファイルは、個別のクレートとしてコンパイルされます。 各結合テストファイルをそれ自身のクレートとして扱うと、 エンドユーザがあなたのクレートを使用するかのように個別のスコープを生成するのに役立ちます。 ですが、これは tests ディレクトリのファイルが、コードをモジュールとファイルに分ける方法に関して第7章で学んだように、 src のファイルとは同じ振る舞いを共有しないことを意味します。 tests ディレクトリのファイルの異なる振る舞いは、複数の結合テストファイルで役に立ちそうなヘルパー関数ができ、 第7章の「モジュールを別のファイルに移動する」節の手順に従って共通モジュールに抽出しようとした時に最も気付きやすくなります。 例えば、 tests/common.rs を作成し、そこに setup という名前の関数を配置したら、 複数のテストファイルの複数のテスト関数から呼び出したい setup に何らかのコードを追加することができます: ファイル名: tests/common.rs pub fn setup() { // ここにライブラリテスト固有のコードが来る // setup code specific to your library's tests would go here\n} 再度テストを実行すると、 common.rs ファイルは何もテスト関数を含んだり、 setup 関数をどこかから呼んだりしてないのに、 テスト出力に common.rs 用の区域が見えるでしょう。 running 1 test\ntest tests::internal ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Running target/debug/deps/common-b8b07b6f1be2db70 running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Running target/debug/deps/integration_test-d993c68b431d39df running 1 test\ntest it_adds_two ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Doc-tests adder running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out common が running 0 tests とテスト結果に表示されるのは、望んだ結果ではありません。 ただ単に他の結合テストファイルと何らかのコードを共有したかっただけです。 common がテスト出力に出現するのを防ぐには、 tests/common.rs を作成する代わりに、 tests/common/mod.rs を作成します。第7章の「モジュールファイルシステムの規則」節において、 module_name/mod.rs という命名規則をサブモジュールのあるモジュールのファイルに使用しました。 ここでは common にサブモジュールはありませんが、 このように命名することでコンパイラに common モジュールを結合テストファイルとして扱わないように指示します。 setup 関数のコードを tests/common/mod.rs に移動し、 tests/common.rs ファイルを削除すると、 テスト出力に区域はもう表示されなくなります。 tests ディレクトリのサブディレクトリ内のファイルは個別クレートとしてコンパイルされたり、 テスト出力に区域が表示されることがないのです。 tests/common/mod.rs を作成した後、それをどの結合テストファイルからもモジュールとして使用することができます。 こちらは、 tests/integration_test.rs 内の it_adds_two テストから setup 関数を呼び出す例です: ファイル名: tests/integration_test.rs extern crate adder; mod common; #[test]\nfn it_adds_two() { common::setup(); assert_eq!(4, adder::add_two(2));\n} mod common; という宣言は、リスト7-4で模擬したモジュール宣言と同じであることに注意してください。 それから、テスト関数内で common::setup() 関数を呼び出すことができます。 バイナリクレート用の結合テスト もしもプロジェクトが src/main.rs ファイルのみを含み、 src/lib.rs ファイルを持たないバイナリクレートだったら、 tests ディレクトリに結合テストを作成し、 extern crate を使用して src/main.rs ファイルに定義された関数をインポートすることはできません。 ライブラリクレートのみが、他のクレートが呼び出して使用できる関数を晒せるのです; バイナリクレートはそれ単体で実行することを意味しています。 これは、バイナリを提供するRustのプロジェクトに、 src/lib.rs ファイルに存在するロジックを呼び出す単純な src/main.rs ファイルがある一因になっています。 この構造を使用して結合テストは、 extern crate を使用して重要な機能を用いることでライブラリクレートをテストすることが できます 。 この重要な機能が動作すれば、 src/main.rs ファイルの少量のコードも動作し、その少量のコードはテストする必要がないわけです。","breadcrumbs":"テスト » 結合テスト","id":"ch11-03-test-organization.html#a結合テスト","title":"結合テスト"},"ch12-00-an-io-project.html#a入出力プロジェクト-コマンドラインプログラムを構築する":{"body":"この章は、ここまでに学んできた多くのスキルを思い出すきっかけであり、もういくつか標準ライブラリの機能も探究します。 ファイルやコマンドラインの入出力と相互作用するコマンドラインツールを構築し、 今やあなたの支配下にあるRustの概念の一部を練習していきます。 Rustの速度、安全性、単バイナリ出力、クロスプラットフォームサポートにより、コマンドラインツールを作るのにふさわしい言語なので、 このプロジェクトでは、独自の伝統的なコマンドラインツールの grep ( g lobally search a r egular e xpression and p rint: 正規表現をグローバルで検索し表示する)を作成していきます。最も単純な使用法では、 grep は指定したファイルから指定した文字列を検索します。そうするには、 grep は引数としてファイル名と文字列を受け取ります。それからファイルを読み込んでそのファイル内で文字列引数を含む行を探し、 検索した行を出力するのです。 その過程で、多くのコマンドラインツールが使用している端末の機能を使用させる方法を示します。 環境変数の値を読み取ってユーザがこのツールの振る舞いを設定できるようにします。また、 標準出力( stdout )の代わりに、標準エラーに出力( stderr )するので、例えば、 ユーザはエラーメッセージは画面上で確認しつつ、成功した出力はファイルにリダイレクトできます。 Rustコミュニティのあるメンバであるアンドリュー・ガラント(Andrew Gallant)が既に全機能装備の非常に高速な grep 、 ripgrep と呼ばれるものを作成しました。比較対象として、我々の grep はとても単純ですが、 この章により、 ripgrep のような現実世界のプロジェクトを理解するのに必要な背景知識の一部を身に付けられるでしょう。 この grep プロジェクトは、ここまでに学んできた多くの概念を集結させます: コードを体系化する(モジュール、第7章で学んだことを使用) ベクタと文字列を使用する(コレクション、第8章) エラーを処理する(第9章) 適切な箇所でトレイトとライフタイムを使用する(第10章) テストを記述する(第11章) さらに、クロージャ、イテレータ、トレイトオブジェクトなど、第13章、17章で詳しく講義するものもちょっとだけ紹介します。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する","id":"ch12-00-an-io-project.html#a入出力プロジェクト-コマンドラインプログラムを構築する","title":"入出力プロジェクト: コマンドラインプログラムを構築する"},"ch12-01-accepting-command-line-arguments.html#args-関数と不正なユニコード":{"body":"引数のどれかが不正なユニコードを含んでいたら、 std::env::args はパニックすることに注意してください。 プログラムが不正なユニコードを含む引数を受け付ける必要があるなら、代わりに std::env::args_os を使用してください。 この関数は、 String 値ではなく、 OsString 値を生成するイテレータを返します。ここでは、 簡潔性のために std::env::args を使うことを選択しました。 なぜなら、 OsString 値はプラットフォームごとに異なり、 String 値に比べて取り扱いが煩雑だからです。 main の最初の行で env::args を呼び出し、そして即座に collect を使用して、 イテレータをイテレータが生成する値全てを含むベクタに変換しています。 collect 関数を使用して多くの種類のコレクションを生成することができるので、 args の型を明示的に注釈して文字列のベクタが欲しいのだと指定しています。Rustにおいて、 型を注釈しなければならない頻度は非常に少ないのですが、 collect はよく確かに注釈が必要になる一つの関数なのです。 コンパイラには、あなたが欲しているコレクションの種類が推論できないからです。 最後に、デバッグ整形機の :? を使用してベクタを出力しています。引数なしでコードを走らせてみて、 それから引数二つで試してみましょう: $ cargo run\n--snip--\n[\"target/debug/minigrep\"] $ cargo run needle haystack\n--snip--\n[\"target/debug/minigrep\", \"needle\", \"haystack\"] ベクタの最初の値は \"target/debug/minigrep\" であることに注目してください。これはバイナリの名前です。 これはCの引数リストの振る舞いと合致し、実行時に呼び出された名前をプログラムに使わせてくれるわけです。 メッセージで出力したり、プログラムを起動するのに使用されたコマンドラインエイリアスによってプログラムの振る舞いを変えたい場合に、 プログラム名にアクセスするのにしばしば便利です。ですが、この章の目的には、これを無視し、必要な二つの引数のみを保存します。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » args 関数と不正なユニコード","id":"ch12-01-accepting-command-line-arguments.html#args-関数と不正なユニコード","title":"args 関数と不正なユニコード"},"ch12-01-accepting-command-line-arguments.html#aコマンドライン引数を受け付ける":{"body":"いつものように、 cargo new で新しいプロジェクトを作りましょう。プロジェクトを minigrep と名付けて、 既に自分のシステムに存在するかもしれない grep ツールと区別しましょう。 最初の仕事は、 minigrep を二つの引数を受け付けるようにすることです: ファイル名と検索する文字列ですね。 つまり、 cargo run で検索文字列と検索を行うファイルへのパスと共にプログラムを実行できるようになりたいということです。 こんな感じにね: $ cargo run searchstring example-filename.txt 今現在は、 cargo new で生成されたプログラムは、与えた引数を処理できません。 Crates.io に存在する既存のライブラリには、 コマンドライン引数を受け付けるプログラムを書く手助けをしてくれるものもありますが、ちょうどこの概念を学んでいる最中なので、 この能力を自分で実装しましょう。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » コマンドライン引数を受け付ける","id":"ch12-01-accepting-command-line-arguments.html#aコマンドライン引数を受け付ける","title":"コマンドライン引数を受け付ける"},"ch12-01-accepting-command-line-arguments.html#a引数の値を変数に保存する":{"body":"引数のベクタの値を出力すると、プログラムはコマンドライン引数として指定された値にアクセスできることが説明されました。 さて、プログラムの残りを通して使用できるように、二つの引数の値を変数に保存する必要があります。 それをしているのがリスト12-2です。 ファイル名: src/main.rs use std::env; fn main() { let args: Vec = env::args().collect(); let query = &args[1]; let filename = &args[2]; // {}を探しています println!(\"Searching for {}\", query); // {}というファイルの中 println!(\"In file {}\", filename);\n} リスト12-2: クエリ引数とファイル名引数を保持する変数を生成 ベクタを出力した時に確認したように、プログラム名がベクタの最初の値、 args[0] を占めているので、 添え字 1 から始めます。 minigrep が取る最初の引数は、検索する文字列なので、 最初の引数への参照を変数 query に置きました。2番目の引数はファイル名でしょうから、 2番目の引数への参照は変数 filename に置きました。 一時的にこれらの変数の値を出力して、コードが意図通りに動いていることを証明しています。 再度このプログラムを test と sample.txt という引数で実行しましょう: $ cargo run test sample.txt Compiling minigrep v0.1.0 (file:///projects/minigrep) Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/minigrep test sample.txt`\nSearching for test\nIn file sample.txt 素晴らしい、プログラムは動作しています!必要な引数の値が、正しい変数に保存されています。後ほど、 何らかのエラー処理を加えて、ユーザが引数を提供しなかった場合など、可能性のある特定のエラー状況に対処します; 今は、そのような状況はないものとし、代わりにファイル読み取り能力を追加することに取り組みます。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » 引数の値を変数に保存する","id":"ch12-01-accepting-command-line-arguments.html#a引数の値を変数に保存する","title":"引数の値を変数に保存する"},"ch12-01-accepting-command-line-arguments.html#a引数の値を読み取る":{"body":"minigrep が渡したコマンドライン引数の値を読み取れるようにするために、Rustの標準ライブラリで提供されている関数が必要になり、 それは、 std::env::args です。この関数は、 minigrep に与えられたコマンドライン引数の イテレータ を返します。 イテレータについてはまだ議論していません(完全には第13章で講義します)が、とりあえずイテレータに関しては、 2つの詳細のみ知っていればいいです: イテレータは一連の値を生成することと、イテレータに対して collect 関数を呼び出し、 イテレータが生成する要素全部を含むベクタなどのコレクションに変えられることです。 リスト12-1のコードを使用して minigrep プログラムに渡されたあらゆるコマンドライン引数を読み取れるようにし、 それからその値をベクタとして集結させてください。 ファイル名: src/main.rs use std::env; fn main() { let args: Vec = env::args().collect(); println!(\"{:?}\", args);\n} リスト12-1: コマンドライン引数をベクタに集結させ、出力する まず、 std::env モジュールを use 文でスコープに導入したので、 args 関数が使用できます。 std::env::args 関数は、2レベルモジュールがネストされていることに注目してください。 第7章で議論したように、希望の関数が2モジュール以上ネストされている場合、 関数ではなく親モジュールをスコープに導入するのが因習的です。そうすることで、 std::env から別の関数も容易に使用することができます。また、 use std::env::args を追記し、関数を args とするだけで呼び出すのに比べて曖昧でもありません。 というのも、 args は現在のモジュールに定義されている関数と容易に見間違えられるかもしれないからです。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » 引数の値を読み取る","id":"ch12-01-accepting-command-line-arguments.html#a引数の値を読み取る","title":"引数の値を読み取る"},"ch12-02-reading-a-file.html#aファイルを読み込む":{"body":"では、 filename コマンドライン引数で指定されたファイルを読み込む機能を追加しましょう。 まず、テスト実行するためのサンプルファイルが必要ですね: minigrep が動作していることを確かめるために使用するのに最適なファイルは、 複数行にわたって同じ単語の繰り返しのある少量のテキストです。リスト12-3は、 うまくいくであろうエミリー・ディキンソン(Emily Dickinson)の詩です! プロジェクトのルート階層に poem.txt というファイルを作成し、この詩「私は誰でもない!あなたは誰?」を入力してください。 ファイル名: poem.txt I'm nobody! Who are you?\nAre you nobody, too?\nThen there's a pair of us - don't tell!\nThey'd banish us, you know. How dreary to be somebody!\nHow public, like a frog\nTo tell your name the livelong day\nTo an admiring bog! 私は誰でもない!あなたは誰?\nあなたも誰でもないの?\nなら、私たちは組だね、何も言わないで!\nあの人たちは、私たちを追放するでしょう。わかりますよね? 誰かでいるなんて侘しいじゃない!\nカエルみたいで公すぎるじゃない。\n自分の名を長い1日に告げるのなんて。\n感服するような沼地にね! リスト12-3: エミリー・ディキンソンの詩は、いいテストケースになる テキストを適当な場所に置いて、 src/main.rs を編集し、ファイルを開くコードを追加してください。 リスト12-4に示したようにですね。 ファイル名: src/main.rs use std::env;\nuse std::fs::File;\nuse std::io::prelude::*; fn main() {\n# let args: Vec = env::args().collect();\n#\n# let query = &args[1];\n# let filename = &args[2];\n#\n# println!(\"Searching for {}\", query); // --snip-- println!(\"In file {}\", filename); // ファイルが見つかりませんでした let mut f = File::open(filename).expect(\"file not found\"); let mut contents = String::new(); f.read_to_string(&mut contents) // ファイルの読み込み中に問題がありました .expect(\"something went wrong reading the file\"); // テキストは\\n{}です println!(\"With text:\\n{}\", contents);\n} リスト12-4: 第2引数で指定されたファイルの中身を読み込む 最初に、もう何個か use 文を追記して、標準ライブラリの関係のある箇所を持ってきています: ファイルを扱うのに std::fs::File が必要ですし、 std::io::prelude::* はファイル入出力を含む入出力処理をするのに有用なトレイトを色々含んでいます。 言語が一般的な初期化処理で特定の型や関数を自動的にスコープに導入するように、 std::io モジュールにはそれ独自の共通の型や関数の初期化処理があり、入出力を行う際に必要になるわけです。 標準の初期化処理とは異なり、 std::io の初期化処理には明示的に use 文を加えなければなりません。 main に3文を追記しました: 一つ目が、 File::open 関数を呼んで filename 変数の値に渡して、 ファイルへの可変なハンドルを得る処理です。二つ目が、 contents という名の変数を生成して、 可変で空の String を割り当てる処理です。この変数が、ファイル読み込み後に中身を保持します。 三つ目が、ファイルハンドルに対して read_to_string を呼び出し、引数として contents への可変参照を渡す処理です。 それらの行の後に、今回もファイル読み込み後に contents の値を出力する一時的な println! 文を追記したので、 ここまでプログラムがきちんと動作していることを確認できます。 第1コマンドライン引数には適当な文字列(まだ検索する箇所は実装してませんからね)を、第2引数に poem.txt ファイルを入れて、 このコードを実行しましょう: $ cargo run the poem.txt Compiling minigrep v0.1.0 (file:///projects/minigrep) Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/minigrep the poem.txt`\nSearching for the\nIn file poem.txt\nWith text:\nI'm nobody! Who are you?\nAre you nobody, too?\nThen there's a pair of us - don't tell!\nThey'd banish us, you know. How dreary to be somebody!\nHow public, like a frog\nTo tell your name the livelong day\nTo an admiring bog! 素晴らしい!コードがファイルの中身を読み込み、出力するようになりました。しかし、このコードにはいくつか欠陥があります。 main 関数が複数の責任を受け持っています: 一般に、各関数がただ一つの責任だけを持つようになれば、 関数は明確かつ、管理しやすくなります。もう一つの問題点は、できうる限りのエラー処理を怠っていることです。 まだプログラムが小規模なので、これらの欠陥は大きな問題にはなりませんが、プログラムが大規模になるにつれ、 それを綺麗に解消するのは困難になっていきます。プログラムを開発する際に早い段階でリファクタングを行うのは、 良い戦術です。リファクタリングするコードの量が少なければ、はるかに簡単になりますからね。次は、それを行いましょう。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » ファイルを読み込む","id":"ch12-02-reading-a-file.html#aファイルを読み込む","title":"ファイルを読み込む"},"ch12-03-improving-error-handling-and-modularity.html#aエラー処理を修正する":{"body":"さて、エラー処理の修正に取り掛かりましょう。ベクタが2個以下の要素しか含んでいないときに args ベクタの添え字1か2にアクセスしようとすると、 プログラムがパニックすることを思い出してください。試しに引数なしでプログラムを実行してください。すると、こんな感じになります: $ cargo run Compiling minigrep v0.1.0 (file:///projects/minigrep) Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/minigrep`\nthread 'main' panicked at 'index out of bounds: the len is 1\nbut the index is 1', src/main.rs:29:21\n(スレッド'main'は、「境界外アクセス: 長さは1なのに添え字も1です」でパニックしました)\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. 境界外アクセス: 長さは1なのに添え字も1です という行は、プログラマ向けのエラーメッセージです。 エンドユーザが起きたことと代わりにすべきことを理解する手助けにはならないでしょう。これを今修正しましょう。 エラーメッセージを改善する リスト12-8で、 new 関数に、添え字1と2にアクセスする前にスライスが十分長いことを実証するチェックを追加しています。 スライスの長さが十分でなければ、プログラムはパニックし、 境界外インデックス よりもいいエラーメッセージを表示します。 ファイル名: src/main.rs // --snip--\nfn new(args: &[String]) -> Config { if args.len() < 3 { // 引数の数が足りません panic!(\"not enough arguments\"); } // --snip-- リスト12-8: 引数の数のチェックを追加する このコードは、リスト9-9で記述した value 引数が正常な値の範囲外だった時に panic! を呼び出した Guess::new 関数と似ています。 ここでは、値の範囲を確かめる代わりに、 args の長さが少なくとも3であることを確かめていて、 関数の残りの部分は、この条件が満たされているという前提のもとで処理を行うことができます。 args に2要素以下しかなければ、この条件は真になり、 panic! マクロを呼び出して、即座にプログラムを終了させます。 では、 new のこの追加の数行がある状態で、再度引数なしでプログラムを走らせ、エラーがどんな見た目か確かめましょう: $ cargo run Compiling minigrep v0.1.0 (file:///projects/minigrep) Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/minigrep`\nthread 'main' panicked at 'not enough arguments', src/main.rs:30:12\n(スレッド'main'は「引数が足りません」でパニックしました)\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. この出力の方がマシです: これでエラーメッセージが合理的になりました。ですが、 ユーザに与えたくない追加の情報も含まれてしまっています。おそらく、 ここではリスト9-9で使用したテクニックを使用するのは最善ではありません: panic! の呼び出しは、第9章で議論したように、使用の問題よりもプログラミング上の問題により適しています。 代わりに、第9章で学んだもう一つのテクニックを使用することができます。成功か失敗かを示唆する Result を返すことです。 panic! を呼び出す代わりに new から Result を返す 代わりに、成功時には Config インスタンスを含み、エラー時には問題に言及する Result 値を返すことができます。 Config::new が main と対話する時、 Result 型を使用して問題があったと信号を送ることができます。 それから main を変更して、 panic! 呼び出しが引き起こしていた thread 'main' と RUST_BACKTRACE に関する周囲のテキストがない、 ユーザ向けのより実用的なエラーに Err 列挙子を変換することができます。 リスト12-9は、 Config::new の戻り値に必要な変更と Result を返すのに必要な関数の本体を示しています。 main も更新するまで、これはコンパイルできないことに注意してください。その更新は次のリストで行います。 ファイル名: src/main.rs impl Config { fn new(args: &[String]) -> Result { if args.len() < 3 { return Err(\"not enough arguments\"); } let query = args[1].clone(); let filename = args[2].clone(); Ok(Config { query, filename }) }\n} リスト12-9: Config::new から Result を返却する new 関数は、これで、成功時には Config インスタンスを、エラー時には &'static str を伴う Result を返すようになりました。 第10章の「静的ライフタイム」節から &'static str は文字列リテラルの型であることを思い出してください。 これは、今はエラーメッセージの型になっています。 new 関数の本体で2つ変更を行いました: 十分な数の引数をユーザが渡さなかった場合に panic! を呼び出す代わりに、 今は Err 値を返し、 Config 戻り値を Ok に包んでいます。これらの変更により、関数が新しい型シグニチャに適合するわけです。 Config::new から Err 値を返すことにより、 main 関数は、 new 関数から返ってくる Result 値を処理し、 エラー時により綺麗にプロセスから抜け出すことができます。 Config::new を呼び出し、エラーを処理する エラーケースを処理し、ユーザフレンドリーなメッセージを出力するために、 main を更新して、 リスト12-10に示したように Config::new から返されている Result を処理する必要があります。 また、 panic! からコマンドラインツールを0以外のエラーコードで抜け出す責任も奪い取り、 手作業でそれも実装します。0以外の終了コードは、 我々のプログラムを呼び出したプロセスにプログラムがエラー状態で終了したことを通知する慣習です。 ファイル名: src/main.rs use std::process; fn main() { let args: Vec = env::args().collect(); let config = Config::new(&args).unwrap_or_else(|err| { // 引数解析時に問題 println!(\"Problem parsing arguments: {}\", err); process::exit(1); }); // --snip-- リスト12-10: 新しい Config 作成に失敗したら、エラーコードで終了する このリストにおいて、以前には講義していないメソッドを使用しました: unwrap_or_else です。 これは標準ライブラリで Result に定義されています。 unwrap_or_else を使うことで、 panic! ではない何らか独自のエラー処理を定義できるのです。この Result が Ok 値だったら、 このメソッドの振る舞いは unwrap に似ています: Ok が包んでいる中身の値を返すのです。 しかし、値が Err 値なら、このメソッドは、 クロージャ 内でコードを呼び出し、 クロージャは私たちが定義し、引数として unwrap_or_else に渡す匿名関数です。クロージャについては第13章で詳しく講義します。 とりあえず、 unwrap_or_else は、今回リスト12-9で追加した not enough arguments という静的文字列の Err の中身を、 縦棒の間に出現する err 引数のクロージャに渡していることだけ知っておく必要があります。 クロージャのコードはそれから、実行された時に err 値を使用できます。 新規 use 行を追加して標準ライブラリから process をインポートしました。クロージャ内のエラー時に走るコードは、 たった2行です: err の値を出力し、それから process::exit を呼び出します。 process::exit 関数は、 即座にプログラムを停止させ、渡された数字を終了コードとして返します。これは、リスト12-8で使用した panic! ベースの処理と似ていますが、 もう余計な出力はされません。試しましょう: $ cargo run Compiling minigrep v0.1.0 (file:///projects/minigrep) Finished dev [unoptimized + debuginfo] target(s) in 0.48 secs Running `target/debug/minigrep`\nProblem parsing arguments: not enough arguments 素晴らしい!この出力の方が遥かにユーザに優しいです。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » エラー処理を修正する","id":"ch12-03-improving-error-handling-and-modularity.html#aエラー処理を修正する","title":"エラー処理を修正する"},"ch12-03-improving-error-handling-and-modularity.html#aコードをライブラリクレートに分割する":{"body":"ここまで minigrep は良さそうですね!では、テストを行え、 src/main.rs ファイルの責任が減らせるように、 src/main.rs ファイルを分割し、一部のコードを src/lib.rs ファイルに置きましょう。 main 関数以外のコード全部を src/main.rs から src/lib.rs に移動しましょう: run 関数定義 関係する use 文 Config の定義 Config::new 関数定義 src/lib.rs の中身にはリスト12-13に示したようなシグニチャがあるはずです(関数の本体は簡潔性のために省略しました)。 リスト12-14で src/main.rs に変更を加えるまで、このコードはコンパイルできないことに注意してください。 ファイル名: src/lib.rs use std::error::Error;\nuse std::fs::File;\nuse std::io::prelude::*; pub struct Config { pub query: String, pub filename: String,\n} impl Config { pub fn new(args: &[String]) -> Result { // --snip-- }\n} pub fn run(config: Config) -> Result<(), Box> { // --snip--\n} リスト12-13: Config と run を src/lib.rs に移動する ここでは、寛大に pub を使用しています: Config のフィールドと new メソッドと run 関数です。 これでテスト可能な公開APIのあるライブラリクレートができました! さて、 src/lib.rs に移動したコードを src/main.rs のバイナリクレートのスコープに持っていく必要があります。 リスト12-14に示したようにですね。 ファイル名: src/main.rs extern crate minigrep; use std::env;\nuse std::process; use minigrep::Config; fn main() { // --snip-- if let Err(e) = minigrep::run(config) { // --snip-- }\n} リスト12-14: minigrep クレートを src/main.rs のスコープに持っていく ライブラリクレートをバイナリクレートに持っていくのに、 extern crate minigrep を使用しています。 それから use minigrep::Config 行を追加して Config 型をスコープに持ってきて、 run 関数にクレート名を接頭辞として付けます。これで全機能が連結され、動くはずです。 cargo run でプログラムを走らせて、すべてがうまくいっていることを確かめてください。 ふう!作業量が多かったですね。ですが、将来成功する準備はできています。 もう、エラー処理は遥かに楽になり、コードのモジュール化もできました。 ここから先の作業は、ほぼ src/lib.rs で完結するでしょう。 古いコードでは大変だけれども、新しいコードでは楽なことをして新発見のモジュール性を活用しましょう: テストを書くのです!","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » コードをライブラリクレートに分割する","id":"ch12-03-improving-error-handling-and-modularity.html#aコードをライブラリクレートに分割する","title":"コードをライブラリクレートに分割する"},"ch12-03-improving-error-handling-and-modularity.html#aバイナリプロジェクトの責任の分離":{"body":"main 関数に複数の仕事の責任を割り当てるという構造上の問題は、多くのバイナリプロジェクトでありふれています。 結果として、 main が肥大化し始めた際にバイナリプログラムの個別の責任を分割するためにガイドラインとして活用できる工程をRustコミニュティは、 開発しました。この工程は、以下のような手順になっています: プログラムを main.rs と lib.rs に分け、ロジックを lib.rs に移動する。 コマンドライン引数の解析ロジックが小規模な限り、 main.rs に置いても良い。 コマンドライン引数の解析ロジックが複雑化の様相を呈し始めたら、 main.rs から抽出して lib.rs に移動する。 この工程の後に main 関数に残る責任は以下に限定される: 引数の値でコマンドライン引数の解析ロジックを呼び出す 他のあらゆる設定を行う lib.rs の run 関数を呼び出す run がエラーを返した時に処理する このパターンは、責任の分離についてです: main.rs はプログラムの実行を行い、 そして、 lib.rs が手にある仕事のロジック全てを扱います。 main 関数を直接テストすることはできないので、 この構造により、プログラムのロジック全てを lib.rs の関数に移すことでテストできるようになります。 main.rs に残る唯一のコードは、読めばその正当性が評価できるだけ小規模になるでしょう。 この工程に従って、プログラムのやり直しをしましょう。 引数解析器を抽出する 引数解析の機能を main が呼び出す関数に抽出して、コマンドライン引数解析ロジックを src/lib.rs に移動する準備をします。 リスト12-5に新しい関数 parse_config を呼び出す main の冒頭部を示し、 この新しい関数は今だけ src/main.rs に定義します。 ファイル名: src/main.rs fn main() { let args: Vec = env::args().collect(); let (query, filename) = parse_config(&args); // --snip--\n} fn parse_config(args: &[String]) -> (&str, &str) { let query = &args[1]; let filename = &args[2]; (query, filename)\n} リスト12-5: main から parse_config 関数を抽出する それでもまだ、コマンドライン引数をベクタに集結させていますが、 main 関数内で引数の値の添え字1を変数 query に、 添え字2を変数 filename に代入する代わりに、ベクタ全体を parse_config 関数に渡しています。 そして、 parse_config 関数にはどの引数がどの変数に入り、それらの値を main に返すというロジックが存在します。 まだ main 内に query と filename という変数を生成していますが、もう main は、 コマンドライン引数と変数がどう対応するかを決定する責任は持ちません。 このやり直しは、私たちの小規模なプログラムにはやりすぎに思えるかもしれませんが、 少しずつ段階的にリファクタリングしているのです。この変更後、プログラムを再度実行して、 引数解析がまだ動作していることを実証してください。問題が発生した時に原因を特定する助けにするために頻繁に進捗を確認するのはいいことです。 設定値をまとめる もう少し parse_config 関数を改善することができます。現時点では、タプルを返していますが、 即座にタプルを分解して再度個別の値にしています。これは、正しい抽象化をまだできていないかもしれない兆候です。 まだ改善の余地があると示してくれる他の徴候は、 parse_config の config の部分であり、 返却している二つの値は関係があり、一つの設定値の一部にどちらもなることを暗示しています。 現状では、一つのタプルにまとめていること以外、この意味をデータの構造に載せていません; この二つの値を1構造体に置き換え、構造体のフィールドそれぞれに意味のある名前をつけることもできるでしょう。 そうすることで将来このコードのメンテナンス者が、異なる値が相互に関係する仕方や、目的を理解しやすくできるでしょう。 注釈: この複雑型(complex type)がより適切な時に組み込みの値を使うアンチパターンを、 primitive obsession ( 訳注 : 初めて聞いた表現。 組み込み型強迫観念 といったところだろうか)と呼ぶ人もいます。 リスト12-6は、 parse_config 関数の改善を示しています。 ファイル名: src/main.rs # use std::env;\n# use std::fs::File;\n#\nfn main() { let args: Vec = env::args().collect(); let config = parse_config(&args); println!(\"Searching for {}\", config.query); println!(\"In file {}\", config.filename); let mut f = File::open(config.filename).expect(\"file not found\"); // --snip--\n} struct Config { query: String, filename: String,\n} fn parse_config(args: &[String]) -> Config { let query = args[1].clone(); let filename = args[2].clone(); Config { query, filename }\n} リスト12-6: parse_config をリファクタリングして Config 構造体のインスタンスを返す query と filename というフィールドを持つよう定義された Config という構造体を追加しました。 parse_config のシグニチャは、これで Config 値を返すと示すようになりました。 parse_config の本体では、 以前は args の String 値を参照する文字列スライスを返していましたが、 今では所有する String 値を含むように Config を定義しています。 main の args 変数は引数値の所有者であり、 parse_config 関数だけに借用させていますが、これは Config が args の値の所有権を奪おうとしたら、 Rustの借用規則に違反してしまうことを意味します。 String のデータは、多くの異なる手法で管理できますが、最も単純だけれどもどこか非効率的な手段は、 値に対して clone メソッドを呼び出すことです。これにより、 Config インスタンスが所有するデータの総コピーが生成されるので、 文字列データへの参照を保持するよりも時間とメモリを消費します。ですが、データをクローンすることで、 コードがとても素直にもなります。というのも、参照のライフタイムを管理する必要がないからです。 つまり、この場面において、少々のパフォーマンスを犠牲にして単純性を得るのは、価値のある代償です。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » バイナリプロジェクトの責任の分離","id":"ch12-03-improving-error-handling-and-modularity.html#aバイナリプロジェクトの責任の分離","title":"バイナリプロジェクトの責任の分離"},"ch12-03-improving-error-handling-and-modularity.html#aリファクタリングしてモジュール性とエラー処理を向上させる":{"body":"プログラムを改善するために、プログラムの構造と起こりうるエラーに対処する方法に関連する4つの問題を修正していきましょう。 1番目は、 main 関数が2つの仕事を受け持っていることです: 引数を解析し、ファイルを開いています。 このような小さな関数なら、これは、大した問題ではありませんが、 main 内でプログラムを巨大化させ続けたら、 main 関数が扱う個別の仕事の数も増えていきます。関数が責任を受け持つごとに、 正しいことを確認しにくくなり、テストも行いづらくなり、機能を壊さずに変更するのも困難になっていきます。 機能を小分けして、各関数が1つの仕事のみに責任を持つようにするのが最善です。 この問題は、2番目の問題にも結びついています: query と filename はプログラムの設定用変数ですが、 f や contents といった変数は、プログラムのロジックを担っています。 main が長くなるほど、 スコープに入れるべき変数も増えます。そして、スコープにある変数が増えれば、各々の目的を追うのも大変になるわけです。 設定用変数を一つの構造に押し込め、目的を明瞭化するのが最善です。 3番目の問題は、ファイルを開き損ねた時に expect を使ってエラーメッセージを出力しているのに、 エラーメッセージが ファイルが見つかりませんでした としか表示しないことです。 ファイルを開く行為は、ファイルが存在しない以外にもいろんな方法で失敗することがあります: 例えば、ファイルは存在するかもしれないけれど、開く権限がないかもしれないなどです。 現時点では、そのような状況になった時、「ファイルが見つかりませんでした」というエラーメッセージを出力し、 これはユーザに間違った情報を与えるのです。 4番目は、異なるエラーを処理するのに expect を繰り返し使用しているので、ユーザが十分な数の引数を渡さずにプログラムを起動した時に、 問題を明確に説明しない「範囲外アクセス(index out of bounds)」というエラーがRustから得られることです。 エラー処理のコードが全て1箇所に存在し、将来エラー処理ロジックが変更になった時に、 メンテナンス者が1箇所のコードのみを考慮すればいいようにするのが最善でしょう。 エラー処理コードが1箇所にあれば、エンドユーザにとって意味のあるメッセージを出力していることを確認することにもつながります。 プロジェクトをリファクタリングして、これら4つの問題を扱いましょう。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » リファクタリングしてモジュール性とエラー処理を向上させる","id":"ch12-03-improving-error-handling-and-modularity.html#aリファクタリングしてモジュール性とエラー処理を向上させる","title":"リファクタリングしてモジュール性とエラー処理を向上させる"},"ch12-03-improving-error-handling-and-modularity.html#clone-を使用する代償":{"body":"実行時コストのために clone を使用して所有権問題を解消するのを避ける傾向が多くのRustaceanにあります。 第13章で、この種の状況においてより効率的なメソッドの使用法を学ぶでしょう。ですがとりあえずは、 これらのコピーをするのは1回だけですし、ファイル名とクエリ文字列は非常に小さなものなので、 いくつかの文字列をコピーして進捗するのは良しとしましょう。最初の通り道でコードを究極的に効率化しようとするよりも、 ちょっと非効率的でも動くプログラムを用意する方がいいでしょう。もっとRustの経験を積めば、 最も効率的な解決法から開始することも簡単になるでしょうが、今は、 clone を呼び出すことは完璧に受け入れられることです。 main を更新したので、 parse_config から返された Config のインスタンスを config という変数に置くようになり、 以前は個別の query と filename 変数を使用していたコードを更新したので、代わりに Config 構造体のフィールドを使用するようになりました。 これでコードは query と filename が関連していることと、その目的がプログラムの振る舞い方を設定するということをより明確に伝えます。 これらの値を使用するあらゆるコードは、 config インスタンスの目的の名前を冠したフィールドにそれらを発見することを把握しています。 Config のコンストラクタを作成する ここまでで、コマンドライン引数を解析する責任を負ったロジックを main から抽出し、 parse_config 関数に配置しました。 そうすることで query と filename の値が関連し、その関係性がコードに載っていることを確認する助けになりました。 それから Config 構造体を追加して query と filename の関係する目的を名前付けし、 構造体のフィールド名として parse_config 関数からその値の名前を返すことができています。 したがって、今や parse_config 関数の目的は Config インスタンスを生成することになったので、 parse_config をただの関数から Config 構造体に紐づく new という関数に変えることができます。 この変更を行うことで、コードがより慣用的になります。 String などの標準ライブラリの型のインスタンスを、 String::new を呼び出すことで生成できます。同様に、 parse_config を Config に紐づく new 関数に変えれば、 Config::new を呼び出すことで Config のインスタンスを生成できるようになります。リスト12-7が、 行う必要のある変更を示しています。 ファイル名: src/main.rs # use std::env;\n#\nfn main() { let args: Vec = env::args().collect(); let config = Config::new(&args); // --snip--\n} # struct Config {\n# query: String,\n# filename: String,\n# }\n#\n// --snip-- impl Config { fn new(args: &[String]) -> Config { let query = args[1].clone(); let filename = args[2].clone(); Config { query, filename } }\n} リスト12-7: parse_config を Config::new に変える parse_config を呼び出していた main を代わりに Config::new を呼び出すように更新しました。 parse_config の名前を new に変え、 impl ブロックに入れ込んだので、 new 関数と Config が紐づくようになりました。 再度このコードをコンパイルしてみて、動作することを確かめてください。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » clone を使用する代償","id":"ch12-03-improving-error-handling-and-modularity.html#clone-を使用する代償","title":"clone を使用する代償"},"ch12-03-improving-error-handling-and-modularity.html#main-からロジックを抽出する":{"body":"これで設定解析のリファクタリングが終了したので、プログラムのロジックに目を向けましょう。 「バイナリプロジェクトの責任の分離」で述べたように、 現在 main 関数に存在する設定のセットアップやエラー処理に関わらない全てのロジックを保持することになる run という関数を抽出します。 やり終わったら、 main は簡潔かつ視察で確かめやすくなり、他のロジック全部に対してテストを書くことができるでしょう。 リスト12-11は、抜き出した run 関数を示しています。今は少しずつ段階的に関数を抽出する改善を行っています。 それでも、 src/main.rs に関数を定義していきます。 ファイル名: src/main.rs fn main() { // --snip-- println!(\"Searching for {}\", config.query); println!(\"In file {}\", config.filename); run(config);\n} fn run(config: Config) { let mut f = File::open(config.filename).expect(\"file not found\"); let mut contents = String::new(); f.read_to_string(&mut contents) .expect(\"something went wrong reading the file\"); println!(\"With text:\\n{}\", contents);\n} // --snip-- リスト12-11: 残りのプログラムロジックを含む run 関数を抽出する これで run 関数は、ファイル読み込みから始まる main 関数の残りのロジック全てを含むようになりました。 この run 関数は、引数に Config インスタンスを取ります。 run 関数からエラーを返す 残りのプログラムロジックが run 関数に隔離されたので、リスト12-9の Config::new のように、 エラー処理を改善することができます。 expect を呼び出してプログラムにパニックさせる代わりに、 run 関数は、何か問題が起きた時に Result を返します。これにより、 さらにエラー処理周りのロジックをユーザに優しい形で main に統合することができます。 リスト12-12にシグニチャと run 本体に必要な変更を示しています。 ファイル名: src/main.rs use std::error::Error; // --snip-- fn run(config: Config) -> Result<(), Box> { let mut f = File::open(config.filename)?; let mut contents = String::new(); f.read_to_string(&mut contents)?; println!(\"With text:\\n{}\", contents); Ok(())\n} リスト12-12: run 関数を変更して Result を返す ここでは、3つの大きな変更を行いました。まず、 run 関数の戻り値を Result<(), Box> に変えました。 この関数は、以前はユニット型、 () を返していて、それを Ok の場合に返される値として残しました。 エラー型については、 トレイトオブジェクト の Box を使用しました(同時に冒頭で use 文により、 std::error::Error をスコープに導入しています)。トレイトオブジェクトについては、第17章で講義します。 とりあえず、 Box は、関数が Error トレイトを実装する型を返すことを意味しますが、 戻り値の型を具体的に指定しなくても良いことを知っておいてください。これにより、 エラーケースによって異なる型のエラー値を返す柔軟性を得ます。 2番目に、 expect の呼び出しよりも ? 演算子を選択して取り除きました。第9章で語りましたね。 エラーでパニックするのではなく、 ? 演算子は呼び出し元が処理できるように、現在の関数からエラー値を返します。 3番目に、 run 関数は今、成功時に Ok 値を返すようになりました。 run 関数の成功型は、 シグニチャで () と定義したので、ユニット型の値を Ok 値に包む必要があります。 最初は、この Ok(()) という記法は奇妙に見えるかもしれませんが、このように () を使うことは、 run を副作用のためだけに呼び出していると示唆する慣習的な方法です; 必要な値は返しません。 このコードを実行すると、コンパイルは通るものの、警告が表示されるでしょう: warning: unused `std::result::Result` which must be used\n(警告: 使用されなければならない`std::result::Result`が未使用です) --> src/main.rs:18:5 |\n18 | run(config); | ^^^^^^^^^^^^\n= note: #[warn(unused_must_use)] on by default コンパイラは、コードが Result 値を無視していると教えてくれて、この Result 値は、 エラーが発生したと示唆しているかもしれません。しかし、エラーがあったか確認するつもりはありませんが、 コンパイラは、ここにエラー処理コードを書くつもりだったんじゃないかと思い出させてくれています! 今、その問題を改修しましょう。 main で run から返ってきたエラーを処理する リスト12-10の Config::new に対して行った方法に似たテクニックを使用してエラーを確認し、扱いますが、 少し違いがあります: ファイル名: src/main.rs fn main() { // --snip-- println!(\"Searching for {}\", config.query); println!(\"In file {}\", config.filename); if let Err(e) = run(config) { println!(\"Application error: {}\", e); process::exit(1); }\n} unwrap_or_else ではなく、 if let で run が Err 値を返したかどうかを確認し、そうなら process::exit(1) を呼び出しています。 run 関数は、 Config::new が Config インスタンスを返すのと同じように unwrap したい値を返すことはありません。 run は成功時に () を返すので、エラーを検知することにのみ興味があり、 () でしかないので、 unwrap_or_else に包まれた値を返してもらう必要はないのです。 if let と unwrap_or_else 関数の中身はどちらも同じです: エラーを出力して終了します。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » main からロジックを抽出する","id":"ch12-03-improving-error-handling-and-modularity.html#main-からロジックを抽出する","title":"main からロジックを抽出する"},"ch12-04-testing-the-librarys-functionality.html#aテストを通過させるコードを書く":{"body":"空のベクタを常に返しているために、現状テストは失敗しています。それを修正し、 search を実装するには、 プログラムは以下の手順に従う必要があります: 中身を各行ごとに繰り返す。 行にクエリ文字列が含まれるか確認する。 するなら、それを返却する値のリストに追加する。 しないなら、何もしない。 一致する結果のリストを返す。 各行を繰り返す作業から、この手順に順に取り掛かりましょう。 lines メソッドで各行を繰り返す Rustには、文字列を行ごとに繰り返す役立つメソッドがあり、利便性のために lines と名付けられ、 リスト12-17のように動作します。まだ、これはコンパイルできないことに注意してください。 ファイル名: src/lib.rs pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { for line in contents.lines() { // 行に対して何かする // do something with line }\n} リスト12-17: contents の各行を繰り返す lines メソッドはイテレータを返します。イテレータについて詳しくは、第13章で話しますが、 リスト3-5でこのようなイテレータの使用法は見かけたことを思い出してください。 そこでは、イテレータに for ループを使用してコレクションの各要素に対して何らかのコードを走らせていました。 クエリを求めて各行を検索する 次に現在の行がクエリ文字列を含むか確認します。幸運なことに、 文字列にはこれを行ってくれる contains という役に立つメソッドがあります! search 関数に、 contains メソッドの呼び出しを追加してください。リスト12-18のようにですね。 それでもまだコンパイルできないことに注意してください。 ファイル名: src/lib.rs pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { for line in contents.lines() { if line.contains(query) { // do something with line } }\n} リスト12-18: 行が query の文字列を含むか確認する機能を追加する 合致した行を保存する また、クエリ文字列を含む行を保存する方法が必要です。そのために、 for ループの前に可変なベクタを生成し、 push メソッドを呼び出して line をベクタに保存することができます。 for ループの後でベクタを返却します。 リスト12-19のようにですね。 ファイル名: src/lib.rs pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { let mut results = Vec::new(); for line in contents.lines() { if line.contains(query) { results.push(line); } } results\n} リスト12-19: 合致する行を保存したので、返すことができる これで search 関数は、 query を含む行だけを返すはずであり、テストも通るはずです。 テストを実行しましょう: $ cargo test\n--snip--\nrunning 1 test\ntest test::one_result ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out テストが通り、動いていることがわかりました! ここで、テストが通過するよう保ったまま、同じ機能を保持しながら、検索関数の実装をリファクタリングする機会を考えることもできます。 検索関数のコードは悪すぎるわけではありませんが、イテレータの有用な機能の一部を活用していません。 この例には第13章で再度触れ、そこでは、イテレータをより深く探究し、さらに改善する方法に目を向けます。 run 関数内で search 関数を使用する search 関数が動きテストできたので、 run 関数から search を呼び出す必要があります。 config.query の値と、 ファイルから run が読み込む contents の値を search 関数に渡す必要があります。 それから run は、 search から返ってきた各行を出力するでしょう: ファイル名: src/lib.rs pub fn run(config: Config) -> Result<(), Box> { let mut f = File::open(config.filename)?; let mut contents = String::new(); f.read_to_string(&mut contents)?; for line in search(&config.query, &contents) { println!(\"{}\", line); } Ok(())\n} それでも for ループで search から各行を返し、出力しています。 さて、プログラム全体が動くはずです!試してみましょう。まずはエミリー・ディキンソンの詩から、 ちょうど1行だけを返すはずの言葉から。\"frog\"です: $ cargo run frog poem.txt Compiling minigrep v0.1.0 (file:///projects/minigrep) Finished dev [unoptimized + debuginfo] target(s) in 0.38 secs Running `target/debug/minigrep frog poem.txt`\nHow public, like a frog かっこいい!今度は、複数行にマッチするであろう言葉を試しましょう。\"body\"とかね: $ cargo run body poem.txt Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/minigrep body poem.txt`\nI’m nobody! Who are you?\nAre you nobody, too?\nHow dreary to be somebody! そして最後に、詩のどこにも現れない単語を探したときに、何も出力がないことを確かめましょう。 \"monomorphization\"などね: $ cargo run monomorphization poem.txt Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/minigrep monomorphization poem.txt` 最高です!古典的なツールの独自のミニバージョンを構築し、アプリケーションを構造化する方法を多く学びました。 また、ファイル入出力、ライフタイム、テスト、コマンドライン引数の解析についても、少し学びました。 このプロジェクトをまとめ上げるために、環境変数を扱う方法と標準エラー出力に出力する方法を少しだけデモします。 これらはどちらも、コマンドラインプログラムを書く際に有用です。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » テストを通過させるコードを書く","id":"ch12-04-testing-the-librarys-functionality.html#aテストを通過させるコードを書く","title":"テストを通過させるコードを書く"},"ch12-04-testing-the-librarys-functionality.html#aテスト駆動開発でライブラリの機能を開発する":{"body":"今や、ロジックを src/lib.rs に抜き出し、引数集めとエラー処理を src/main.rs に残したので、 コードの核となる機能のテストを書くのが非常に容易になりました。いろんな引数で関数を直接呼び出し、 コマンドラインからバイナリを呼び出す必要なく戻り値を確認できます。ご自由に Config::new や run 関数の機能のテストは、 ご自身でお書きください。 この節では、テスト駆動開発(TDD)過程を活用して minigrep プログラムに検索ロジックを追加します。 このソフトウェア開発テクニックは、以下の手順に従います: 失敗するテストを書き、走らせて想定通りの理由で失敗することを確かめる。 十分な量のコードを書くか変更して新しいテストを通過するようにする。 追加または変更したばかりのコードをリファクタリングし、テストが通り続けることを確認する。 手順1から繰り返す! この過程は、ソフトウェアを書く多くの方法のうちの一つに過ぎませんが、TDDによりコードデザインも駆動することができます。 テストを通過させるコードを書く前にテストを書くことで、過程を通して高いテストカバー率を保つ助けになります。 実際にクエリ文字列の検索を行う機能の実装をテスト駆動し、クエリに合致する行のリストを生成します。 この機能を search という関数に追加しましょう。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » テスト駆動開発でライブラリの機能を開発する","id":"ch12-04-testing-the-librarys-functionality.html#aテスト駆動開発でライブラリの機能を開発する","title":"テスト駆動開発でライブラリの機能を開発する"},"ch12-04-testing-the-librarys-functionality.html#a失敗するテストを記述する":{"body":"もう必要ないので、プログラムの振る舞いを確認していた println! 文を src/lib.rs と src/main.rs から削除しましょう。 それから src/lib.rs で、テスト関数のある test モジュールを追加します。第11章のようにですね。 このテスト関数が search 関数に欲しい振る舞いを指定します: クエリとそれを検索するテキストを受け取り、 クエリを含む行だけをテキストから返します。リスト12-15にこのテストを示していますが、まだコンパイルは通りません。 ファイル名: src/lib.rs # fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {\n# vec![]\n# }\n#\n#[cfg(test)]\nmod test { use super::*; #[test] fn one_result() { let query = \"duct\"; // Rustは // 安全で速く生産性も高い。 // 3つ選んで。 let contents = \"\\\nRust:\nsafe, fast, productive.\nPick three.\"; assert_eq!( vec![\"safe, fast, productive.\"], search(query, contents) ); }\n} リスト12-15: こうだったらいいなという search 関数の失敗するテストを作成する このテストは、 \"duct.\" という文字列を検索します。検索対象の文字列は3行で、うち1行だけが \"duct.\" を含みます。 search 関数から返る値が想定している行だけを含むことをアサーションします。 このテストを走らせ、失敗するところを観察することはできません。このテストはコンパイルもできないからです: まだ search 関数が存在していません!ゆえに今度は、空のベクタを常に返す search 関数の定義を追加することで、 テストをコンパイルし走らせるだけのコードを追記します。リスト12-16に示したようにですね。そうすれば、 テストはコンパイルでき、失敗するはずです。なぜなら、空のベクタは、 \"safe, fast, productive.\" という行を含むベクタとは合致しないからです。 ファイル名: src/lib.rs pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { vec![]\n} リスト12-16: テストがコンパイルできるのに十分なだけ search 関数を定義する 明示的なライフタイムの 'a が search のシグニチャで定義され、 contents 引数と戻り値で使用されていることに注目してください。 第10章からライフタイム仮引数は、どの実引数のライフタイムが戻り値のライフタイムに関連づけられているかを指定することを思い出してください。 この場合、返却されるベクタは、 ( query 引数ではなく) contents 引数のスライスを参照する文字列スライスを含むべきと示唆しています。 言い換えると、コンパイラに search 関数に返されるデータは、 search 関数に contents 引数で渡されているデータと同期間生きることを教えています。 これは重要なことです!スライス に 参照されるデータは、参照が有効になるために有効である必要があるのです; コンパイラが contents ではなく query の文字列スライスを生成すると想定してしまったら、 安全性チェックを間違って行うことになってしまいます。 ライフタイム注釈を忘れてこの関数をコンパイルしようとすると、こんなエラーが出ます: error[E0106]: missing lifetime specifier\n(エラー: ライフタイム指定子が欠けています) --> src/lib.rs:5:51 |\n5 | pub fn search(query: &str, contents: &str) -> Vec<&str> { | ^ expected lifetime\nparameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `query` or `contents` (助言: この関数の戻り値は、借用された値を含んでいますが、シグニチャにはそれが、 `query`か`contents`から借用されたものであるかが示されていません) コンパイラには、二つの引数のどちらが必要なのか知る由がないので、教えてあげる必要があるのです。 contents がテキストを全て含む引数で、合致するそのテキストの一部を返したいので、 contents がライフタイム記法で戻り値に関連づくはずの引数であることをプログラマは知っています。 他のプログラミング言語では、シグニチャで引数と戻り値を関連づける必要はありません。これは奇妙に思えるかもしれませんが、 時間とともに楽になっていきます。この例を第10章、「ライフタイムで参照を有効化する」節と比較したくなるかもしれません。 さあ、テストを実行しましょう: $ cargo test Compiling minigrep v0.1.0 (file:///projects/minigrep)\n--warnings-- Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs Running target/debug/deps/minigrep-abcabcabc running 1 test\ntest test::one_result ... FAILED failures: ---- test::one_result stdout ---- thread 'test::one_result' panicked at 'assertion failed: `(left ==\nright)`\nleft: `[\"safe, fast, productive.\"]`,\nright: `[]`)', src/lib.rs:48:8\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. failures: test::one_result test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out error: test failed, to rerun pass '--lib' 素晴らしい。テストは全く想定通りに失敗しています。テストが通るようにしましょう!","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » 失敗するテストを記述する","id":"ch12-04-testing-the-librarys-functionality.html#a失敗するテストを記述する","title":"失敗するテストを記述する"},"ch12-05-working-with-environment-variables.html#a大文字小文字を区別しない-search-関数用に失敗するテストを書く":{"body":"環境変数がオンの場合に呼び出す search_case_insensitive 関数を新しく追加したいです。テスト駆動開発の過程に従い続けるので、 最初の手順は、今回も失敗するテストを書くことです。新しい search_case_insensitive 関数用の新規テストを追加し、 古いテストを one_result から case_sensitive に名前変更して、二つのテストの差異を明確化します。 リスト12-20に示したようにですね。 ファイル名: src/lib.rs #[cfg(test)]\nmod test { use super::*; #[test] fn case_sensitive() { let query = \"duct\";\n// Rust\n// 安全かつ高速で生産的\n// 三つを選んで\n// ガムテープ let contents = \"\\\nRust:\nsafe, fast, productive.\nPick three.\nDuct tape.\"; assert_eq!( vec![\"safe, fast, productive.\"], search(query, contents) ); } #[test] fn case_insensitive() { let query = \"rUsT\";\n// (最後の行のみ)\n// 私を信じて let contents = \"\\\nRust:\nsafe, fast, productive.\nPick three.\nTrust me.\"; assert_eq!( vec![\"Rust:\", \"Trust me.\"], search_case_insensitive(query, contents) ); }\n} リスト12-20: 追加しようとしている大文字小文字を区別しない関数用の失敗するテストを新しく追加する 古いテストの contents も変更していることに注意してください。大文字小文字を区別する検索を行う際に、 \"duct\" というクエリに合致しないはずの大文字Dを使用した \"Duct tape\" (ガムテープ)という新しい行を追加しました。 このように古いテストを変更することで、既に実装済みの大文字小文字を区別する検索機能を誤って壊してしまわないことを保証する助けになります。 このテストはもう通り、大文字小文字を区別しない検索に取り掛かっても通り続けるはずです。 大文字小文字を区別 しない 検索の新しいテストは、クエリに\"rUsT\"を使用しています。 追加直前の search_case_insensitive 関数では、\"rUsT\"というクエリは、 両方ともクエリとは大文字小文字が異なるのに、大文字Rの\"Rust:\"を含む行と、 “Trust me.” という行にもマッチするはずです。これが失敗するテストであり、まだ search_case_insensitive 関数を定義していないので、 コンパイルは失敗するでしょう。リスト12-16の search 関数で行ったのと同様に空のベクタを常に返すような仮実装を追加し、テストがコンパイルされるものの、失敗する様をご自由に確認してください。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » 大文字小文字を区別しない search 関数用に失敗するテストを書く","id":"ch12-05-working-with-environment-variables.html#a大文字小文字を区別しない-search-関数用に失敗するテストを書く","title":"大文字小文字を区別しない search 関数用に失敗するテストを書く"},"ch12-05-working-with-environment-variables.html#a環境変数を取り扱う":{"body":"おまけの機能を追加して minigrep を改善します: 環境変数でユーザがオンにできる大文字小文字無視の検索用のオプションです。 この機能をコマンドラインオプションにして、適用したい度にユーザが入力しなければならないようにすることもできますが、 代わりに環境変数を使用します。そうすることでユーザは1回環境変数をセットすれば、そのターミナルセッションの間は、 大文字小文字無視の検索を行うことができるようになるわけです。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » 環境変数を取り扱う","id":"ch12-05-working-with-environment-variables.html#a環境変数を取り扱う","title":"環境変数を取り扱う"},"ch12-05-working-with-environment-variables.html#search_case_insensitive-関数を実装する":{"body":"search_case_insensitive 関数は、リスト12-21に示しましたが、 search 関数とほぼ同じです。 唯一の違いは、 query と各 line を小文字化していることなので、入力引数の大文字小文字によらず、 行がクエリを含んでいるか確認する際には、同じになるわけです。 ファイル名: src/lib.rs pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { let query = query.to_lowercase(); let mut results = Vec::new(); for line in contents.lines() { if line.to_lowercase().contains(&query) { results.push(line); } } results\n} リスト12-21: 比較する前にクエリと行を小文字化するよう、 search_case_insensitive 関数を定義する まず、 query 文字列を小文字化し、同じ名前の覆い隠された変数に保存します。ユーザのクエリが \"rust\" や \"RUST\" 、 \"Rust\" 、 \"rUsT\" などだったりしても、 \"rust\" であり、大文字小文字を区別しないかのようにクエリを扱えるように、 to_lowercase をクエリに対して呼び出すことは必須です。 query は最早、文字列スライスではなく String であることに注意してください。というのも、 to_lowercase を呼び出すと、既存のデータを参照するというよりも、新しいデータを作成するからです。 例として、クエリは \"rUsT\" だとしましょう: その文字列スライスは、小文字の u や t を使えるように含んでいないので、 \"rust\" を含む新しい String のメモリを確保しなければならないのです。今、 contains メソッドに引数として query を渡すと、 アンド記号を追加する必要があります。 contains のシグニチャは、文字列スライスを取るよう定義されているからです。 次に、各 line が query を含むか確かめる前に to_lowercase の呼び出しを追加し、全文字を小文字化しています。 今や line と query を小文字に変換したので、クエリが大文字であろうと小文字であろうとマッチを検索するでしょう。 この実装がテストを通過するか確認しましょう: running 2 tests\ntest test::case_insensitive ... ok\ntest test::case_sensitive ... ok test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 素晴らしい!どちらも通りました。では、 run 関数から新しい search_case_insensitive 関数を呼び出しましょう。 1番目に大文字小文字の区別を切り替えられるよう、 Config 構造体に設定オプションを追加します。 まだどこでも、このフィールドの初期化をしていないので、追加するとコンパイルエラーが起きます: ファイル名: src/lib.rs pub struct Config { pub query: String, pub filename: String, pub case_sensitive: bool,\n} 論理値を持つ case_sensitive フィールドを追加したことに注意してください。次に、 run 関数に、 case_sensitive フィールドの値を確認し、 search 関数か search_case_insensitive 関数を呼ぶかを決定するのに使ってもらう必要があります。 リスト12-22のようにですね。それでも、これはまだコンパイルできないことに注意してください。 ファイル名: src/lib.rs # use std::error::Error;\n# use std::fs::File;\n# use std::io::prelude::*;\n#\n# fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {\n# vec![]\n# }\n#\n# pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {\n# vec![]\n# }\n#\n# pub struct Config {\n# query: String,\n# filename: String,\n# case_sensitive: bool,\n# }\n#\npub fn run(config: Config) -> Result<(), Box> { let mut f = File::open(config.filename)?; let mut contents = String::new(); f.read_to_string(&mut contents)?; let results = if config.case_sensitive { search(&config.query, &contents) } else { search_case_insensitive(&config.query, &contents) }; for line in results { println!(\"{}\", line); } Ok(())\n} リスト12-22: config.case_sensitive の値に基づいて search か search_case_insensitive を呼び出す 最後に、環境変数を確認する必要があります。環境変数を扱う関数は、標準ライブラリの env モジュールにあるので、 use std::env; 行で src/lib.rs の冒頭でそのモジュールをスコープに持ってくる必要があります。そして、 env モジュールから var 関数を使用して CASE_INSENSITIVE という環境変数のチェックを行います。 リスト12-23のようにですね。 ファイル名: src/lib.rs use std::env;\n# struct Config {\n# query: String,\n# filename: String,\n# case_sensitive: bool,\n# } // --snip-- impl Config { pub fn new(args: &[String]) -> Result { if args.len() < 3 { return Err(\"not enough arguments\"); } let query = args[1].clone(); let filename = args[2].clone(); let case_sensitive = env::var(\"CASE_INSENSITIVE\").is_err(); Ok(Config { query, filename, case_sensitive }) }\n} リスト12-23: CASE_INSENSITIVE という環境変数のチェックを行う ここで、 case_sensitive という新しい変数を生成しています。その値をセットするために、 env::var 関数を呼び出し、 CASE_INSENSITIVE 環境変数の名前を渡しています。 env::var 関数は、 環境変数がセットされていたら、環境変数の値を含む Ok 列挙子の成功値になる Result を返します。 環境変数がセットされていなければ、 Err 列挙子を返すでしょう。 Result の is_err メソッドを使用して、エラーでありゆえに、セットされていないことを確認しています。 これは大文字小文字を区別する検索をす べき ことを意味します。 CASE_INSENSITIVE 環境変数が何かにセットされていれば、 is_err はfalseを返し、プログラムは大文字小文字を区別しない検索を実行するでしょう。環境変数の 値 はどうでもよく、 セットされているかどうかだけ気にするので、 unwrap や expect あるいは、他のここまで見かけた Result のメソッドではなく、 is_err をチェックしています。 case_sensitive 変数の値を Config インスタンスに渡しているので、リスト12-22で実装したように、 run 関数はその値を読み取り、 search か search_case_insensitive を呼び出すか決定できるのです。 試行してみましょう!まず、環境変数をセットせずにクエリは to でプログラムを実行し、 この時は全て小文字で\"to\"という言葉を含むあらゆる行が合致するはずです。 $ cargo run to poem.txt Compiling minigrep v0.1.0 (file:///projects/minigrep) Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/minigrep to poem.txt`\nAre you nobody, too?\nHow dreary to be somebody! まだ機能しているようです!では、 CASE_INSENSITIVE を1にしつつ、同じクエリの to でプログラムを実行しましょう。 PowerShellを使用しているなら、1コマンドではなく、2コマンドで環境変数をセットし、プログラムを実行する必要があるでしょう: $ $env:CASE_INSENSITIVE=1\n$ cargo run to poem.txt 大文字も含む可能性のある\"to\"を含有する行が得られるはずです: $ CASE_INSENSITIVE=1 cargo run to poem.txt Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/minigrep to poem.txt`\nAre you nobody, too?\nHow dreary to be somebody!\nTo tell your name the livelong day\nTo an admiring bog! 素晴らしい、\"To\"を含む行も出てきましたね! minigrep プログラムはこれで、 環境変数によって制御できる大文字小文字を区別しない検索も行えるようになりました。もうコマンドライン引数か、 環境変数を使ってオプションを管理する方法も知りましたね。 引数 と 環境変数で同じ設定を行うことができるプログラムもあります。そのような場合、 プログラムはどちらが優先されるか決定します。自身の別の鍛錬として、コマンドライン引数か、 環境変数で大文字小文字の区別を制御できるようにしてみてください。 片方は大文字小文字を区別するようにセットされ、もう片方は区別しないようにセットしてプログラムが実行された時に、 コマンドライン引数と環境変数のどちらの優先度が高くなるかを決めてください。 std::env モジュールは、環境変数を扱うもっと多くの有用な機能を有しています: ドキュメンテーションを確認して、何が利用可能か確かめてください。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » search_case_insensitive 関数を実装する","id":"ch12-05-working-with-environment-variables.html#search_case_insensitive-関数を実装する","title":"search_case_insensitive 関数を実装する"},"ch12-06-writing-to-stderr-instead-of-stdout.html#aまとめ":{"body":"この章では、ここまでに学んできた主要な概念の一部を念押しし、Rustで入出力処理を行う方法を講義しました。 コマンドライン引数、ファイル、環境変数、そしてエラー出力に eprintln! マクロを使用することで、 もう、コマンドラインアプリケーションを書く準備ができています。以前の章の概念を使用することで、 コードはうまく体系化され、適切なデータ構造に効率的にデータを保存し、エラーをうまく扱い、 よくテストされるでしょう。 次は、関数型言語に影響されたRust機能を一部探究します: クロージャとイテレータです。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » まとめ","id":"ch12-06-writing-to-stderr-instead-of-stdout.html#aまとめ","title":"まとめ"},"ch12-06-writing-to-stderr-instead-of-stdout.html#aエラーが書き込まれる場所を確認する":{"body":"まず、 minigrep に出力される中身が、代わりに標準エラーに書き込みたいいかなるエラーメッセージも含め、 どのように標準出力に書き込まれているかを観察しましょう。意図的にエラーを起こしつつ、 ファイルに標準出力ストリームをリダイレクトすることでそうします。標準エラーストリームはリダイレクトしないので、 標準エラーに送られる内容は、すべて画面に表示され続けます。 コマンドラインプログラムは、エラーメッセージを標準エラー出力に送信していると期待されているので、 標準出力ストリームをファイルにリダイレクトしても、画面にエラーメッセージが見られます。 我々のプログラムは、現状、いい振る舞いをしていません: 代わりにファイルにエラーメッセージ出力を保存するところを、 目撃するところです! この動作をデモする方法は、 > と標準出力ストリームをリダイレクトする先のファイル名、 output.txt でプログラムを走らせることによります。 引数は何も渡さず、そうするとエラーが起きるはずです: $ cargo run > output.txt > 記法により、標準出力の中身を画面の代わりに output.txt に書き込むようシェルは指示されます。 画面に出力されると期待していたエラーメッセージは見られないので、ファイルに入っているということでしょう。 以下が output.txt が含んでいる内容です: Problem parsing arguments: not enough arguments そうです。エラーメッセージは標準出力に出力されているのです。このようなエラーメッセージは標準エラーに出力され、 成功した状態のデータのみがファイルに残ると遥かに有用です。それを変更します。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » エラーが書き込まれる場所を確認する","id":"ch12-06-writing-to-stderr-instead-of-stdout.html#aエラーが書き込まれる場所を確認する","title":"エラーが書き込まれる場所を確認する"},"ch12-06-writing-to-stderr-instead-of-stdout.html#aエラーを標準エラーに出力する":{"body":"リスト12-24のコードを使用して、エラーメッセージの出力の仕方を変更します。この章の前で行ったリファクタリングのため、 エラーメッセージを出力するコードはすべて1関数、 main にあります。標準ライブラリは、 標準エラーストリームに出力する eprintln! マクロを提供しているので、 println! を呼び出してエラーを出力していた2箇所を代わりに eprintln! を使うように変更しましょう。 ファイル名: src/main.rs fn main() { let args: Vec = env::args().collect(); let config = Config::new(&args).unwrap_or_else(|err| { eprintln!(\"Problem parsing arguments: {}\", err); process::exit(1); }); if let Err(e) = minigrep::run(config) { eprintln!(\"Application error: {}\", e); process::exit(1); }\n} リスト12-24: eprintln! を使って標準出力ではなく、標準エラーにエラーメッセージを書き込む println! を eprintln! に変えてから、再度同じようにプログラムを実行しましょう。 引数なしかつ、標準出力を > でリダイレクトしてね: $ cargo run > output.txt\nProblem parsing arguments: not enough arguments これで、エラーは画面に見えつつ、 output.txt は何も含まなくなり、これはコマンドラインプログラムに期待する動作です。 再度、標準出力をファイルにリダイレクトしてエラーは起こさない引数でプログラムを走らせましょう。以下のようにですね: $ cargo run to poem.txt > output.txt ターミナルには出力は見られず、 output.txt に結果が含まれます: ファイル名: output.txt Are you nobody, too?\nHow dreary to be somebody! これは、もう成功した出力には標準出力を、エラー出力には標準エラーを適切に使用していることをデモしています。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » エラーを標準エラーに出力する","id":"ch12-06-writing-to-stderr-instead-of-stdout.html#aエラーを標準エラーに出力する","title":"エラーを標準エラーに出力する"},"ch12-06-writing-to-stderr-instead-of-stdout.html#a標準出力ではなく標準エラーにエラーメッセージを書き込む":{"body":"現時点では、すべての出力を println! 関数を使用して端末に書き込んでいます。多くの端末は、 2種類の出力を提供します: 普通の情報用の 標準出力 ( stdout )とエラーメッセージ用の 標準エラー出力 ( stderr )です。 この差異のおかげで、ユーザは、エラーメッセージを画面に表示しつつ、 プログラムの成功した出力をファイルにリダイレクトすることを選択できます。 println! 関数は、標準出力に出力する能力しかないので、標準エラーに出力するには他のものを使用しなければなりません。","breadcrumbs":"入出力プロジェクト: コマンドラインプログラムを構築する » 標準出力ではなく標準エラーにエラーメッセージを書き込む","id":"ch12-06-writing-to-stderr-instead-of-stdout.html#a標準出力ではなく標準エラーにエラーメッセージを書き込む","title":"標準出力ではなく標準エラーにエラーメッセージを書き込む"},"ch13-00-functional-features.html#a関数型言語の機能-イテレータとクロージャ":{"body":"Rustの設計は、多くの既存の言語やテクニックにインスピレーションを得ていて、 その一つの大きな影響が 関数型プログラミング です。関数型でのプログラミングには、しばしば、 引数で渡したり、関数から関数を返したり、関数を後ほど使用するために変数に代入することで関数を値として使用することが含まれます。 この章では、関数型プログラミングがどんなものであったり、なかったりするかという問題については議論しませんが、 代わりに関数型とよく言及される多くの言語の機能に似たRustの機能の一部について議論しましょう。 具体的には、以下を講義します: クロージャ 、変数に保存できる関数に似た文法要素 イテレータ 、一連の要素を処理する方法 これら2つの機能を使用して第12章の入出力プロジェクトを改善する方法 これら2つの機能のパフォーマンス(ネタバレ: 思ったよりも速いです) パターンマッチングやenumなど、他のRustの機能も関数型に影響されていますが、他の章で講義してきました。 クロージャとイテレータをマスターすることは、慣用的で速いRustコードを書くことの重要な部分なので、 この章を丸ごと捧げます。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ","id":"ch13-00-functional-features.html#a関数型言語の機能-イテレータとクロージャ","title":"関数型言語の機能: イテレータとクロージャ"},"ch13-01-closures.html#aクロージャ-環境をキャプチャできる匿名関数":{"body":"Rustのクロージャは、変数に保存したり、引数として他の関数に渡すことのできる匿名関数です。 ある場所でクロージャを生成し、それから別の文脈でクロージャを呼び出して評価することができます。 関数と異なり、呼び出されたスコープの値をクロージャは、キャプチャすることができます。 これらのクロージャの機能がコードの再利用や、動作のカスタマイズを行わせてくれる方法を模擬しましょう。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » クロージャ: 環境をキャプチャできる匿名関数","id":"ch13-01-closures.html#aクロージャ-環境をキャプチャできる匿名関数","title":"クロージャ: 環境をキャプチャできる匿名関数"},"ch13-01-closures.html#aクロージャで動作の抽象化を行う":{"body":"クロージャを保存して後々使用できるようにするのが有用な場面の例に取り掛かりましょう。その過程で、 クロージャの記法、型推論、トレイトについて語ります。 以下のような架空の場面を考えてください: カスタマイズされたエクササイズのトレーニングプランを生成するアプリを作るスタートアップで働くことになりました。 バックエンドはRustで記述され、トレーニングプランを生成するアルゴリズムは、アプリユーザの年齢や、 BMI、運動の好み、最近のトレーニング、指定された強弱値などの多くの要因を考慮します。 実際に使用されるアルゴリズムは、この例では重要ではありません; 重要なのは、この計算が数秒要することです。 必要なときだけこのアルゴリズムを呼び出し、1回だけ呼び出したいので、必要以上にユーザを待たせないことになります。 リスト13-1に示した simulated_expensive_calculation 関数でこの仮定のアルゴリズムを呼び出すことをシミュレートし、 この関数は calculating slowly と出力し、2秒待ってから、渡した数値をなんでも返します。 ファイル名: src/main.rs use std::thread;\nuse std::time::Duration; fn simulated_expensive_calculation(intensity: u32) -> u32 { // ゆっくり計算します println!(\"calculating slowly...\"); thread::sleep(Duration::from_secs(2)); intensity\n} リスト13-1: 実行に約2秒かかる架空の計算の代役を務める関数 次は、この例で重要なトレーニングアプリの部分を含む main 関数です。この関数は、 ユーザがトレーニングプランを要求した時にアプリが呼び出すコードを表します。 アプリのフロントエンドと相互作用する部分は、クロージャの使用と関係ないので、プログラムへの入力を表す値をハードコードし、 その出力を表示します。 必要な入力は以下の通りです: ユーザの強弱値、これはユーザがトレーニングを要求して、低強度のトレーニングか、 高強度のトレーニングがしたいかを示したときに指定されます。 乱数、これはトレーニングプランにバリエーションを起こします。 出力は、推奨されるトレーニングプランになります。リスト13-2は使用する main 関数を示しています。 ファイル名: src/main.rs fn main() { let simulated_user_specified_value = 10; let simulated_random_number = 7; generate_workout( simulated_user_specified_value, simulated_random_number );\n}\n# fn generate_workout(intensity: u32, random_number: u32) {} リスト13-2: ユーザ入力や乱数生成をシミュレートするハードコードされた値がある main 関数 簡潔性のために、変数 simulated_user_specified_value は10、変数 simulated_random_number は7とハードコードしました; 実際のプログラムにおいては、強弱値はアプリのフロントエンドから取得し、乱数の生成には、第2章の数当てゲームの例のように、 rand クレートを使用するでしょう。 main 関数は、シミュレートされた入力値とともに generate_workout 関数を呼び出します。 今や文脈ができたので、アルゴリズムに取り掛かりましょう。リスト13-3の generate_workout 関数は、 この例で最も気にかかるアプリのビジネスロジックを含んでいます。この例での残りの変更は、 この関数に対して行われるでしょう: ファイル名: src/main.rs # use std::thread;\n# use std::time::Duration;\n#\n# fn simulated_expensive_calculation(num: u32) -> u32 {\n# println!(\"calculating slowly...\");\n# thread::sleep(Duration::from_secs(2));\n# num\n# }\n#\nfn generate_workout(intensity: u32, random_number: u32) { if intensity < 25 { println!( // 今日は{}回腕立て伏せをしてください! \"Today, do {} pushups!\", simulated_expensive_calculation(intensity) ); println!( // 次に、{}回腹筋をしてください! \"Next, do {} situps!\", simulated_expensive_calculation(intensity) ); } else { if random_number == 3 { // 今日は休憩してください!水分補給を忘れずに! println!(\"Take a break today! Remember to stay hydrated!\"); } else { println!( // 今日は、{}分間走ってください! \"Today, run for {} minutes!\", simulated_expensive_calculation(intensity) ); } }\n} リスト13-3: 入力に基づいてトレーニングプランを出力するビジネスロジックと、 simulated_expensive_calculation 関数の呼び出し リスト13-3のコードには、遅い計算を行う関数への呼び出しが複数あります。最初の if ブロックが、 simulated_expensive_calculation を2回呼び出し、外側の else 内の if は全く呼び出さず、 2番目の else ケースの内側にあるコードは1回呼び出しています。 generate_workout 関数の期待される振る舞いは、まずユーザが低強度のトレーニング(25より小さい数値で表される)か、 高強度のトレーニング(25以上の数値)を欲しているか確認することです。 低強度のトレーニングプランは、シミュレーションしている複雑なアルゴリズムに基づいて、 多くの腕立て伏せや腹筋運動を推奨してきます。 ユーザが高強度のトレーニングを欲していれば、追加のロジックがあります: アプリが生成した乱数がたまたま3なら、 アプリは休憩と水分補給を勧めます。そうでなければ、ユーザは複雑なアルゴリズムに基づいて数分間のランニングをします。 このコードは現在、ビジネスのほしいままに動くでしょうが、データサイエンスチームが、 simulated_expensive_calculation 関数を呼び出す方法に何らかの変更を加える必要があると決定したとしましょう。 そのような変更が起きた時に更新を簡略化するため、 simulated_expensive_calculation 関数を1回だけ呼び出すように、 このコードをリファクタリングしたいです。また、その過程でその関数への呼び出しを増やすことなく無駄に2回、 この関数を現時点で呼んでいるところを切り捨てたくもあります。要するに、結果が必要なければ関数を呼び出したくなく、 それでも1回だけ呼び出したいのです。 関数でリファクタリング 多くの方法でトレーニングプログラムを再構築することもできます。 1番目に simulated_expensive_calculation 関数への重複した呼び出しを変数に抽出しようとしましょう。リスト13-4に示したように。 ファイル名: src/main.rs # use std::thread;\n# use std::time::Duration;\n#\n# fn simulated_expensive_calculation(num: u32) -> u32 {\n# println!(\"calculating slowly...\");\n# thread::sleep(Duration::from_secs(2));\n# num\n# }\n#\nfn generate_workout(intensity: u32, random_number: u32) { let expensive_result = simulated_expensive_calculation(intensity); if intensity < 25 { println!( \"Today, do {} pushups!\", expensive_result ); println!( \"Next, do {} situps!\", expensive_result ); } else { if random_number == 3 { println!(\"Take a break today! Remember to stay hydrated!\"); } else { println!( \"Today, run for {} minutes!\", expensive_result ); } }\n} リスト13-4: 複数の simulated_expensive_calculation の呼び出しを1箇所に抽出し、 結果を expensive_result 変数に保存する この変更により simulated_expensive_calculation の呼び出しが単一化され、 最初の if ブロックが無駄に関数を2回呼んでいた問題を解決します。不幸なことに、これでは、 あらゆる場合にこの関数を呼び出し、その結果を待つことになり、結果値を全く使用しない内側の if ブロックでもそうしてしまいます。 プログラムの1箇所でコードを定義したいですが、結果が本当に必要なところでだけコードを 実行 します。 これは、クロージャのユースケースです! クロージャでリファクタリングして、コードを保存する if ブロックの前にいつも simulated_expensive_calculation 関数を呼び出す代わりに、 クロージャを定義し、関数呼び出しの結果を保存するのではなく、その クロージャ を変数に保存できます。リスト13-5のようにですね。 simulated_expensive_calculation の本体全体を実際に、ここで導入しているクロージャ内に移すことができます。 ファイル名: src/main.rs # use std::thread;\n# use std::time::Duration;\n#\nlet expensive_closure = |num| { println!(\"calculating slowly...\"); thread::sleep(Duration::from_secs(2)); num\n};\n# expensive_closure(5); リスト13-5: クロージャを定義し、 expensive_closure 変数に保存する クロージャ定義が = に続き、変数 expensive_closure に代入されています。クロージャを定義するには、 1組の縦棒から始め、その内部にクロージャの仮引数を指定します; この記法は、SmalltalkやRubyのクロージャ定義と類似していることから、 選択されました。このクロージャには、 num という引数が1つあります: 2つ以上引数があるなら、 |param1, param2| のように、カンマで区切ります。 引数の後に、クロージャの本体を保持する波括弧を配置します(これはクロージャ本体が式一つなら省略可能です)。 波括弧の後、クロージャのお尻には、セミコロンが必要で、 let 文を完成させます。クロージャ本体の最後の行から返る値( num )が、 呼び出された時にクロージャから返る値になります。その行がセミコロンで終わっていないからです; ちょうど関数の本体みたいですね。 この let 文は、 expensive_closure が、匿名関数を呼び出した 結果の値 ではなく、 匿名関数の 定義 を含むことを意味することに注意してください。コードを定義して、 1箇所で呼び出し、そのコードを保存し、後々、それを呼び出したいがためにクロージャを使用していることを思い出してください; 呼び出したいコードは、現在、 expensive_closure に保存されています。 クロージャが定義されたので、 if ブロックのコードを変更して、そのコードを実行するクロージャを呼び出し、結果値を得ることができます。 クロージャは、関数のように呼び出せます: クロージャ定義を含む変数名を指定し、使用したい引数値を含むかっこを続けます。 リスト13-6に示したようにですね。 ファイル名: src/main.rs # use std::thread;\n# use std::time::Duration;\n#\nfn generate_workout(intensity: u32, random_number: u32) { let expensive_closure = |num| { println!(\"calculating slowly...\"); thread::sleep(Duration::from_secs(2)); num }; if intensity < 25 { println!( \"Today, do {} pushups!\", expensive_closure(intensity) ); println!( \"Next, do {} situps!\", expensive_closure(intensity) ); } else { if random_number == 3 { println!(\"Take a break today! Remember to stay hydrated!\"); } else { println!( \"Today, run for {} minutes!\", expensive_closure(intensity) ); } }\n} リスト13-6: 定義した expensive_closure を呼び出す 今では、重い計算はたった1箇所でのみ呼び出され、その結果が必要なコードを実行するだけになりました。 ところが、リスト13-3の問題の一つを再浮上させてしまいました: それでも、最初の if ブロックでクロージャを2回呼んでいて、 そうすると、重いコードを2回呼び出し、必要な分の2倍ユーザを待たせてしまいます。その if ブロックのみに属する変数を生成して、 クロージャの呼び出し結果を保持するその if ブロックに固有の変数を生成することでこの問題を解消することもできますが、 クロージャは他の解決法も用意してくれます。その解決策については、もう少し先で語りましょう。でもまずは、 クロージャ定義に型注釈がない理由とクロージャに関わるトレイトについて話しましょう。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » クロージャで動作の抽象化を行う","id":"ch13-01-closures.html#aクロージャで動作の抽象化を行う","title":"クロージャで動作の抽象化を行う"},"ch13-01-closures.html#aクロージャで環境をキャプチャする":{"body":"トレーニング生成の例においては、クロージャをインラインの匿名関数として使っただけでした。しかし、 クロージャには、関数にはない追加の能力があります: 環境をキャプチャし、 自分が定義されたスコープの変数にアクセスできるのです。 リスト13-12は、 equal_to_x 変数に保持されたクロージャを囲む環境から x 変数を使用するクロージャの例です。 ファイル名: src/main.rs fn main() { let x = 4; let equal_to_x = |z| z == x; let y = 4; assert!(equal_to_x(y));\n} リスト13-12: 内包するスコープの変数を参照するクロージャの例 ここで、 x は equal_to_x の引数でもないのに、 equal_to_x が定義されているのと同じスコープで定義されている x 変数を equal_to_x クロージャは使用できています。 同じことを関数では行うことができません; 以下の例で試したら、コードはコンパイルできません: ファイル名: src/main.rs fn main() { let x = 4; fn equal_to_x(z: i32) -> bool { z == x } let y = 4; assert!(equal_to_x(y));\n} エラーが出ます: error[E0434]: can't capture dynamic environment in a fn item; use the || { ...\n} closure form instead\n(エラー: fn要素では動的な環境をキャプチャできません; 代わりに|| { ... }のクロージャ形式を\n使用してください) --> src/main.rs |\n4 | fn equal_to_x(z: i32) -> bool { z == x } | ^ コンパイラは、この形式はクロージャでのみ動作することさえも思い出させてくれています! クロージャが環境から値をキャプチャすると、メモリを使用してクロージャ本体で使用できるようにその値を保存します。 このメモリ使用は、環境をキャプチャしないコードを実行するようなもっと一般的な場合には払いたくないオーバーヘッドです。 関数は、絶対に環境をキャプチャすることが許可されていないので、関数を定義して使えば、このオーバーヘッドを招くことは絶対にありません。 クロージャは、3つの方法で環境から値をキャプチャでき、この方法は関数が引数を取れる3つの方法に直に対応します: 所有権を奪う、可変で借用する、不変で借用するです。これらは、以下のように3つの Fn トレイトでコード化されています: FnOnce は、クロージャの 環境 として知られている内包されたスコープからキャプチャした変数を消費します。 キャプチャした変数を消費するために、定義された際にクロージャはこれらの変数の所有権を奪い、 自身にムーブするのです。名前のうち、 Once の部分は、 このクロージャは同じ変数の所有権を2回以上奪うことができないという事実を表しているので、1回しか呼ぶことができないのです。 FnMut は、可変で値を借用するので、環境を変更することができます。 Fn は、環境から値を不変で借用します。 クロージャを生成する時、クロージャが環境を使用する方法に基づいて、コンパイラはどのトレイトを使用するか推論します。 少なくとも1回は呼び出されるので、全てのクロージャは FnOnce を実装しています。キャプチャした変数をムーブしないクロージャは、 FnMut も実装し、キャプチャした変数に可変でアクセスする必要のないクロージャは、 Fn も実装しています。 リスト13-12では、 equal_to_x クロージャは x を不変で借用しています(ゆえに equal_to_x は Fn トレイトです)。 クロージャの本体は、 x を読む必要しかないからです。 環境でクロージャが使用している値の所有権を奪うことをクロージャに強制したいなら、引数リストの前に move キーワードを使用できます。 このテクニックは、新しいスレッドにデータが所有されるように、クロージャを新しいスレッドに渡して、 データをムーブする際に大概は有用です。 並行性について語る第16章で、 move クロージャの例はもっと多く出てきます。とりあえず、 こちらが move キーワードがクロージャ定義に追加され、整数の代わりにベクタを使用するリスト13-12からのコードです。 整数はムーブではなく、コピーされてしまいますからね; このコードはまだコンパイルできないことに注意してください。 ファイル名: src/main.rs fn main() { let x = vec![1, 2, 3]; let equal_to_x = move |z| z == x; // ここでは、xを使用できません: {:?} println!(\"can't use x here: {:?}\", x); let y = vec![1, 2, 3]; assert!(equal_to_x(y));\n} 以下のようなエラーを受けます: error[E0382]: use of moved value: `x`\n(エラー: ムーブされた値の使用: `x`) --> src/main.rs:6:40 |\n4 | let equal_to_x = move |z| z == x; | -------- value moved (into closure) here (値はここで(クロージャに)ムーブされた)\n5 |\n6 | println!(\"can't use x here: {:?}\", x); | ^ value used here after move (ムーブ後、値はここで使用された) | = note: move occurs because `x` has type `std::vec::Vec`, which does not implement the `Copy` trait (注釈: `x`が`std::vec::Vec`という`Copy`トレイトを実装しない型のため、ムーブが起きました) クロージャが定義された際に、クロージャに x の値はムーブされています。 move キーワードを追加したからです。 そして、クロージャは x の所有権を持ち、 main が println! で x を使うことはもう叶わないのです。 println! を取り除けば、この例は修正されます。 Fn トレイトのどれかを指定するほとんどの場合、 Fn から始めると、コンパイラがクロージャ本体内で起こっていることにより、 FnMut や FnOnce が必要な場合、教えてくれるでしょう。 環境をキャプチャできるクロージャが関数の引数として有用な場面を説明するために、次のトピックに移りましょう: イテレータです。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » クロージャで環境をキャプチャする","id":"ch13-01-closures.html#aクロージャで環境をキャプチャする","title":"クロージャで環境をキャプチャする"},"ch13-01-closures.html#aクロージャの型推論と注釈":{"body":"クロージャでは、 fn 関数のように引数の型や戻り値の型を注釈する必要はありません。関数では、 型注釈は必要です。ユーザに露出する明示的なインターフェイスの一部だからです。このインターフェイスを堅実に定義することは、 関数が使用したり、返したりする値の型についてみんなが合意していることを保証するために重要なのです。 しかし、クロージャはこのような露出するインターフェイスには使用されません: 変数に保存され、 名前付けしたり、ライブラリの使用者に晒されることなく、使用されます。 クロージャは通常短く、あらゆる任意の筋書きではなく、狭い文脈でのみ関係します。 このような限定された文脈内では、コンパイラは、多くの変数の型を推論できるのと似たように、 引数や戻り値の型を頼もしく推論することができます。 このような小さく匿名の関数で型をプログラマに注釈させることは煩わしいし、コンパイラがすでに利用可能な情報と大きく被っています。 本当に必要な以上に冗長になることと引き換えに、明示性と明瞭性を向上させたいなら、変数に型注釈を加えることもできます; リスト13-5で定義したクロージャに型を注釈するなら、リスト13-7に示した定義のようになるでしょう。 ファイル名: src/main.rs # use std::thread;\n# use std::time::Duration;\n#\nlet expensive_closure = |num: u32| -> u32 { println!(\"calculating slowly...\"); thread::sleep(Duration::from_secs(2)); num\n}; リスト13-7: クロージャの引数と戻り値の省略可能な型注釈を追加する 型注釈を付け加えると、クロージャの記法は、関数の記法により酷似して見えます。以下が、引数に1を加える関数の定義と、 同じ振る舞いをするクロージャの定義の記法を縦に比べたものです。 空白を追加して、関連のある部分を並べています。これにより、縦棒の使用と省略可能な記法の量を除いて、 クロージャ記法が関数記法に似ているところを説明しています。 fn add_one_v1 (x: u32) -> u32 { x + 1 }\nlet add_one_v2 = |x: u32| -> u32 { x + 1 };\nlet add_one_v3 = |x| { x + 1 };\nlet add_one_v4 = |x| x + 1 ; 1行目が関数定義を示し、2行目がフルに注釈したクロージャ定義を示しています。 3行目は、クロージャ定義から型注釈を取り除き、4行目は、かっこを取り除いていて、 かっこはクロージャの本体がただ1つの式からなるので、省略可能です。これらは全て、 呼び出された時に同じ振る舞いになる合法な定義です。 クロージャ定義には、引数それぞれと戻り値に対して推論される具体的な型が一つあります。例えば、 リスト13-8に引数として受け取った値を返すだけの短いクロージャの定義を示しました。 このクロージャは、この例での目的以外には有用ではありません。この定義には、 何も型注釈を加えていないことに注意してください: それから1回目に String を引数に、 2回目に u32 を引数に使用してこのクロージャを2回呼び出そうとしたら、エラーになります。 ファイル名: src/main.rs let example_closure = |x| x; let s = example_closure(String::from(\"hello\"));\nlet n = example_closure(5); リスト13-8: 2つの異なる型で型が推論されるクロージャの呼び出しを試みる コンパイラは、次のエラーを返します: error[E0308]: mismatched types --> src/main.rs | | let n = example_closure(5); | ^ expected struct `std::string::String`, found integral variable | = note: expected type `std::string::String` found type `{integer}` String 値で example_closure を呼び出した最初の時点で、コンパイラは x とクロージャの戻り値の型を String と推論します。 そして、その型が example_closure のクロージャに閉じ込められ、同じクロージャを異なる型で使用しようとすると、 型エラーが出るのです。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » クロージャの型推論と注釈","id":"ch13-01-closures.html#aクロージャの型推論と注釈","title":"クロージャの型推論と注釈"},"ch13-01-closures.html#aジェネリック引数と-fn-トレイトを使用してクロージャを保存する":{"body":"トレーニング生成アプリに戻りましょう。リスト13-6において、まだコードは必要以上の回数、重い計算のクロージャを呼んでいました。 この問題を解決する一つの選択肢は、重いクロージャの結果を再利用できるように変数に保存し、クロージャを再度呼ぶ代わりに、 結果が必要になる箇所それぞれでその変数を使用することです。しかしながら、この方法は同じコードを大量に繰り返す可能性があります。 運のいいことに、別の解決策もあります。クロージャやクロージャの呼び出し結果の値を保持する構造体を作れるのです。 結果の値が必要な場合のみにその構造体はクロージャを実行し、その結果の値をキャッシュするので、残りのコードは、 結果を保存し、再利用する責任を負わなくて済むのです。このパターンは、 メモ化 (memoization)または、 遅延評価 (lazy evaluation)として知っているかもしれません。 クロージャを保持する構造体を作成するために、クロージャの型を指定する必要があります。 構造体定義は、各フィールドの型を把握しておく必要がありますからね。各クロージャインスタンスには、 独自の匿名の型があります: つまり、たとえ2つのクロージャが全く同じシグニチャでも、その型はそれでも違うものと考えられるということです。 クロージャを使用する構造体、enum、関数引数を定義するには、第10章で議論したように、 ジェネリクスとトレイト境界を使用します。 Fn トレイトは、標準ライブラリで用意されています。全てのクロージャは、以下のいずれかのトレイトを実装しています: Fn 、 FnMut または、 FnOnce です。「クロージャで環境をキャプチャする」節で、これらのトレイト間の差異を議論します; この例では、 Fn トレイトを使えます。 Fn トレイト境界にいくつかの型を追加することで、このトレイト境界に合致するクロージャが持つべき引数と戻り値の型を示します。 今回のクロージャは u32 型の引数を一つ取り、 u32 を返すので、指定するトレイト境界は Fn(u32) -> u32 になります。 リスト13-9は、クロージャとオプションの結果値を保持する Cacher 構造体の定義を示しています。 ファイル名: src/main.rs struct Cacher where T: Fn(u32) -> u32\n{ calculation: T, value: Option,\n} リスト13-9: クロージャを calculation に、オプションの結果値を value に保持する Cacher 構造体を定義する Cacher 構造体は、ジェネリックな型 T の calculation フィールドを持ちます。 T のトレイト境界は、 Fn トレイトを使うことでクロージャであると指定しています。 calculation フィールドに保存したいクロージャは全て、 1つの u32 引数( Fn の後の括弧内で指定されている)を取り、 u32 ( -> の後に指定されている)を返さなければなりません。 注釈: 関数も3つの Fn トレイト全部を実装します。もし環境から値をキャプチャする必要がなければ、 Fn トレイトを実装する何かが必要になるクロージャではなく、関数を使用できます。 value フィールドの型は、 Option です。クロージャを実行する前に、 value は None になるでしょう。 Cacher を使用するコードがクロージャの 結果 を求めてきたら、その時点で Cacher はクロージャを実行し、 その結果を value フィールドの Some 列挙子に保存します。それから、コードが再度クロージャの結果を求めたら、 クロージャを再実行するのではなく、 Cacher は Some 列挙子に保持された結果を返すでしょう。 たった今解説した value フィールド周りのロジックは、リスト13-10で定義されています。 ファイル名: src/main.rs # struct Cacher\n# where T: Fn(u32) -> u32\n# {\n# calculation: T,\n# value: Option,\n# }\n#\nimpl Cacher where T: Fn(u32) -> u32\n{ fn new(calculation: T) -> Cacher { Cacher { calculation, value: None, } } fn value(&mut self, arg: u32) -> u32 { match self.value { Some(v) => v, None => { let v = (self.calculation)(arg); self.value = Some(v); v }, } }\n} リスト13-10: Cacher のキャッシュ機構 呼び出し元のコードにこれらのフィールドの値を直接変えてもらうのではなく、 Cacher に構造体のフィールドの値を管理してほしいので、 これらのフィールドは非公開になっています。 Cacher::new 関数はジェネリックな引数の T を取り、 Cacher 構造体と同じトレイト境界を持つよう定義しました。 それから calculation フィールドに指定されたクロージャと、 value フィールドに None 値を保持する Cacher インスタンスを Cacher::new は返します。 まだクロージャを実行していないからですね。 呼び出し元のコードがクロージャの評価結果を必要としたら、クロージャを直接呼ぶ代わりに、 value メソッドを呼びます。 このメソッドは、結果の値が self.value の Some に既にあるかどうか確認します; そうなら、 クロージャを再度実行することなく Some 内の値を返します。 self.value が None なら、コードは self.calculation に保存されたクロージャを呼び出し、 結果を将来使えるように self.value に保存し、その値を返しもします。 リスト13-11は、リスト13-6の関数 generate_workout でこの Cacher 構造体を使用する方法を示しています。 ファイル名: src/main.rs # use std::thread;\n# use std::time::Duration;\n#\n# struct Cacher\n# where T: Fn(u32) -> u32\n# {\n# calculation: T,\n# value: Option,\n# }\n#\n# impl Cacher\n# where T: Fn(u32) -> u32\n# {\n# fn new(calculation: T) -> Cacher {\n# Cacher {\n# calculation,\n# value: None,\n# }\n# }\n#\n# fn value(&mut self, arg: u32) -> u32 {\n# match self.value {\n# Some(v) => v,\n# None => {\n# let v = (self.calculation)(arg);\n# self.value = Some(v);\n# v\n# },\n# }\n# }\n# }\n#\nfn generate_workout(intensity: u32, random_number: u32) { let mut expensive_result = Cacher::new(|num| { println!(\"calculating slowly...\"); thread::sleep(Duration::from_secs(2)); num }); if intensity < 25 { println!( \"Today, do {} pushups!\", expensive_result.value(intensity) ); println!( \"Next, do {} situps!\", expensive_result.value(intensity) ); } else { if random_number == 3 { println!(\"Take a break today! Remember to stay hydrated!\"); } else { println!( \"Today, run for {} minutes!\", expensive_result.value(intensity) ); } }\n} リスト13-11: generate_workout 関数内で Cacher を使用し、キャッシュ機構を抽象化する クロージャを変数に直接保存する代わりに、クロージャを保持する Cacher の新規インスタンスを保存しています。 そして、結果が必要な場所それぞれで、その Cacher インスタンスに対して value メソッドを呼び出しています。 必要なだけ value メソッドを呼び出したり、全く呼び出さないこともでき、重い計算は最大でも1回しか走りません。 リスト13-2の main 関数とともにこのプログラムを走らせてみてください。 simulated_user_specified_value と simulated_random_number 変数の値を変えて、 いろんな if や else ブロックの場合全てで、 calculating slowly は1回だけ、必要な時にのみ出現することを実証してください。 必要以上に重い計算を呼び出さないことを保証するのに必要なロジックの面倒を Cacher は見るので、 generate_workout はビジネスロジックに集中できるのです。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » ジェネリック引数と Fn トレイトを使用してクロージャを保存する","id":"ch13-01-closures.html#aジェネリック引数と-fn-トレイトを使用してクロージャを保存する","title":"ジェネリック引数と Fn トレイトを使用してクロージャを保存する"},"ch13-01-closures.html#cacher-実装の限界":{"body":"値をキャッシュすることは、コードの他の部分でも異なるクロージャで行いたくなる可能性のある一般的に有用な振る舞いです。 しかし、現在の Cacher の実装には、他の文脈で再利用することを困難にしてしまう問題が2つあります。 1番目の問題は、 Cacher インスタンスが、常に value メソッドの引数 arg に対して同じ値になると想定していることです。 言い換えると、 Cacher のこのテストは、失敗するでしょう: #[test]\nfn call_with_different_values() { let mut c = Cacher::new(|a| a); let v1 = c.value(1); let v2 = c.value(2); assert_eq!(v2, 2);\n} このテストは、渡された値を返すクロージャを伴う Cacher インスタンスを新しく生成しています。 この Cacher インスタンスに対して1という arg 値で呼び出し、それから2という arg 値で呼び出し、 2という arg 値の value 呼び出しは2を返すべきと期待しています。 このテストをリスト13-9とリスト13-10の Cacher 実装で動かすと、 assert_eq からこんなメッセージが出て、 テストは失敗します: thread 'call_with_different_values' panicked at 'assertion failed: `(left == right)` left: `1`, right: `2`', src/main.rs 問題は、初めて c.value を1で呼び出した時に、 Cacher インスタンスは self.value に Some(1) を保存したことです。 その後 value メソッドに何を渡しても、常に1を返すわけです。 単独の値ではなく、ハッシュマップを保持するように Cacher を改変してみてください。ハッシュマップのキーは、 渡される arg 値になり、ハッシュマップの値は、そのキーでクロージャを呼び出した結果になるでしょう。 self.value が直接 Some か None 値であることを調べる代わりに、 value 関数はハッシュマップの arg を調べ、 存在するならその値を返します。存在しないなら、 Cacher はクロージャを呼び出し、 arg 値に紐づけてハッシュマップに結果の値を保存します。 現在の Cacher 実装の2番目の問題は、引数の型に u32 を一つ取り、 u32 を返すクロージャしか受け付けないことです。 例えば、文字列スライスを取り、 usize を返すクロージャの結果をキャッシュしたくなるかもしれません。 この問題を修正するには、 Cacher 機能の柔軟性を向上させるためによりジェネリックな引数を導入してみてください。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » Cacher 実装の限界","id":"ch13-01-closures.html#cacher-実装の限界","title":"Cacher 実装の限界"},"ch13-02-iterators.html#aイテレータを消費するメソッド":{"body":"Iterator トレイトには、標準ライブラリが提供してくれているデフォルト実装のある多くの異なるメソッドがあります; Iterator トレイトの標準ライブラリのAPIドキュメントを検索することで、これらのメソッドについて知ることができます。 これらのメソッドの中には、定義内で next メソッドを呼ぶものもあり、故に Iterator トレイトを実装する際には、 next メソッドを実装する必要があるのです。 next を呼び出すメソッドは、 消費アダプタ (consuming adaptors)と呼ばれます。呼び出しがイテレータの使い込みになるからです。 一例は、 sum メソッドで、これはイテレータの所有権を奪い、 next を繰り返し呼び出すことで要素を繰り返し、 故にイテレータを消費するのです。繰り返しが進むごとに、各要素を一時的な合計に追加し、 繰り返しが完了したら、その合計を返します。リスト13-16は、 sum の使用を説明したテストです: ファイル名: src/lib.rs #[test]\nfn iterator_sum() { let v1 = vec![1, 2, 3]; let v1_iter = v1.iter(); let total: i32 = v1_iter.sum(); assert_eq!(total, 6);\n} リスト13-16: sum メソッドを呼び出してイテレータの全要素の合計を得る sum は呼び出し対象のイテレータの所有権を奪うので、 sum 呼び出し後に v1_iter を使用することはできません。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » イテレータを消費するメソッド","id":"ch13-02-iterators.html#aイテレータを消費するメソッド","title":"イテレータを消費するメソッド"},"ch13-02-iterators.html#a一連の要素をイテレータで処理する":{"body":"イテレータパターンにより、一連の要素に順番に何らかの作業を行うことができます。イテレータは、 各要素を繰り返し、シーケンスが終わったことを決定するロジックの責任を負います。イテレータを使用すると、 自身でそのロジックを再実装する必要がなくなるのです。 Rustにおいて、イテレータは 怠惰 です。つまり、イテレータを使い込んで消費するメソッドを呼ぶまで何の効果もないということです。 例えば、リスト13-13のコードは、 Vec に定義された iter メソッドを呼ぶことで v1 ベクタの要素に対するイテレータを生成しています。 このコード単独では、何も有用なことはしません。 let v1 = vec![1, 2, 3]; let v1_iter = v1.iter(); リスト13-13: イテレータを生成する 一旦イテレータを生成したら、いろんな手段で使用することができます。第3章のリスト3-5では、 ここまで iter の呼び出しが何をするかごまかしてきましたが、 for ループでイテレータを使い、 各要素に何かコードを実行しています。 リスト13-14の例は、イテレータの生成と for ループでイテレータを使用することを区別しています。 イテレータは、 v1_iter 変数に保存され、その時には繰り返しは起きていません。 v1_iter のイテレータで、 for ループが呼び出された時に、イテレータの各要素がループの繰り返しで使用され、各値が出力されます。 let v1 = vec![1, 2, 3]; let v1_iter = v1.iter(); for val in v1_iter { // {}でした println!(\"Got: {}\", val);\n} リスト13-14: for ループでイテレータを使用する 標準ライブラリにより提供されるイテレータが存在しない言語では、変数を添え字0から始め、 その変数でベクタに添え字アクセスして値を得て、ベクタの総要素数に到達するまでループでその変数の値をインクリメントすることで、 この同じ機能を書く可能性が高いでしょう。 イテレータはそのロジック全てを処理してくれるので、めちゃくちゃにしてしまう可能性のあるコードの繰り返しを減らしてくれます。 イテレータにより、添え字を使えるデータ構造、ベクタなどだけではなく、多くの異なるシーケンスに対して同じロジックを使う柔軟性も得られます。 イテレータがそれをする方法を調査しましょう。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » 一連の要素をイテレータで処理する","id":"ch13-02-iterators.html#a一連の要素をイテレータで処理する","title":"一連の要素をイテレータで処理する"},"ch13-02-iterators.html#a他のイテレータを生成するメソッド":{"body":"Iterator トレイトに定義された他のメソッドは、 イテレータアダプタ (iterator adaptors)として知られていますが、 イテレータを別の種類のイテレータに変えさせてくれます。イテレータアダプタを複数回呼ぶ呼び出しを連結して、 複雑な動作を読みやすい形で行うことができます。ですが、全てのイテレータは怠惰なので、消費アダプタメソッドのどれかを呼び出し、 イテレータアダプタの呼び出しから結果を得なければなりません。 リスト13-17は、イテレータアダプタメソッドの map の呼び出し例を示し、各要素に対して呼び出すクロージャを取り、 新しいイテレータを生成します。ここのクロージャは、ベクタの各要素が1インクリメントされる新しいイテレータを作成します。 ところが、このコードは警告を発します: ファイル名: src/main.rs let v1: Vec = vec![1, 2, 3]; v1.iter().map(|x| x + 1); リスト13-17: イテレータアダプタの map を呼び出して新規イテレータを作成する 出る警告は以下の通りです: warning: unused `std::iter::Map` which must be used: iterator adaptors are lazy\nand do nothing unless consumed\n(警告: 使用されねばならない`std::iter::Map`が未使用です: イテレータアダプタは怠惰で、\n消費されるまで何もしません) --> src/main.rs:4:5 |\n4 | v1.iter().map(|x| x + 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: #[warn(unused_must_use)] on by default リスト13-17のコードは何もしません; 指定したクロージャは、決して呼ばれないのです。警告が理由を思い出させてくれています: イテレータアダプタは怠惰で、ここでイテレータを消費する必要があるのです。 これを修正し、イテレータを消費するには、 collect メソッドを使用しますが、これは第12章のリスト12-1で env::args とともに使用しました。 このメソッドはイテレータを消費し、結果の値をコレクションデータ型に集結させます。 リスト13-18において、 map 呼び出しから返ってきたイテレータを繰り返した結果をベクタに集結させています。 このベクタは、最終的に元のベクタの各要素に1を足したものが含まれます。 ファイル名: src/main.rs let v1: Vec = vec![1, 2, 3]; let v2: Vec<_> = v1.iter().map(|x| x + 1).collect(); assert_eq!(v2, vec![2, 3, 4]); リスト13-18: map メソッドを呼び出して新規イテレータを作成し、 それから collect メソッドを呼び出してその新規イテレータを消費し、ベクタを生成する map はクロージャを取るので、各要素に対して行いたいどんな処理も指定することができます。 これは、 Iterator トレイトが提供する繰り返し動作を再利用しつつ、 クロージャにより一部の動作をカスタマイズできる好例になっています。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » 他のイテレータを生成するメソッド","id":"ch13-02-iterators.html#a他のイテレータを生成するメソッド","title":"他のイテレータを生成するメソッド"},"ch13-02-iterators.html#a環境をキャプチャするクロージャを使用する":{"body":"イテレータが出てきたので、 filter イテレータアダプタを使って環境をキャプチャするクロージャの一般的な使用をデモすることができます。 イテレータの filter メソッドは、イテレータの各要素を取り、論理値を返すクロージャを取ります。 このクロージャが true を返せば、 filter が生成するイテレータにその値が含まれます。クロージャが false を返したら、 結果のイテレータにその値は含まれません。 リスト13-19では、環境から shoe_size 変数をキャプチャするクロージャで filter を使って、 Shoe 構造体インスタンスのコレクションを繰り返しています。指定したサイズの靴だけを返すわけです。 ファイル名: src/lib.rs #[derive(PartialEq, Debug)]\nstruct Shoe { size: u32, style: String,\n} fn shoes_in_my_size(shoes: Vec, shoe_size: u32) -> Vec { shoes.into_iter() .filter(|s| s.size == shoe_size) .collect()\n} #[test]\nfn filters_by_size() { let shoes = vec![ Shoe { size: 10, style: String::from(\"sneaker\") }, Shoe { size: 13, style: String::from(\"sandal\") }, Shoe { size: 10, style: String::from(\"boot\") }, ]; let in_my_size = shoes_in_my_size(shoes, 10); assert_eq!( in_my_size, vec![ Shoe { size: 10, style: String::from(\"sneaker\") }, Shoe { size: 10, style: String::from(\"boot\") }, ] );\n} リスト13-19: shoe_size をキャプチャするクロージャで filter メソッドを使用する shoes_in_my_size 関数は、引数として靴のベクタとサイズの所有権を奪います。指定されたサイズの靴だけを含むベクタを返します。 shoes_in_my_size の本体で、 into_iter を呼び出してベクタの所有権を奪うイテレータを作成しています。 そして、 filter を呼び出してそのイテレータをクロージャが true を返した要素だけを含む新しいイテレータに適合させます。 クロージャは、環境から shoe_size 引数をキャプチャし、指定されたサイズの靴だけを保持しながら、 その値を各靴のサイズと比較します。最後に、 collect を呼び出すと、 関数により返ってきたベクタに適合させたイテレータから返ってきた値が集まるのです。 shoes_in_my_size を呼び出した時に、指定した値と同じサイズの靴だけが得られることをテストは示しています。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » 環境をキャプチャするクロージャを使用する","id":"ch13-02-iterators.html#a環境をキャプチャするクロージャを使用する","title":"環境をキャプチャするクロージャを使用する"},"ch13-02-iterators.html#iterator-トレイトで独自のイテレータを作成する":{"body":"ベクタに対し、 iter 、 into_iter 、 iter_mut を呼び出すことでイテレータを作成できることを示してきました。 ハッシュマップなどの標準ライブラリの他のコレクション型からもイテレータを作成できます。 Iterator トレイトを自分で実装することで、したいことを何でもするイテレータを作成することもできます。 前述の通り、定義を提供する必要のある唯一のメソッドは、 next メソッドなのです。一旦、そうしてしまえば、 Iterator トレイトが用意しているデフォルト実装のある他の全てのメソッドを使うことができるのです! デモ用に、絶対に1から5をカウントするだけのイテレータを作成しましょう。まず、値を保持する構造体を生成し、 Iterator トレイトを実装することでこの構造体をイテレータにし、その実装内の値を使用します。 リスト13-20は、 Counter 構造体と Counter のインスタンスを作る new 関連関数の定義です: ファイル名: src/lib.rs struct Counter { count: u32,\n} impl Counter { fn new() -> Counter { Counter { count: 0 } }\n} リスト13-20: Counter 構造体と count に対して0という初期値で Counter のインスタンスを作る new 関数を定義する Counter 構造体には、 count というフィールドがあります。このフィールドは、 1から5までの繰り返しのどこにいるかを追いかける u32 値を保持しています。 Counter の実装にその値を管理してほしいので、 count フィールドは非公開です。 count フィールドは常に0という値から新規インスタンスを開始するという動作を new 関数は強要します。 次に、 next メソッドの本体をこのイテレータが使用された際に起きてほしいことを指定するように定義して、 Counter 型に対して Iterator トレイトを実装します。リスト13-21のようにですね: ファイル名: src/lib.rs # struct Counter {\n# count: u32,\n# }\n#\nimpl Iterator for Counter { type Item = u32; fn next(&mut self) -> Option { self.count += 1; if self.count < 6 { Some(self.count) } else { None } }\n} リスト13-21: Counter 構造体に Iterator トレイトを実装する イテレータの Item 関連型を u32 に設定しました。つまり、イテレータは、 u32 の値を返します。 ここでも、まだ関連型について心配しないでください。第19章で講義します。 イテレータに現在の状態に1を足してほしいので、まず1を返すように count を0に初期化しました。 count の値が5以下なら、 next は Some に包まれた現在の値を返しますが、 count が6以上なら、イテレータは None を返します。 Counter イテレータの next メソッドを使用する 一旦 Iterator トレイトを実装し終わったら、イテレータの出来上がりです!リスト13-22は、 リスト13-15のベクタから生成したイテレータと全く同様に、直接 next メソッドを呼び出すことで、 Counter 構造体のイテレータ機能を使用できることをデモするテストを示しています。 ファイル名: src/lib.rs # struct Counter {\n# count: u32,\n# }\n#\n# impl Iterator for Counter {\n# type Item = u32;\n#\n# fn next(&mut self) -> Option {\n# self.count += 1;\n#\n# if self.count < 6 {\n# Some(self.count)\n# } else {\n# None\n# }\n# }\n# }\n#\n#[test]\nfn calling_next_directly() { let mut counter = Counter::new(); assert_eq!(counter.next(), Some(1)); assert_eq!(counter.next(), Some(2)); assert_eq!(counter.next(), Some(3)); assert_eq!(counter.next(), Some(4)); assert_eq!(counter.next(), Some(5)); assert_eq!(counter.next(), None);\n} リスト13-22: next メソッド実装の機能をテストする このテストは、 counter 変数に新しい Counter インスタンスを生成し、 それからイテレータにほしい動作が実装し終わっていることを実証しながら、 next を繰り返し呼び出しています: 1から5の値を返すことです。 他の Iterator トレイトメソッドを使用する next メソッドを定義して Iterator トレイトを実装したので、今では、標準ライブラリで定義されているように、 どんな Iterator トレイトメソッドのデフォルト実装も使えるようになりました。全て next メソッドの機能を使っているからです。 例えば、何らかの理由で、 Counter インスタンスが生成する値を取り、最初の値を飛ばしてから、 別の Counter インスタンスが生成する値と一組にし、各ペアを掛け算し、3で割り切れる結果だけを残し、 全結果の値を足し合わせたくなったら、リスト13-23のテストに示したように、そうすることができます: ファイル名: src/lib.rs # struct Counter {\n# count: u32,\n# }\n#\n# impl Counter {\n# fn new() -> Counter {\n# Counter { count: 0 }\n# }\n# }\n#\n# impl Iterator for Counter {\n# // このイテレータはu32を生成します\n# // Our iterator will produce u32s\n# type Item = u32;\n#\n# fn next(&mut self) -> Option {\n# // カウントをインクリメントする。故に0から始まる\n# // increment our count. This is why we started at zero.\n# self.count += 1;\n#\n# // カウントが終わったかどうか確認する\n# // check to see if we've finished counting or not.\n# if self.count < 6 {\n# Some(self.count)\n# } else {\n# None\n# }\n# }\n# }\n#\n#[test]\nfn using_other_iterator_trait_methods() { let sum: u32 = Counter::new().zip(Counter::new().skip(1)) .map(|(a, b)| a * b) .filter(|x| x % 3 == 0) .sum(); assert_eq!(18, sum);\n} リスト13-23: Counter イテレータに対していろんな Iterator トレイトのメソッドを使用する zip は4組しか生成しないことに注意してください; 理論的な5番目の組の (5, None) は、 入力イテレータのどちらかが None を返したら、 zip は None を返却するため、決して生成されることはありません。 next メソッドの動作方法を指定し、標準ライブラリが next を呼び出す他のメソッドにデフォルト実装を提供しているので、 これらのメソッド呼び出しは全て可能です。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » Iterator トレイトで独自のイテレータを作成する","id":"ch13-02-iterators.html#iterator-トレイトで独自のイテレータを作成する","title":"Iterator トレイトで独自のイテレータを作成する"},"ch13-02-iterators.html#iterator-トレイトと-next-メソッド":{"body":"全てのイテレータは、標準ライブラリで定義されている Iterator というトレイトを実装しています。 このトレイトの定義は、以下のようになっています: pub trait Iterator { type Item; fn next(&mut self) -> Option; // デフォルト実装のあるメソッドは省略 // methods with default implementations elided\n} この定義は新しい記法を使用していることに注目してください: type Item と Self::Item で、 これらはこのトレイトとの 関連型 (associated type)を定義しています。関連型についての詳細は、第19章で語ります。 とりあえず、知っておく必要があることは、このコードが Iterator トレイトを実装するには、 Item 型も定義する必要があり、 そして、この Item 型が next メソッドの戻り値の型に使われていると述べていることです。換言すれば、 Item 型がイテレータから返ってくる型になるだろうということです。 Iterator トレイトは、一つのメソッドを定義することを実装者に要求することだけします: next メソッドで、 これは1度に Some に包まれたイテレータの1要素を返し、繰り返しが終わったら、 None を返します。 イテレータに対して直接 next メソッドを呼び出すこともできます; リスト13-15は、 ベクタから生成されたイテレータの next を繰り返し呼び出した時にどんな値が返るかを模擬しています。 ファイル名: src/lib.rs #[test]\nfn iterator_demonstration() { let v1 = vec![1, 2, 3]; let mut v1_iter = v1.iter(); assert_eq!(v1_iter.next(), Some(&1)); assert_eq!(v1_iter.next(), Some(&2)); assert_eq!(v1_iter.next(), Some(&3)); assert_eq!(v1_iter.next(), None);\n} リスト13-15: イテレータに対して next メソッドを呼び出す v1_iter を可変にする必要があったことに注目してください: イテレータの next メソッドを呼び出すと、 今シーケンスのどこにいるかを追いかけるためにイテレータが使用している内部の状態が変わります。 つまり、このコードはイテレータを 消費 、または使い込むのです。 next の各呼び出しは、イテレータの要素を一つ、食います。 for ループを使用した時には、 v1_iter を可変にする必要はありませんでした。というのも、ループが v1_iter の所有権を奪い、 陰で可変にしていたからです。 また、 next の呼び出しで得られる値は、ベクタの値への不変な参照であることにも注目してください。 iter メソッドは、不変参照へのイテレータを生成します。 v1 の所有権を奪い、所有された値を返すイテレータを生成したいなら、 iter ではなく into_iter を呼び出すことができます。同様に、可変参照を繰り返したいなら、 iter ではなく iter_mut を呼び出せます。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » Iterator トレイトと next メソッド","id":"ch13-02-iterators.html#iterator-トレイトと-next-メソッド","title":"Iterator トレイトと next メソッド"},"ch13-03-improving-our-io-project.html#aイテレータを使用して-clone-を取り除く":{"body":"リスト12-6において、スライスに添え字アクセスして値をクローンすることで、 Config 構造体に値を所有させながら、 String 値のスライスを取り、 Config 構造体のインスタンスを作るコードを追記しました。リスト13-24では、 リスト12-23のような Config::new の実装を再現しました: ファイル名: src/lib.rs impl Config { pub fn new(args: &[String]) -> Result { if args.len() < 3 { return Err(\"not enough arguments\"); } let query = args[1].clone(); let filename = args[2].clone(); let case_sensitive = env::var(\"CASE_INSENSITIVE\").is_err(); Ok(Config { query, filename, case_sensitive }) }\n} リスト13-24: リスト12-23から Config::new 関数の再現 その際、将来的に除去する予定なので、非効率的な clone 呼び出しを憂慮するなと述べました。 えっと、その時は今です! 引数 args に String 要素のスライスがあるためにここで clone が必要だったのですが、 new 関数は args を所有していません。 Config インスタンスの所有権を返すためには、 Config インスタンスがその値を所有できるように、 Config の query と filename フィールドから値をクローンしなければなりませんでした。 イテレータについての新しい知識があれば、 new 関数をスライスを借用する代わりに、 引数としてイテレータの所有権を奪うように変更することができます。スライスの長さを確認し、 特定の場所に添え字アクセスするコードの代わりにイテレータの機能を使います。これにより、 イテレータは値にアクセスするので、 Config::new 関数がすることが明確化します。 ひとたび、 Config::new がイテレータの所有権を奪い、借用する添え字アクセス処理をやめたら、 clone を呼び出して新しくメモリ確保するのではなく、イテレータからの String 値を Config にムーブできます。 返却されるイテレータを直接使う 入出力プロジェクトの src/main.rs ファイルを開いてください。こんな見た目のはずです: ファイル名: src/main.rs fn main() { let args: Vec = env::args().collect(); let config = Config::new(&args).unwrap_or_else(|err| { eprintln!(\"Problem parsing arguments: {}\", err); process::exit(1); }); // --snip--\n} リスト12-24のような main 関数の冒頭をリスト13-25のコードに変更します。 これは、 Config::new も更新するまでコンパイルできません。 ファイル名: src/main.rs fn main() { let config = Config::new(env::args()).unwrap_or_else(|err| { eprintln!(\"Problem parsing arguments: {}\", err); process::exit(1); }); // --snip--\n} リスト13-25: env::args の戻り値を Config::new に渡す env::args 関数は、イテレータを返します!イテレータの値をベクタに集結させ、それからスライスを Config::new に渡すのではなく、 今では env::args から返ってくるイテレータの所有権を直接 Config::new に渡しています。 次に、 Config::new の定義を更新する必要があります。入出力プロジェクトの src/lib.rs ファイルで、 Config::new のシグニチャをリスト13-26のように変えましょう。関数本体を更新する必要があるので、 それでもコンパイルはできません。 ファイル名: src/lib.rs impl Config { pub fn new(mut args: std::env::Args) -> Result { // --snip-- リスト13-26: Config::new のシグニチャをイテレータを期待するように更新する env::args 関数の標準ライブラリドキュメントは、自身が返すイテレータの型は、 std::env::Args であると表示しています。 Config::new 関数のシグニチャを更新したので、引数 args の型は、 &[String] ではなく、 std::env::Args になりました。 args の所有権を奪い、繰り返しを行うことで args を可変化する予定なので、 args 引数の仕様に mut キーワードを追記でき、可変にします。 添え字の代わりに Iterator トレイトのメソッドを使用する 次に、 Config::new の本体を修正しましょう。標準ライブラリのドキュメントは、 std::env::Args が Iterator トレイトを実装していることにも言及しているので、 それに対して next メソッドを呼び出せることがわかります!リスト13-27は、 リスト12-23のコードを next メソッドを使用するように更新したものです: ファイル名: src/lib.rs # fn main() {}\n# use std::env;\n#\n# struct Config {\n# query: String,\n# filename: String,\n# case_sensitive: bool,\n# }\n#\nimpl Config { pub fn new(mut args: std::env::Args) -> Result { args.next(); let query = match args.next() { Some(arg) => arg, // クエリ文字列を取得しませんでした None => return Err(\"Didn't get a query string\"), }; let filename = match args.next() { Some(arg) => arg, // ファイル名を取得しませんでした None => return Err(\"Didn't get a file name\"), }; let case_sensitive = env::var(\"CASE_INSENSITIVE\").is_err(); Ok(Config { query, filename, case_sensitive }) }\n} リスト13-27: Config::new の本体をイテレータメソッドを使うように変更する env::args の戻り値の1番目の値は、プログラム名であることを思い出してください。それは無視し、 次の値を取得したいので、まず next を呼び出し、戻り値に対して何もしません。2番目に、 next を呼び出して Config の query フィールドに置きたい値を得ます。 next が Some を返したら、 match を使用してその値を抜き出します。 None を返したら、十分な引数が与えられなかったということなので、 Err 値で早期リターンします。 filename 値に対しても同じことをします。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » イテレータを使用して clone を取り除く","id":"ch13-03-improving-our-io-project.html#aイテレータを使用して-clone-を取り除く","title":"イテレータを使用して clone を取り除く"},"ch13-03-improving-our-io-project.html#aイテレータアダプタでコードをより明確にする":{"body":"入出力プロジェクトの search 関数でも、イテレータを活用することができます。その関数はリスト12-19に示していますが、以下のリスト13-28に再掲します。 ファイル名: src/lib.rs pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { let mut results = Vec::new(); for line in contents.lines() { if line.contains(query) { results.push(line); } } results\n} リスト13-28: リスト12-19の search 関数の実装 イテレータアダプタメソッドを使用して、このコードをもっと簡潔に書くことができます。そうすれば、 可変な中間の results ベクタをなくすこともできます。関数型プログラミングスタイルは、可変な状態の量を最小化することを好み、 コードを明瞭化します。可変な状態を除去すると、検索を同時並行に行うという将来的な改善をするのが、 可能になる可能性があります。なぜなら、 results ベクタへの同時アクセスを管理する必要がなくなるからです。 リスト13-29は、この変更を示しています: ファイル名: src/lib.rs pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> { contents.lines() .filter(|line| line.contains(query)) .collect()\n} リスト13-29: search 関数の実装でイテレータアダプタのメソッドを使用する search 関数の目的は、 query を含む contents の行全てを返すことであることを思い出してください。 リスト13-19の filter 例に酷似して、このコードは filter アダプタを使用して line.contains(query) が真を返す行だけを残すことができます。 それから、合致した行を別のベクタに collect で集結させます。ずっと単純です!ご自由に、 同じ変更を行い、 search_case_insensitive 関数でもイテレータメソッドを使うようにしてください。 次の論理的な疑問は、自身のコードでどちらのスタイルを選ぶかと理由です: リスト13-28の元の実装とリスト13-29のイテレータを使用するバージョンです。 多くのRustプログラマは、イテレータスタイルを好みます。とっかかりが少し困難ですが、 いろんなイテレータアダプタとそれがすることの感覚を一度掴めれば、イテレータの方が理解しやすいこともあります。 いろんなループを少しずつもてあそんだり、新しいベクタを構築する代わりに、コードは、ループの高難度の目的に集中できるのです。 これは、ありふれたコードの一部を抽象化するので、イテレータの各要素が通過しなければならないふるい条件など、 このコードに独特の概念を理解しやすくなります。 ですが、本当に2つの実装は等価なのでしょうか?直観的な仮説は、より低レベルのループの方がより高速ということかもしれません。 パフォーマンスに触れましょう。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » イテレータアダプタでコードをより明確にする","id":"ch13-03-improving-our-io-project.html#aイテレータアダプタでコードをより明確にする","title":"イテレータアダプタでコードをより明確にする"},"ch13-03-improving-our-io-project.html#a入出力プロジェクトを改善する":{"body":"このイテレータに関する新しい知識があれば、イテレータを使用してコードのいろんな場所をより明確で簡潔にすることで、 第12章の入出力プロジェクトを改善することができます。イテレータが Config::new 関数と search 関数の実装を改善する方法に目を向けましょう。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » 入出力プロジェクトを改善する","id":"ch13-03-improving-our-io-project.html#a入出力プロジェクトを改善する","title":"入出力プロジェクトを改善する"},"ch13-04-performance.html#aまとめ":{"body":"クロージャとイテレータは、関数型言語の考えに着想を得たRustの機能です。低レベルのパフォーマンスで、 高レベルの考えを明確に表現するというRustの能力に貢献しています。クロージャとイテレータの実装は、 実行時のパフォーマンスが影響されないようなものです。これは、ゼロ代償抽象化を提供するのに努力を惜しまないRustの目標の一部です。 今や入出力プロジェクトの表現力を改善したので、プロジェクトを世界と共有するのに役に立つ cargo の機能にもっと目を向けましょう。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » まとめ","id":"ch13-04-performance.html#aまとめ","title":"まとめ"},"ch13-04-performance.html#aパフォーマンス比較-ループvsイテレータ":{"body":"ループを使うべきかイテレータを使うべきか決定するために、 search 関数のうち、どちらのバージョンが速いか知る必要があります: 明示的な for ループがあるバージョンと、イテレータのバージョンです。 サー・アーサー・コナン・ドイル(Sir Arthur Conan Doyle)の、 シャーロックホームズの冒険 (The Adventures of Sherlock Homes)全体を String に読み込み、 そのコンテンツで the という単語を検索することでベンチマークを行いました。 こちらが、 for を使用した search 関数のバージョンと、イテレータを使用したバージョンに関するベンチマーク結果です。 test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)\ntest bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200) イテレータバージョンの方が些(いささ)か高速ですね!ここでは、ベンチマークのコードは説明しません。 なぜなら、要点は、2つのバージョンが等価であることを証明することではなく、 これら2つの実装がパフォーマンス的にどう比較されるかを大まかに把握することだからです。 より理解しやすいベンチマークには、いろんなサイズの様々なテキストを contents として、異なる単語、異なる長さの単語を query として、 他のあらゆる種類のバリエーションを確認するべきです。重要なのは: イテレータは、 高度な抽象化にも関わらず、低レベルのコードを自身で書いているかのように、ほぼ同じコードにコンパイルされることです。 イテレータは、Rustの ゼロコスト抽象化 の一つであり、これは、抽象化を使うことが追加の実行時オーバーヘッドを生まないことを意味しています。 このことは、C++の元の設計者であり実装者のビャーネ・ストロヴストルップ(Bjarne Stroustrup)が、 ゼロオーバーヘッド を「C++の基礎(2012)」で定義したのと類似しています。 一般的に、C++の実装は、ゼロオーバーヘッド原則を遵守します: 使用しないものには、支払わなくてよい。 さらに: 実際に使っているものに対して、コードをそれ以上うまく渡すことはできない。 別の例として、以下のコードは、オーディオデコーダから取ってきました。デコードアルゴリズムは、 線形予測数学演算を使用して、以前のサンプルの線形関数に基づいて未来の値を予測します。このコードは、 イテレータ連結をしてスコープにある3つの変数に計算を行っています: buffer というデータのスライス、 12の coefficients (係数)の配列、 qlp_shift でデータをシフトする量です。この例の中で変数を宣言しましたが、 値は与えていません; このコードは、文脈の外では大して意味を持ちませんが、 それでもRustが高レベルな考えを低レベルなコードに翻訳する簡潔で現実的な例になっています: let buffer: &mut [i32];\nlet coefficients: [i64; 12];\nlet qlp_shift: i16; for i in 12..buffer.len() { let prediction = coefficients.iter() .zip(&buffer[i - 12..i]) .map(|(&c, &s)| c * s as i64) .sum::() >> qlp_shift; let delta = buffer[i]; buffer[i] = prediction as i32 + delta;\n} prediction の値を算出するために、このコードは、 coefficients の12の値を繰り返し、 zip メソッドを使用して、 係数値を前の buffer の12の値と組にします。それから各組について、その値をかけ合わせ、結果を全て合計し、 合計のビットを qlp_shift ビット分だけ右にシフトさせます。 オーディオデコーダのようなアプリケーションの計算は、しばしばパフォーマンスに最も重きを置きます。 ここでは、イテレータを作成し、2つのアダプタを使用し、それから値を消費しています。 このRustコードは、どんな機械語コードにコンパイルされるのでしょうか?えー、執筆時点では、 手作業で書いたものと同じ機械語にコンパイルされます。 coefficients の値の繰り返しに対応するループは全く存在しません: コンパイラは、12回繰り返しがあることを把握しているので、ループを「展開」します。 ループの展開 は、ループ制御コードのオーバーヘッドを除去し、代わりにループの繰り返しごとに同じコードを生成する最適化です。 係数は全てレジスタに保存されます。つまり、値に非常に高速にアクセスします。実行時に配列の境界チェックをすることもありません。 コンパイラが適用可能なこれらの最適化全てにより、結果のコードは究極的に効率化されます。このことがわかったので、 もうイテレータとクロージャを恐れなしに使用することができますね!それらのおかげでコードは、高レベルだけれども、 そうすることに対して実行時のパフォーマンスを犠牲にしないようになります。","breadcrumbs":"関数型言語の機能: イテレータとクロージャ » パフォーマンス比較: ループVSイテレータ","id":"ch13-04-performance.html#aパフォーマンス比較-ループvsイテレータ","title":"パフォーマンス比較: ループVSイテレータ"},"ch14-00-more-about-cargo.html#cargoとcratesioについてより詳しく":{"body":"今までCargoのビルド、実行、コードのテストを行うという最も基礎的な機能のみを使ってきましたが、 他にもできることはたくさんあります。この章では、そのような他のより高度な機能の一部を議論し、 以下のことをする方法をお見せしましょう: リリースプロファイルでビルドをカスタマイズする crates.io でライブラリを公開する ワークスペースで巨大なプロジェクトを体系化する crates.io からバイナリをインストールする 独自のコマンドを使用してCargoを拡張する また、Cargoはこの章で講義する以上のこともできるので、機能の全解説を見るには、 ドキュメンテーション を参照されたし。","breadcrumbs":"CargoとCrates.ioについてより詳しく","id":"ch14-00-more-about-cargo.html#cargoとcratesioについてより詳しく","title":"CargoとCrates.ioについてより詳しく"},"ch14-01-release-profiles.html#aリリースプロファイルでビルドをカスタマイズする":{"body":"Rustにおいて、 リリースプロファイル とは、プログラマがコードのコンパイルオプションについてより制御可能にしてくれる、 定義済みのカスタマイズ可能なプロファイルです。各プロファイルは、それぞれ独立して設定されます。 Cargoには2つの主なプロファイルが存在します: dev プロファイルは、 cargo build コマンドを実行したときに使用され、 release プロファイルは、 cargo build --release コマンドを実行したときに使用されます。 dev プロファイルは、開発中に役に立つデフォルト設定がなされており、 release プロファイルは、 リリース用の設定がなされています。 これらのプロファイル名は、ビルドの出力で馴染みのある可能性があります: $ cargo build Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs\n$ cargo build --release Finished release [optimized] target(s) in 0.0 secs このビルド出力で表示されている dev と release は、コンパイラが異なるプロファイルを使用していることを示しています。 プロジェクトの Cargo.toml ファイルに [profile.*] セクションが存在しない際に適用される各プロファイル用のデフォルト設定が、 Cargoには存在します。カスタマイズしたいプロファイル用の [profile.*] セクションを追加することで、 デフォルト設定の一部を上書きすることができます。例えば、こちらが dev と release プロファイルの opt-level 設定のデフォルト値です: ファイル名: Cargo.toml [profile.dev]\nopt-level = 0 [profile.release]\nopt-level = 3 opt-level 設定は、0から3の範囲でコンパイラがコードに適用する最適化の度合いを制御します。 最適化を多くかけると、コンパイル時間が延びるので、開発中に頻繁にコードをコンパイルするのなら、 たとえ出力結果のコードの動作速度が遅くなっても早くコンパイルが済んでほしいですよね。 これが、 dev の opt-level のデフォルト設定が 0 になっている唯一の理由です。 コードのリリース準備ができたら、より長い時間をコンパイルにかけるのが最善の策です。 リリースモードでコンパイルするのはたった1回ですが、コンパイル結果のプログラムは何度も実行するので、 リリースモードでは、長いコンパイル時間と引き換えに、生成したコードが速く動作します。 そのため、 release の opt-level のデフォルト設定が 3 になっているのです。 デフォルト設定に対して Cargo.toml で異なる値を追加すれば、上書きすることができます。 例として、開発用プロファイルで最適化レベル1を使用したければ、以下の2行をプロジェクトの Cargo.toml ファイルに追加できます: ファイル名: Cargo.toml [profile.dev]\nopt-level = 1 このコードは、デフォルト設定の 0 を上書きします。こうすると、 cargo build を実行したときに、 dev プロファイル用のデフォルト設定に加えて、Cargoは opt-level の変更を適用します。 opt-level を 1 に設定したので、Cargoはデフォルトよりは最適化を行いますが、リリースビルドほどではありません。 設定の選択肢と各プロファイルのデフォルト設定の一覧は、 Cargoのドキュメンテーション を参照されたし。","breadcrumbs":"CargoとCrates.ioについてより詳しく » リリースプロファイルでビルドをカスタマイズする","id":"ch14-01-release-profiles.html#aリリースプロファイルでビルドをカスタマイズする","title":"リリースプロファイルでビルドをカスタマイズする"},"ch14-02-publishing-to-crates-io.html#a役に立つドキュメンテーションコメントを行う":{"body":"パッケージを正確にドキュメントすることで、他のユーザがパッケージを使用する方法や、いつ使用すべきかを理解する手助けをすることになるので、 ドキュメンテーションを書くことに時間を費やす価値があります。第3章で、2連スラッシュ、 // でRustのコードにコメントをつける方法を議論しました。 Rustには、ドキュメンテーション用のコメントも用意されていて、便利なことに ドキュメンテーションコメント として知られ、 HTMLドキュメントを生成します。クレートの 実装 法とは対照的にクレートの 使用 法を知ることに興味のあるプログラマ向けの、 公開API用のドキュメンテーションコメントの中身をこのHTMLは表示します。 ドキュメンテーションコメントは、2つではなく、3連スラッシュ、 /// を使用し、テキストを整形するMarkdown記法もサポートしています。 ドキュメント対象の要素の直前にドキュメンテーションコメントを配置してください。 リスト14-1は、 my_crate という名のクレートの add_one 関数用のドキュメンテーションコメントを示しています: ファイル名: src/lib.rs /// Adds one to the number given.\n/// 与えられた数値に1を足す。\n///\n/// # Examples\n///\n/// ```\n/// let five = 5;\n///\n/// assert_eq!(6, my_crate::add_one(5));\n/// ```\npub fn add_one(x: i32) -> i32 { x + 1\n} リスト14-1: 関数のドキュメンテーションコメント ここで、 add_one 関数がすることの説明を与え、 Examples というタイトルでセクションを開始し、 add_one 関数の使用法を模擬するコードを提供しています。このドキュメンテーションコメントから cargo doc を実行することで、 HTMLドキュメントを生成することができます。このコマンドはコンパイラとともに配布されている rustdoc ツールを実行し、 生成されたHTMLドキュメントを target/doc ディレクトリに配置します。 利便性のために、 cargo doc --open を走らせれば、現在のクレートのドキュメント用のHTML(と、 自分のクレートが依存している全てのドキュメント)を構築し、その結果をWebブラウザで開きます。 add_one 関数まで下り、図14-1に示したように、ドキュメンテーションコメントのテキストがどう描画されるかを確認しましょう: 図14-1: add_one 関数のHTMLドキュメント よく使われるセクション # Examples マークダウンのタイトルをリスト14-1で使用し、「例」というタイトルのセクションをHTMLに生成しました。 こちらがこれ以外にドキュメントでよくクレート筆者が使用するセクションです: Panics : ドキュメント対象の関数が panic! する可能性のある筋書きです。プログラムをパニックさせたくない関数の使用者は、 これらの状況で関数が呼ばれないことを確かめる必要があります。 Errors : 関数が Result を返すなら、起きうるエラーの種類とどんな条件がそれらのエラーを引き起こす可能性があるのか解説すると、 呼び出し側の役に立つので、エラーの種類によって処理するコードを変えて書くことができます。 Safety : 関数が呼び出すのに unsafe (unsafeについては第19章で議論します)なら、 関数がunsafeな理由を説明し、関数が呼び出し元に保持していると期待する不変条件を講義するセクションがあるべきです。 多くのドキュメンテーションコメントでは、これら全てのセクションが必要になることはありませんが、 これは自分のコードを呼び出している人が知りたいと思うコードの方向性を思い出させてくれるいいチェックリストになります。 テストとしてのドキュメンテーションコメント ドキュメンテーションコメントに例のコードブロックを追加すると、ライブラリの使用方法のデモに役立ち、 おまけもついてきます: cargo test を走らせると、ドキュメントのコード例をテストとして実行するのです! 例付きのドキュメントに上回るものはありません。しかし、ドキュメントが書かれてからコードが変更されたがために、 動かない例がついているよりも悪いものもありません。リスト14-1から add_one 関数のドキュメンテーションとともに、 cargo test を走らせたら、テスト結果に以下のような区域が見られます: Doc-tests my_crate running 1 test\ntest src/lib.rs - add_one (line 5) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out さて、例の assert_eq! がパニックするように、関数か例を変更し、再度 cargo test を実行したら、 docテストが、例とコードがお互いに同期されていないことを捕捉するところを目撃するでしょう! 含まれている要素にコメントする docコメントの別スタイル、 //! は、コメントに続く要素にドキュメンテーションを付け加えるのではなく、 コメントを含む要素にドキュメンテーションを付け加えます。典型的には、クレートのルートファイル(慣例的には、 src/lib.rs )内部や、 モジュールの内部で使用して、クレートやモジュール全体にドキュメントをつけます。 例えば、 add_one 関数を含む my_crate クレートの目的を解説するドキュメンテーションを追加したいのなら、 //! で始まるドキュメンテーションコメントを src/lib.rs ファイルの先頭につけることができます。 リスト14-2に示したようにですね: ファイル名: src/lib.rs //! # My Crate\n//!\n//! `my_crate` is a collection of utilities to make performing certain\n//! calculations more convenient. //! #自分のクレート\n//!\n//! `my_crate`は、ユーティリティの集まりであり、特定の計算をより便利に行うことができます。 /// Adds one to the number given.\n// --snip-- リスト14-2: 全体として my_crate クレートにドキュメントをつける //! で始まる最後の行のあとにコードがないことに注目してください。 /// ではなく、 //! でコメントを開始しているので、 このコメントに続く要素ではなく、このコメントを含む要素にドキュメントをつけているわけです。 今回の場合、このコメントを含む要素は src/lib.rs ファイルであり、クレートのルートです。 これらのコメントは、クレート全体を解説しています。 cargo doc --open を実行すると、これらのコメントは、 my_crate のドキュメントの最初のページ、 クレートの公開要素のリストの上部に表示されます。図14-2のようにですね: 図14-2: クレート全体を解説するコメントを含む my_crate の描画されたドキュメンテーション 要素内のドキュメンテーションコメントは、特にクレートやモジュールを解説するのに有用です。 コンテナの全体の目的を説明し、クレートの使用者がクレートの体系を理解する手助けをするのに使用してください。","breadcrumbs":"CargoとCrates.ioについてより詳しく » 役に立つドキュメンテーションコメントを行う","id":"ch14-02-publishing-to-crates-io.html#a役に立つドキュメンテーションコメントを行う","title":"役に立つドキュメンテーションコメントを行う"},"ch14-02-publishing-to-crates-io.html#a新しいクレートにメタデータを追加する":{"body":"アカウントはできたので、公開したいクレートがあるとしましょう。公開前に、 Cargo.toml ファイルの [package] セクションに追加することでクレートにメタデータを追加する必要があるでしょう。 クレートには、独自の名前が必要でしょう。クレートをローカルで作成している間、 クレートの名前はなんでもいい状態でした。ところが、 crates.io のクレート名は、 最初に来たもの勝ちの精神で付与されていますので、一旦クレート名が取られてしまったら、 その名前のクレートを他の人が公開することは絶対できません。もう使われているか、 サイトで使いたい名前を検索してください。まだなら、 Cargo.toml ファイルの [package] 以下の名前を編集して、 名前を公開用に使ってください。以下のように: ファイル名: Cargo.toml [package]\nname = \"guessing_game\" たとえ、独自の名前を選択していたとしても、この時点で cargo publish を実行すると、警告とエラーが出ます: $ cargo publish Updating registry `https://github.com/rust-lang/crates.io-index`\nwarning: manifest has no description, license, license-file, documentation,\nhomepage or repository.\n(警告: マニフェストに説明、ライセンス、ライセンスファイル、ドキュメンテーション、ホームページ、\nリポジトリがありません)\n--snip--\nerror: api errors: missing or empty metadata fields: description, license.\n(エラー: APIエラー: 存在しないメタデータフィールド: description, license) 原因は、大事な情報を一部入れていないからです: 説明とライセンスは、 他の人があなたのクレートは何をし、どんな条件の元で使っていいのかを知るために必要なのです。 このエラーを解消するには、 Cargo.toml ファイルにこの情報を入れ込む必要があります。 1文か2文程度の説明をつけてください。これは、検索結果に表示されますからね。 license フィールドには、 ライセンス識別子 を与える必要があります。 Linux団体のSoftware Package Data Exchange(SPDX) に、この値に使用できる識別子が列挙されています。 例えば、自分のクレートをMITライセンスでライセンスするためには、 MIT 識別子を追加してください: ファイル名: Cargo.toml [package]\nname = \"guessing_game\"\nlicense = \"MIT\" SPDXに出現しないライセンスを使用したい場合、そのライセンスをファイルに配置し、 プロジェクトにそのファイルを含め、それから license キーを使う代わりに、 そのファイルの名前を指定するのに license-file を使う必要があります。 どのライセンスが自分のプロジェクトに相(ふ)応(さわ)しいかというガイドは、 この本の範疇を超えています。Rustコミュニティの多くの人間は、 MIT OR Apache-2.0 のデュアルライセンスを使用することで、 Rust自体と同じようにプロジェクトをライセンスします。この実践は、 OR で区切られる複数のライセンス識別子を指定して、 プロジェクトに複数のライセンスを持たせることもできることを模擬しています。 独自の名前、バージョン、クレート作成時に cargo new が追加した筆者の詳細、説明、ライセンスが追加され、 公開準備のできたプロジェクト用の Cargo.toml ファイルは以下のような見た目になっていることでしょう: ファイル名: Cargo.toml [package]\nname = \"guessing_game\"\nversion = \"0.1.0\"\nauthors = [\"Your Name \"]\ndescription = \"A fun game where you guess what number the computer has chosen.\" (コンピュータが選択した数字を言い当てる面白いゲーム)\nlicense = \"MIT OR Apache-2.0\" [dependencies] Cargoのドキュメンテーション には、 指定して他人が発見し、より容易くクレートを使用できることを保証する他のメタデータが解説されています。","breadcrumbs":"CargoとCrates.ioについてより詳しく » 新しいクレートにメタデータを追加する","id":"ch14-02-publishing-to-crates-io.html#a新しいクレートにメタデータを追加する","title":"新しいクレートにメタデータを追加する"},"ch14-02-publishing-to-crates-io.html#a既存のクレートの新バージョンを公開する":{"body":"クレートに変更を行い、新バージョンをリリースする準備ができたら、 Cargo.toml ファイルに指定された version の値を変更し、再公開します。 セマンティックバージョンルール を使用して加えた変更の種類に基づいて次の適切なバージョン番号を決定してください。 そして、 cargo publish を実行し、新バージョンをアップロードします。","breadcrumbs":"CargoとCrates.ioについてより詳しく » 既存のクレートの新バージョンを公開する","id":"ch14-02-publishing-to-crates-io.html#a既存のクレートの新バージョンを公開する","title":"既存のクレートの新バージョンを公開する"},"ch14-02-publishing-to-crates-io.html#cargo-yank-でcratesioからバージョンを削除する":{"body":"以前のバージョンのクレートを削除することはできないものの、将来のプロジェクトがこれに新たに依存することを防ぐことはできます。 これは、なんらかの理由により、クレートバージョンが壊れている場合に有用です。そのような場面において、 Cargoはクレートバージョンの 取り下げ(yank) をサポートしています。 バージョンを取り下げると、既存のプロジェクトは、引き続きダウンロードしたりそのバージョンに依存したりしつづけられますが、 新規プロジェクトが新しくそのバージョンに依存しだすことは防止されます。つまるところ、取り下げは、 すでに Cargo.lock が存在するプロジェクトは壊さないが、将来的に生成された Cargo.lock ファイルは 取り下げられたバージョンを使わない、ということを意味します。 あるバージョンのクレートを取り下げるには、 cargo yank を実行し、取り下げたいバージョンを指定します: $ cargo yank --vers 1.0.1 --undo をコマンドに付与することで、取り下げを取り消し、再度あるバージョンにプロジェクトを依存させ始めることもできます: $ cargo yank --vers 1.0.1 --undo 取り下げは、コードの削除は一切し ません 。例として、取り下げ機能は、誤ってアップロードされた秘密鍵を削除するためのものではありません。 もしそうなってしまったら、即座に秘密鍵をリセットしなければなりません。","breadcrumbs":"CargoとCrates.ioについてより詳しく » cargo yank でCrates.ioからバージョンを削除する","id":"ch14-02-publishing-to-crates-io.html#cargo-yank-でcratesioからバージョンを削除する","title":"cargo yank でCrates.ioからバージョンを削除する"},"ch14-02-publishing-to-crates-io.html#cratesioにクレートを公開する":{"body":"プロジェクトの依存として crates.io のパッケージを使用しましたが、 自分のパッケージを公開することで他の人とコードを共有することもできます。 crates.io のクレート登録所は、自分のパッケージのソースコードを配布するので、 主にオープンソースのコードをホストします。 RustとCargoは、公開したパッケージを人が使用し、そもそも見つけやすくしてくれる機能を有しています。 これらの機能の一部を次に語り、そして、パッケージの公開方法を説明します。","breadcrumbs":"CargoとCrates.ioについてより詳しく » Crates.ioにクレートを公開する","id":"ch14-02-publishing-to-crates-io.html#cratesioにクレートを公開する","title":"Crates.ioにクレートを公開する"},"ch14-02-publishing-to-crates-io.html#cratesioに公開する":{"body":"アカウントを作成し、APIトークンを保存し、クレートの名前を決め、必要なメタデータを指定したので、 公開する準備が整いました!クレートを公開すると、特定のバージョンが、 crates.io に他の人が使用できるようにアップロードされます。 公開は 永久 なので、クレートの公開時には気をつけてください。バージョンは絶対に上書きできず、 コードも削除できません。 crates.io の一つの主な目標が、 crates.io のクレートに依存している全てのプロジェクトのビルドが、 動き続けるようにコードの永久アーカイブとして機能することなのです。バージョン削除を可能にしてしまうと、 その目標を達成するのが不可能になってしまいます。ですが、公開できるクレートバージョンの数に制限はありません。 再度 cargo publish コマンドを実行してください。今度は成功するはずです: $ cargo publish Updating registry `https://github.com/rust-lang/crates.io-index`\nPackaging guessing_game v0.1.0 (file:///projects/guessing_game)\nVerifying guessing_game v0.1.0 (file:///projects/guessing_game)\nCompiling guessing_game v0.1.0\n(file:///projects/guessing_game/target/package/guessing_game-0.1.0) Finished dev [unoptimized + debuginfo] target(s) in 0.19 secs\nUploading guessing_game v0.1.0 (file:///projects/guessing_game) おめでとうございます!Rustコミュニティとコードを共有し、誰でもあなたのクレートを依存として簡単に追加できます。","breadcrumbs":"CargoとCrates.ioについてより詳しく » Crates.ioに公開する","id":"ch14-02-publishing-to-crates-io.html#cratesioに公開する","title":"Crates.ioに公開する"},"ch14-02-publishing-to-crates-io.html#cratesioのアカウントをセットアップする":{"body":"クレートを公開する前に、 crates.io のアカウントを作成し、 APIトークンを取得する必要があります。そうするには、 crates.io のホームページを訪れ、 Githubアカウントでログインしてください。(現状は、Githubアカウントがなければなりませんが、 いずれは他の方法でもアカウントを作成できるようになる可能性があります。)ログインしたら、 https://crates.io/me/ で自分のアカウントの設定に行き、 APIキーを取り扱ってください。そして、 cargo login コマンドをAPIキーとともに実行してください。 以下のようにですね: $ cargo login abcdefghijklmnopqrstuvwxyz012345 このコマンドは、CargoにAPIトークンを知らせ、 ~/.cargo/credentials にローカルに保存します。 このトークンは、 秘密 です: 他人とは共有しないでください。なんらかの理由で他人と実際に共有してしまったら、 古いものを破棄して crates.io で新しいトークンを生成するべきです。","breadcrumbs":"CargoとCrates.ioについてより詳しく » Crates.ioのアカウントをセットアップする","id":"ch14-02-publishing-to-crates-io.html#cratesioのアカウントをセットアップする","title":"Crates.ioのアカウントをセットアップする"},"ch14-02-publishing-to-crates-io.html#pub-use-で便利な公開apiをエクスポートする":{"body":"第7章において、 mod キーワードを使用してモジュールにコードを体系化する方法、 pub キーワードで要素を公開にする方法、 use キーワードで要素をスコープに導入する方法について講義しました。しかしながら、クレートの開発中に、 自分にとって意味のある構造は、ユーザにはあまり便利ではない可能性があります。複数階層を含む階層で、 自分の構造体を体系化したくなるかもしれませんが、それから階層の深いところで定義した型を使用したい人は、 型が存在することを見つけ出すのに困難を伴う可能性もあります。また、そのような人は、 use my_crate::UsefulType の代わりに use my_crate::some_module::another_module::UsefulType; と入力するのを煩わしく感じる可能性もあります。 自分の公開APIの構造は、クレートを公開する際に考慮すべき点です。自分のクレートを使用したい人は、 自分よりもその構造に馴染みがないですし、クレートのモジュール階層が大きければ、使用したい部分を見つけるのが困難になる可能性があります。 嬉しいお知らせは、構造が他人が他のライブラリから使用するのに便利では ない 場合、内部的な体系を再構築する必要はないということです: 代わりに、要素を再エクスポートし、 pub use で自分の非公開構造とは異なる公開構造にできます。 再エクスポートは、ある場所の公開要素を一つ取り、別の場所で定義されているかのように別の場所で公開します。 例えば、芸術的な概念をモデル化するために art という名のライブラリを作ったとしましょう。 このライブラリ内には、2つのモジュールがあります: PrimaryColor と SecondaryColor という名前の2つのenumを含む、 kinds モジュールと mix という関数を含む utils モジュールです。リスト14-3のようにですね: ファイル名: src/lib.rs //! # Art\n//!\n//! A library for modeling artistic concepts.\n//! #芸術\n//!\n//! 芸術的な概念をモデル化するライブラリ。 pub mod kinds { /// The primary colors according to the RYB color model. /// RYBカラーモデルによる主色 pub enum PrimaryColor { Red, Yellow, Blue, } /// The secondary colors according to the RYB color model. /// RYBカラーモデルによる副色 pub enum SecondaryColor { Orange, Green, Purple, }\n} pub mod utils { use kinds::*; /// Combines two primary colors in equal amounts to create /// a secondary color. ///2つの主色を同じ割合で混合し、副色にする pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor { // --snip-- }\n} リスト14-3: kinds と utils モジュールに体系化される要素を含む art ライブラリ 図14-3は、 cargo doc により生成されるこのクレートのドキュメンテーションの最初のページがどんな見た目になるか示しています: 図14-3: kinds と utils モジュールを列挙する art のドキュメンテーションのトップページ PrimaryColor 型も SecondaryColor 型も、 mix 関数もトップページには列挙されていないことに注意してください。 kinds と utils をクリックしなければ、参照することができません。 このライブラリに依存する別のクレートは、現在定義されているモジュール構造を指定して、 art の要素をインポートする use 文が必要になるでしょう。リスト14-4は、 art クレートから PrimaryColor と mix 要素を使用するクレートの例を示しています: ファイル名: src/main.rs extern crate art; use art::kinds::PrimaryColor;\nuse art::utils::mix; fn main() { let red = PrimaryColor::Red; let yellow = PrimaryColor::Yellow; mix(red, yellow);\n} リスト14-4: 内部構造がエクスポートされて art クレートの要素を使用するクレート リスト14-4は art クレートを使用していますが、このコードの筆者は、 PrimaryColor が kinds モジュールにあり、 mix が utils モジュールにあることを理解しなければなりませんでした。 art クレートのモジュール構造は、 art クレートの使用者よりも、 art クレートに取り組む開発者などに関係が深いです。 クレートの一部を kinds モジュールと utils モジュールに体系化する内部構造は、 art クレートの使用方法を理解しようとする人には、 何も役に立つ情報を含んでいません。代わりに、開発者がどこを見るべきか計算する必要があるので、 art クレートのモジュール構造は混乱を招き、また、開発者はモジュール名を use 文で指定しなければならないので、 この構造は不便です。 公開APIから内部体系を除去するために、リスト14-3の art クレートコードを変更し、 pub use 文を追加して、 最上位で要素を再エクスポートすることができます。リスト14-5みたいにですね: ファイル名: src/lib.rs //! # Art\n//!\n//! A library for modeling artistic concepts. pub use kinds::PrimaryColor;\npub use kinds::SecondaryColor;\npub use utils::mix; pub mod kinds { // --snip--\n} pub mod utils { // --snip--\n} リスト14-5: pub use 文を追加して要素を再エクスポートする このクレートに対して cargo doc が生成するAPIドキュメンテーションは、これで図14-4のようにトップページに再エクスポートを列挙しリンクするので、 PrimaryColor 型と SecondaryColor 型と mix 関数を見つけやすくします。 図14-4: 再エクスポートを列挙する art のドキュメンテーションのトップページ art クレートのユーザは、それでも、リスト14-4にデモされているように、リスト14-3の内部構造を見て使用することもできますし、 リスト14-5のより便利な構造を使用することもできます。リスト14-6に示したようにですね: ファイル名: src/main.rs extern crate art; use art::PrimaryColor;\nuse art::mix; fn main() { // --snip--\n} リスト14-6: art クレートの再エクスポートされた要素を使用するプログラム ネストされたモジュールがたくさんあるような場合、最上位階層で pub use により型を再エクスポートすることは、 クレートの使用者の経験に大きな違いを生みます。 役に立つAPI構造を作ることは、科学というよりも芸術の領域であり、ユーザにとって何が最善のAPIなのか、 探究するために繰り返してみることができます。 pub use は、内部的なクレート構造に柔軟性をもたらし、 その内部構造をユーザに提示する構造から切り離してくれます。インストールしてある他のクレートを見て、 内部構造が公開APIと異なっているか確認してみてください。","breadcrumbs":"CargoとCrates.ioについてより詳しく » pub use で便利な公開APIをエクスポートする","id":"ch14-02-publishing-to-crates-io.html#pub-use-で便利な公開apiをエクスポートする","title":"pub use で便利な公開APIをエクスポートする"},"ch14-03-cargo-workspaces.html#aワークスペースを生成する":{"body":"ワークスペース は、同じ Cargo.lock と出力ディレクトリを共有する一連のパッケージです。 ワークスペースを使用したプロジェクトを作成し、ワークスペースの構造に集中できるよう、瑣末なコードを使用しましょう。 ワークスペースを構築する方法は複数ありますが、一般的な方法を提示しましょう。バイナリ1つとライブラリ2つを含むワークスペースを作ります。 バイナリは、主要な機能を提供しますが、2つのライブラリに依存しています。 一方のライブラリは、 add_one 関数を提供し、2番目のライブラリは、 add_two 関数を提供します。 これら3つのクレートが同じワークスペースの一部になります。ワークスペース用の新しいディレクトリを作ることから始めましょう: $ mkdir add\n$ cd add 次に add ディレクトリにワークスペース全体を設定する Cargo.toml ファイルを作成します。 このファイルには、他の Cargo.toml ファイルで見かけるような [package] セクションやメタデータはありません。 代わりにバイナリクレートへのパスを指定することでワークスペースにメンバを追加させてくれる [workspace] セクションから開始します; 今回の場合、そのパスは adder です: ファイル名: Cargo.toml [workspace] members = [ \"adder\",\n] 次に、 add ディレクトリ内で cargo new を実行することで adder バイナリクレートを作成しましょう: $ cargo new --bin adder Created binary (application) `adder` project この時点で、 cargo build を走らせるとワークスペースを構築できます。 add ディレクトリに存在するファイルは、 以下のようになるはずです: ├── Cargo.lock\n├── Cargo.toml\n├── adder\n│ ├── Cargo.toml\n│ └── src\n│ └── main.rs\n└── target ワークスペースには、コンパイルした生成物を置けるように最上位に target のディレクトリがあります; adder クレートには target ディレクトリはありません。 adder ディレクトリ内部から cargo build を走らせることになっていたとしても、コンパイルされる生成物は、 add/adder/target ではなく、 add/target に落ち着くでしょう。ワークスペースのクレートは、 お互いに依存しあうことを意味するので、Cargoはワークスペースの target ディレクトリをこのように構成します。 各クレートが target ディレクトリを持っていたら、各クレートがワークスペースの他のクレートを再コンパイルし、 target ディレクトリに生成物がある状態にしなければならないでしょう。一つの target ディレクトリを共有することで、 クレートは不必要な再ビルドを回避できるのです。","breadcrumbs":"CargoとCrates.ioについてより詳しく » ワークスペースを生成する","id":"ch14-03-cargo-workspaces.html#aワークスペースを生成する","title":"ワークスペースを生成する"},"ch14-03-cargo-workspaces.html#aワークスペース内に2番目のクレートを作成する":{"body":"次に、ワークスペースに別のメンバクレートを作成し、 add-one と呼びましょう。 最上位の Cargo.toml を変更して members リストで add-one パスを指定するようにしてください: ファイル名: Cargo.toml [workspace] members = [ \"adder\", \"add-one\",\n] それから、 add-one という名前のライブラリクレートを生成してください: $ cargo new add-one --lib Created library `add-one` project これで add ディレクトリには、以下のディレクトリやファイルが存在するはずです: ├── Cargo.lock\n├── Cargo.toml\n├── add-one\n│ ├── Cargo.toml\n│ └── src\n│ └── lib.rs\n├── adder\n│ ├── Cargo.toml\n│ └── src\n│ └── main.rs\n└── target add-one/src/lib.rs ファイルに add_one 関数を追加しましょう: ファイル名: add-one/src/lib.rs pub fn add_one(x: i32) -> i32 { x + 1\n} ワークスペースにライブラリクレートが存在するようになったので、バイナリクレート adder をライブラリクレートの add-one に依存させられます。 まず、 add-one へのパス依存を adder/Cargo.toml に追加する必要があります: ファイル名: adder/Cargo.toml [dependencies] add-one = { path = \"../add-one\" } Cargoはワークスペースのクレートが、お互いに依存しているとは想定していないので、 クレート間の依存関係について明示する必要があります。 次に、 adder クレートの add-one クレートから add_one 関数を使用しましょう。 adder/src/main.rs ファイルを開き、 冒頭に extern crate 行を追加して新しい add-one ライブラリクレートをスコープに導入してください。 それから main 関数を変更し、 add_one 関数を呼び出します。リスト14-7のようにですね: ファイル名: adder/src/main.rs extern crate add_one; fn main() { let num = 10; // こんにちは世界!{}+1は{}! println!(\"Hello, world! {} plus one is {}!\", num, add_one::add_one(num));\n} リスト14-7: adder クレートから add-one ライブラリクレートを使用する 最上位の add ディレクトリで cargo build を実行することでワークスペースをビルドしましょう! $ cargo build Compiling add-one v0.1.0 (file:///projects/add/add-one) Compiling adder v0.1.0 (file:///projects/add/adder) Finished dev [unoptimized + debuginfo] target(s) in 0.68 secs add ディレクトリからバイナリクレートを実行するには、 -p 引数とパッケージ名を cargo run と共に使用して、 使用したいワークスペースのパッケージを指定する必要があります: $ cargo run -p adder Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running `target/debug/adder`\nHello, world! 10 plus one is 11! これにより、 adder/src/main.rs のコードが実行され、これは add_one クレートに依存しています。 ワークスペースの外部クレートに依存する ワークスペースには、各クレートのディレクトリそれぞれに Cargo.lock が存在するのではなく、 ワークスペースの最上位階層にただ一つの Cargo.lock が存在するだけのことに注目してください。 これにより、全クレートが全依存の同じバージョンを使用していることが確認されます。 rand クレートを adder/Cargo.toml と add-one/Cargo.toml ファイルに追加すると、 Cargoは両者をあるバージョンの rand に解決し、それを一つの Cargo.lock に記録します。 ワークスペースの全クレートに同じ依存を使用させるということは、 ワークスペースのクレートが相互に互換性を常に維持するということになります。 add-one/Cargo.toml ファイルの [dependencies] セクションに rand クレートを追加して、 add-one クレートで rand クレートを使用できます: ファイル名: add-one/Cargo.toml [dependencies] rand = \"0.3.14\" これで、 add-one/src/lib.rs ファイルに extern crate rand; を追加でき、 add ディレクトリで cargo build を実行することでワークスペース全体をビルドすると、 rand クレートを持ってきてコンパイルするでしょう: $ cargo build Updating registry `https://github.com/rust-lang/crates.io-index` Downloading rand v0.3.14 --snip-- Compiling rand v0.3.14 Compiling add-one v0.1.0 (file:///projects/add/add-one) Compiling adder v0.1.0 (file:///projects/add/adder) Finished dev [unoptimized + debuginfo] target(s) in 10.18 secs さて、最上位の Cargo.lock は、 rand に対する add-one の依存の情報を含むようになりました。 ですが、 rand はワークスペースのどこかで使用されているにも関わらず、それぞれの Cargo.toml ファイルにも、 rand を追加しない限り、ワークスペースの他のクレートでそれを使用することはできません。 例えば、 adder クレートの adder/src/main.rs ファイルに extern crate rand; を追加すると、 エラーが出ます: $ cargo build Compiling adder v0.1.0 (file:///projects/add/adder)\nerror: use of unstable library feature 'rand': use `rand` from crates.io (see\nissue #27703)\n(エラー: 不安定なライブラリの機能'rand'を使用しています: crates.ioの`rand`を使用してください) --> adder/src/main.rs:1:1 |\n1 | extern crate rand; これを修正するには、 adder クレートの Cargo.toml ファイルを編集し、同様にそのクレートが rand に依存していることを示してください。 adder クレートをビルドすると、 rand を Cargo.lock の adder の依存一覧に追加しますが、 rand のファイルが追加でダウンロードされることはありません。Cargoが、ワークスペースの rand を使用するどのクレートも、 同じバージョンを使っていることを確かめてくれるのです。ワークスペース全体で rand の同じバージョンを使用することにより、 複数のコピーが存在しないのでスペースを節約し、ワークスペースのクレートが相互に互換性を維持することを確かめます。 ワークスペースにテストを追加する さらなる改善として、 add_one クレート内に add_one::add_one 関数のテストを追加しましょう: ファイル名: add-one/src/lib.rs pub fn add_one(x: i32) -> i32 { x + 1\n} #[cfg(test)]\nmod tests { use super::*; #[test] fn it_works() { assert_eq!(3, add_one(2)); }\n} では、最上位の add ディレクトリで cargo test を実行してください: $ cargo test Compiling add-one v0.1.0 (file:///projects/add/add-one) Compiling adder v0.1.0 (file:///projects/add/adder) Finished dev [unoptimized + debuginfo] target(s) in 0.27 secs Running target/debug/deps/add_one-f0253159197f7841 running 1 test\ntest tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Running target/debug/deps/adder-f88af9d2cc175a5e running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Doc-tests add-one running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out 出力の最初の区域が、 add-one クレートの it_works テストが通ったことを示しています。 次の区域には、 adder クレートにはテストが見つからなかったことが示され、 さらに最後の区域には、 add-one クレートにドキュメンテーションテストは見つからなかったと表示されています。 このような構造をしたワークスペースで cargo test を走らせると、ワークスペースの全クレートのテストを実行します。 -p フラグを使用し、テストしたいクレートの名前を指定することで最上位ディレクトリから、 ワークスペースのある特定のクレート用のテストを実行することもできます: $ cargo test -p add-one Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs Running target/debug/deps/add_one-b3235fea9a156f74 running 1 test\ntest tests::it_works ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out Doc-tests add-one running 0 tests test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out この出力は、 cargo test が add-one クレートのテストのみを実行し、 adder クレートのテストは実行しなかったことを示しています。 ワークスペースのクレートを https://crates.io/ に公開したら、ワークスペースのクレートは個別に公開される必要があります。 cargo publish コマンドには --all フラグや -p フラグはないので、各クレートのディレクトリに移動して、 ワークスペースの各クレートを cargo publish して、公開しなければなりません。 鍛錬を積むために、 add-one クレートと同様の方法でワークスペースに add-two クレートを追加してください! プロジェクトが肥大化してきたら、ワークスペースの使用を考えてみてください: 大きな一つのコードの塊よりも、 微細で個別のコンポーネントの方が理解しやすいです。またワークスペースにクレートを保持することは、 同時に変更されることが多いのなら、協調しやすくなることにも繋がります。","breadcrumbs":"CargoとCrates.ioについてより詳しく » ワークスペース内に2番目のクレートを作成する","id":"ch14-03-cargo-workspaces.html#aワークスペース内に2番目のクレートを作成する","title":"ワークスペース内に2番目のクレートを作成する"},"ch14-03-cargo-workspaces.html#cargoのワークスペース":{"body":"第12章で、バイナリクレートとライブラリクレートを含むパッケージを構築しました。プロジェクトの開発が進むにつれて、 ライブラリクレートの肥大化が続き、その上で複数のライブラリクレートにパッケージを分割したくなることでしょう。 この場面において、Cargoは ワークスペース という協調して開発された関連のある複数のパッケージを管理するのに役立つ機能を提供しています。","breadcrumbs":"CargoとCrates.ioについてより詳しく » Cargoのワークスペース","id":"ch14-03-cargo-workspaces.html#cargoのワークスペース","title":"Cargoのワークスペース"},"ch14-04-installing-binaries.html#cargo-install-でcratesioからバイナリをインストールする":{"body":"cargo install コマンドにより、バイナリクレートをローカルにインストールし、使用することができます。 これは、システムパッケージを置き換えることを意図したものではありません。即(すなわ)ち、 Rustの開発者が、他人が crates.io に共有したツールをインストールするのに便利な方法を意味するのです。 バイナリターゲットを持つパッケージのみインストールできることに注意してください。バイナリターゲットとは、 クレートが src/main.rs ファイルやバイナリとして指定された他のファイルを持つ場合に生成される実行可能なプログラムのことであり、 単独では実行不可能なものの、他のプログラムに含むのには適しているライブラリターゲットとは一線を画します。 通常、クレートには、 README ファイルに、クレートがライブラリかバイナリターゲットか、両方をもつかという情報があります。 cargo install でインストールされるバイナリは全て、インストールのルートの bin フォルダに保持されます。 Rustを rustup を使用し、独自の設定を何も行なっていなければ、このディレクトリは、 $HOME/.cargo/bin になります。 cargo install でインストールしたプログラムを実行できるようにするためには、そのディレクトリが $PATH に含まれていることを確かめてください。 例えば、第12章で、ファイルを検索する ripgrep という grep ツールのRust版があることに触れました。 ripgrep をインストールしたかったら、以下を実行することができます: $ cargo install ripgrep\nUpdating registry `https://github.com/rust-lang/crates.io-index` Downloading ripgrep v0.3.2 --snip-- Compiling ripgrep v0.3.2 Finished release [optimized + debuginfo] target(s) in 97.91 secs Installing ~/.cargo/bin/rg 出力の最後の行が、インストールされたバイナリの位置と名前を示していて、 ripgrep の場合、 rg です。 インストールディレクトリが $PATH に存在する限り、前述したように、 rg --help を走らせて、 より高速でRustらしいファイル検索ツールを使用し始めることができます!","breadcrumbs":"CargoとCrates.ioについてより詳しく » cargo install でCrates.ioからバイナリをインストールする","id":"ch14-04-installing-binaries.html#cargo-install-でcratesioからバイナリをインストールする","title":"cargo install でCrates.ioからバイナリをインストールする"},"ch14-05-extending-cargo.html#aまとめ":{"body":"Cargoで crates.io とコードを共有することは、 Rustのエコシステムを多くの異なる作業に有用にするものの一部です。Rustの標準ライブラリは、 小さく安定的ですが、クレートは共有および使用しやすく、言語とは異なるタイムラインで進化します。 積極的に crates.io で自分にとって有用なコードを共有してください; 他の誰かにとっても、役に立つものであることでしょう!","breadcrumbs":"CargoとCrates.ioについてより詳しく » まとめ","id":"ch14-05-extending-cargo.html#aまとめ","title":"まとめ"},"ch14-05-extending-cargo.html#a独自のコマンドでcargoを拡張する":{"body":"Cargoは変更する必要なく、新しいサブコマンドで拡張できるように設計されています。 $PATH にあるバイナリが cargo-something という名前なら、 cargo something を実行することで、 Cargoのサブコマンドであるかのように実行することができます。このような独自のコマンドは、 cargo --list を実行すると、列挙もされます。 cargo install を使用して拡張をインストールし、 それから組み込みのCargoツール同様に実行できることは、Cargoの設計上の非常に便利な恩恵です!","breadcrumbs":"CargoとCrates.ioについてより詳しく » 独自のコマンドでCargoを拡張する","id":"ch14-05-extending-cargo.html#a独自のコマンドでcargoを拡張する","title":"独自のコマンドでCargoを拡張する"},"ch15-00-smart-pointers.html#aスマートポインタ":{"body":"ポインタ は、メモリのアドレスを含む変数の一般的な概念です。このアドレスは、何らかの他のデータを参照、または「指します」。 Rustにおいて、最もありふれた種類のポインタは、参照であり、第4章で習いましたね。参照は、 & 記号で示唆され、指している値を借用します。データを参照すること以外に特別な能力は何もありません。 また、オーバーヘッドもなく、最も頻繁に使われる種類のポインタです。 一方、 スマートポインタ は、ポインタのように振る舞うだけでなく、追加のメタデータと能力があるデータ構造です。 スマートポインタという概念は、Rustに特有のものではありません: スマートポインタは、C++に端を発し、 他の言語にも存在しています。Rustでは、標準ライブラリに定義された色々なスマートポインタが、 参照以上の機能を提供します。この章で探究する一つの例が、 参照カウント 方式のスマートポインタ型です。 このポインタにより、所有者の数を追いかけることでデータに複数の所有者を持たせることができ、 所有者がいなくなったら、データの片付けをしてくれます。 所有権と借用の概念を使うRustで、参照とスマートポインタの別の差異は、参照はデータを借用するだけのポインタであることです; 対照的に多くの場合、スマートポインタは指しているデータを 所有 します。 その時は、スマートポインタとは呼ばなかったものの、第8章の String や Vec のように、 この本の中でいくつかのスマートポインタに遭遇してきました。これらの型はどちらも、 あるメモリを所有し、それを弄ることができるので、スマートポインタに数えられます。また、 メタデータ(キャパシティなど)や追加の能力、あるいは保証( String ならデータが常に有効なUTF-8であると保証することなど)もあります。 スマートポインタは普通、構造体を使用して実装されています。スマートポインタを通常の構造体と区別する特徴は、 スマートポインタは、 Deref と Drop トレイトを実装していることです。 Deref トレイトにより、スマートポインタ構造体のインスタンスは、 参照のように振る舞うことができるので、参照あるいはスマートポインタのどちらとも動作するコードを書くことができます。 Drop トレイトにより、スマートポインタのインスタンスがスコープを外れた時に走るコードをカスタマイズすることができます。 この章では、どちらのトレイトについても議論し、これらのトレイトがスマートポインタにとって重要な理由を説明します。 スマートポインタパターンがRustにおいてよく使われる一般的なデザインパターンだとして、この章では、全ての既存のスマートポインタを講義しません。 多くのライブラリに独自のスマートポインタがあり、自分だけのスマートポインタを書くことさえできます。 標準ライブラリの最もありふれたスマートポインタを講義します: ヒープに値を確保する Box 複数の所有権を可能にする参照カウント型の Rc RefCell を通してアクセスされ、コンパイル時ではなく実行時に借用規則を強制する型の Ref と RefMut さらに、不変な型が、内部の値を可変化するAPIを晒す 内部可変性 パターンについても講義します。 また、 循環参照 についても議論します: 循環参照により、メモリがリークする方法とそれを回避する方法です。 さあ、飛び込みましょう!","breadcrumbs":"スマートポインタ","id":"ch15-00-smart-pointers.html#aスマートポインタ","title":"スマートポインタ"},"ch15-01-box.html#aヒープのデータを指す-boxt-を使用する":{"body":"最も素直なスマートポインタは ボックス であり、その型は Box と記述されます。 ボックスにより、スタックではなくヒープにデータを格納することができます。スタックに残るのは、 ヒープデータへのポインタです。スタックとヒープの違いを再確認するには、第4章を参照されたし。 ボックスは、データをスタックの代わりにヒープに格納する以外は、パフォーマンスのオーバーヘッドはありません。 しかし、多くのおまけの能力もありません。以下のような場面で最もよく使用するでしょう: コンパイル時にはサイズを知ることができない型があり、正確なサイズを要求する文脈でその型の値を使用する時 多くのデータがあり、所有権を転送したいが、その際にデータがコピーされないようにしたい時 値を所有する必要があり、特定の型ではなく特定のトレイトを実装する型であることのみ気にかけている時 「ボックスで再帰的な型を可能にする」節で1つ目の場合について実際に説明します。 2番目の場合、多くのデータの所有権を転送するには、データがスタック上でコピーされるので、長い時間がかかり得ます。 この場面でパフォーマンスを向上させるには、多くのデータをヒープ上にボックスとして格納することができます。 そして、参照しているデータはヒープ上の1箇所に留まりつつ、少量のポインタのデータのみをスタック上でコピーするのです。 3番目のケースは、 トレイトオブジェクト として知られ、第17章の「トレイトオブジェクトで異なる型の値を許容する」の節は、 すべてその話題を説明するためだけのものです。 従って、ここで学ぶのと同じことが第17章においても適用するでしょう!","breadcrumbs":"スマートポインタ » ヒープのデータを指す Box を使用する","id":"ch15-01-box.html#aヒープのデータを指す-boxt-を使用する","title":"ヒープのデータを指す Box を使用する"},"ch15-01-box.html#aボックスで再帰的な型を可能にする":{"body":"コンパイル時に、コンパイラは、ある型が取る領域を知る必要があります。コンパイル時にサイズがわからない型の1つは、 再帰的な型 であり、これは、型の一部として同じ型の他の値を持つものです。この値のネストは、 理論的には無限に続く可能性があるので、コンパイラは再帰的な型の値が必要とする領域を知ることができないのです。 しかしながら、ボックスは既知のサイズなので、再帰的な型の定義にボックスを挟むことで再帰的な型を存在させることができるのです。 コンスリスト は関数型プログラミング言語では一般的なデータ型ですが、これを再帰的な型の例として探究しましょう。 我々が定義するコンスリストは、再帰を除いて素直です; 故に、これから取り掛かる例の概念は、 再帰的な型が関わるもっと複雑な場面に遭遇したら必ず役に立つでしょう。 コンスリストについてもっと詳しく コンスリストは、Lispプログラミング言語とその方言に由来するデータ構造です。Lispでは、 cons 関数(\"construct function\"の省略形です)が2つの引数から新しいペアを構成し、 この引数は通常、単独の値と別のペアからなります。これらのペアを含むペアがリストをなすのです。 cons関数の概念は、より一般的な関数型プログラミングの俗語にもなっています: \"to cons x onto y \"は、 俗に要素 x をこの新しいコンテナの初めに置き、コンテナ y を続けて新しいコンテナのインスタンスを生成することを意味します。 コンスリストの各要素は、2つの要素を含みます: 現在の要素の値と次の要素です。リストの最後の要素は、 次の要素なしに Nil と呼ばれる値だけを含みます。コンスリストは、繰り返し cons 関数を呼び出すことで生成されます。 繰り返しの規範事例を意味する標準的な名前は、 Nil です。これは第6章の\"null\"や\"nil\"の概念とは異なることに注意してください。 \"null\"や\"nil\"は、無効だったり存在しない値です。 関数型プログラミング言語は、頻繁にコンスリストを使用するものの、Rustではあまり使用されないデータ構造です。 Rustで要素のリストがある場合はほとんどの場合、 Vec を使用するのがよりよい選択になります。 他のより複雑で再帰的なデータ型は、様々な場面で役に立ち ます が、コンスリストから始めることで、 大して気を散らすことなく再帰的なデータ型をボックスが定義させてくれる方法を探究することができます。 リスト15-2には、コンスリストのenum定義が含まれています。このコードは、 List 型が既知のサイズではないため、まだコンパイルできないことに注意してください。 既知のサイズがないことをこれから模擬します。 ファイル名: src/main.rs enum List { Cons(i32, List), Nil,\n} リスト15-2: i32 値のコンスリストデータ構造を表すenumを定義する最初の試行 注釈: この例のためだけに i32 値だけを保持するコンスリストを実装します。第10章で議論したように、 ジェネリクスを使用してどんな型の値も格納できるコンスリストを定義して実装することもできたでしょう。 この List 型を使用してリスト 1, 2, 3 を格納すると、リスト15-3のコードのような見た目になるでしょう: ファイル名: src/main.rs use List::{Cons, Nil}; fn main() { let list = Cons(1, Cons(2, Cons(3, Nil)));\n} リスト15-3: List enumを使用してリスト 1, 2, 3 を格納する 最初の Cons 値は、 1 と別の List 値を保持しています。この List 値は、 2 とまた別の List 値を保持する別の Cons 値です。この List 値は、 3 と、ついにリストの終端を通知する非再帰的な列挙子の Nil になる List 値を保持するまたまた別の Cons 値です。 リスト15-3のコードをコンパイルしようとすると、リスト15-4に示したエラーが出ます: error[E0072]: recursive type `List` has infinite size\n(エラー: 再帰的な型`List`は無限のサイズです) --> src/main.rs:1:1 |\n1 | enum List { | ^^^^^^^^^ recursive type has infinite size\n2 | Cons(i32, List), | ----- recursive without indirection | = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `List` representable (助言: 間接参照(例: `Box`、`Rc`、あるいは`&`)をどこかに挿入して、`List`を表現可能にしてください) リスト15-4: 再帰的なenumを定義しようとすると得られるエラー エラーは、この型は「無限のサイズである」と表示しています。理由は、再帰的な列挙子を含む List を定義したからです: 自身の別の値を直接保持しているのです。結果として、コンパイラは、 List 値を格納するのに必要な領域が計算できないのです。 このエラーが得られた理由を少し噛み砕きましょう。まず、非再帰的な型の値を格納するのに必要な領域をどうコンパイラが決定しているかを見ましょう。 非再帰的な型のサイズを計算する 第6章でenum定義を議論した時にリスト6-2で定義した Message enumを思い出してください: enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32),\n} Message 値一つにメモリを確保するために必要な領域を決定するために、コンパイラは、 各列挙子を見てどの列挙子が最も領域を必要とするかを確認します。コンパイラは、 Message::Quit は全く領域を必要とせず、 Message::Move は i32 値を2つ格納するのに十分な領域が必要などと確かめます。 ただ1つの列挙子しか使用されないので、 Message 値一つが必要とする最大の領域は、 最大の列挙子を格納するのに必要になる領域です。 これをコンパイラがリスト15-2の List enumのような再帰的な型が必要とする領域を決定しようとする時に起こることと比較してください。 コンパイラは、 Cons 列挙子を見ることから始め、この列挙子には、型 i32 値が一つと型 List の値が一つ保持されます。 故に、 Cons は1つの i32 と List のサイズに等しい領域を必要とします。 List が必要とするメモリ量を計算するのに、 コンパイラは Cons 列挙子から列挙子を観察します。 Cons 列挙子は型 i32 を1つと型 List の値1つを保持し、 この過程は無限に続きます。図15-1のようにですね。 図15-1: 無限の Cons 列挙子からなる無限の List Box で既知のサイズの再帰的な型を得る コンパイラは、再帰的に定義された型に必要なメモリ量を計算できないので、リスト15-4ではエラーを返します。 しかし、エラーには確かにこの役に立つ提言が含まれています: = help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `List` representable この提言において、「間接参照」は、値を直接格納する代わりに、データ構造を変更して値へのポインタを代わりに格納することで、 値を間接的に格納することを意味します。 Box はポインタなので、コンパイラには Box が必要とする領域が必ずわかります: ポインタのサイズは、 指しているデータの量によって変わることはありません。つまり、別の List 値を直接置く代わりに、 Cons 列挙子の中に Box を配置することができます。 Box は、 Cons 列挙子の中ではなく、ヒープに置かれる次の List 値を指します。概念的には、 それでも他のリストを「保持する」リストとともに作られたリストがありますが、 この実装は今では、要素はお互いの中にあるというよりも、隣り合って配置するような感じになります。 リスト15-2の List enumの定義とリスト15-3の List の使用をリスト15-5のコードに変更することができ、 これはコンパイルが通ります: ファイル名: src/main.rs enum List { Cons(i32, Box), Nil,\n} use List::{Cons, Nil}; fn main() { let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));\n} リスト15-5: 既知のサイズにするために Box を使用する List の定義 Cons 列挙子は、1つの i32 のサイズに加えてボックスのポインタデータを格納する領域を必要とするでしょう。 Nil 列挙子は、値を格納しないので、 Cons 列挙子よりも必要な領域は小さいです。これで、 どんな List 値も i32 1つのサイズに加えてボックスのポインタデータのサイズを必要とすることがわかりました。 ボックスを使うことで、無限の再帰的な繰り返しを破壊したので、コンパイラは、 List 値を格納するのに必要なサイズを計算できます。 図15-2は、 Cons 列挙子の今の見た目を示しています。 図15-2: Cons が Box を保持しているので、無限にサイズがあるわけではない List ボックスは、間接参照とヒープメモリ確保だけを提供します; 他のスマートポインタ型で目撃するような、 他の特別な能力は何もありません。これらの特別な能力が招くパフォーマンスのオーバーヘッドもないので、 間接参照だけが必要になる唯一の機能であるコンスリストのような場合に有用になり得ます。 より多くのボックスのユースケースは第17章でもお見かけするでしょう。 Box 型は、 Deref トレイトを実装しているので、スマートポインタであり、 このトレイトにより Box の値を参照のように扱うことができます。 Box 値がスコープを抜けると、 Drop トレイト実装によりボックスが参照しているヒープデータも片付けられます。 これら2つのトレイトをより詳しく探究しましょう。これら2つのトレイトは、 この章の残りで議論する他のスマートポインタ型で提供される機能にとってさらに重要でしょう。","breadcrumbs":"スマートポインタ » ボックスで再帰的な型を可能にする","id":"ch15-01-box.html#aボックスで再帰的な型を可能にする","title":"ボックスで再帰的な型を可能にする"},"ch15-01-box.html#boxt-を使ってヒープにデータを格納する":{"body":"Box のこのユースケースを議論する前に、 Box の記法と、 Box 内に格納された値を読み書きする方法について講義しましょう。 リスト15-1は、ボックスを使用してヒープに i32 の値を格納する方法を示しています: ファイル名: src/main.rs fn main() { let b = Box::new(5); println!(\"b = {}\", b);\n} リスト15-1: ボックスを使用して i32 の値をヒープに格納する 変数 b を定義して値 5 を指す Box の値があって、この値はヒープに確保されています。このプログラムは、 b = 5 と出力するでしょう; この場合、このデータがスタックにあるのと同じような方法でボックスのデータにアクセスできます。 あらゆる所有された値同様、 b が main の終わりでするようにボックスがスコープを抜けたら、 メモリから解放されます。メモリの解放は(スタックに格納されている)ボックスと(ヒープに格納されている)指しているデータに対して起きます。 ヒープに単独の値を置くことはあまり有用ではないので、このように単独でボックスを使用することはあまりありません。 単独の i32 のような値は、既定で格納される場所であるスタックに置くことが、大多数の場合にはより適切です。 ボックスがなかったら定義することの叶わない型をボックスが定義させてくれる場合を見ましょう。","breadcrumbs":"スマートポインタ » Box を使ってヒープにデータを格納する","id":"ch15-01-box.html#boxt-を使ってヒープにデータを格納する","title":"Box を使ってヒープにデータを格納する"},"ch15-02-deref.html#a参照外し型強制が可変性と相互作用する方法":{"body":"Deref トレイトを使用して不変参照に対して * をオーバーライドするように、 DerefMut トレイトを使用して可変参照の * 演算子をオーバーライドできます。 以下の3つの場合に型やトレイト実装を見つけた時にコンパイラは、参照外し型強制を行います: T: Deref の時、 &T から &U T: DerefMut の時、 &mut T から &mut U T: Deref の時、 &mut T から &U 前者2つは、可変性を除いて一緒です。最初のケースは、 &T があり、 T が何らかの型 U への Deref を実装しているなら、 透過的に &U を得られると述べています。2番目のケースは、同じ参照外し型強制が可変参照についても起こることを述べています。 3番目のケースはもっと巧妙です: Rustはさらに、可変参照を不変参照にも型強制するのです。ですが、逆はできま せん : 不変参照は、絶対に可変参照に型強制されないのです。借用規則により、可変参照があるなら、 その可変参照がそのデータへの唯一の参照に違いありません(でなければ、プログラムはコンパイルできません)。 1つの可変参照を1つの不変参照に変換することは、借用規則を絶対に破壊しません。 不変参照を可変参照にするには、そのデータへの不変参照がたった1つしかないことが必要ですが、 借用規則はそれを保証してくれません。故に、不変参照を可変参照に変換することが可能であるという前提を敷けません。","breadcrumbs":"スマートポインタ » 参照外し型強制が可変性と相互作用する方法","id":"ch15-02-deref.html#a参照外し型強制が可変性と相互作用する方法","title":"参照外し型強制が可変性と相互作用する方法"},"ch15-02-deref.html#a参照外し演算子で値までポインタを追いかける":{"body":"普通の参照は1種のポインタであり、ポインタの捉え方の一つが、どこか他の場所に格納された値への矢印としてです。 リスト15-6で、 i32 値への参照を生成し、それから参照外し演算子を使用して参照をデータまで追いかけています: ファイル名: src/main.rs fn main() { let x = 5; let y = &x; assert_eq!(5, x); assert_eq!(5, *y);\n} リスト15-6: 参照外し演算子を使用して参照を i32 値まで追いかける 変数 x は i32 値の 5 を保持しています。 y を x への参照にセットします。 x は 5 に等しいとアサートできます。 しかしながら、 y の値に関するアサートを行いたい場合、 *y を使用して参照を指している値まで追いかけなければなりません(そのため 参照外し です)。 一旦、 y を参照外ししたら、 y が指している 5 と比較できる整数値にアクセスできます。 代わりに assert_eq!(5, y); と書こうとしたら、こんなコンパイルエラーが出るでしょう: error[E0277]: the trait bound `{integer}: std::cmp::PartialEq<&{integer}>` is\nnot satisfied\n(エラー: トレイト境界`{integer}: std::cmp::PartialEq<&{integer}>`は満たされていません) --> src/main.rs:6:5 |\n6 | assert_eq!(5, y); | ^^^^^^^^^^^^^^^^^ can't compare `{integer}` with `&{integer}` | = help: the trait `std::cmp::PartialEq<&{integer}>` is not implemented for `{integer}` (助言: トレイト`std::cmp::PartialEq<&{integer}>`は`{integer}`に対して実装されていません) 参照と数値は異なる型なので、比較することは許容されていません。参照外し演算子を使用して、 参照を指している値まで追いかけなければならないのです。","breadcrumbs":"スマートポインタ » 参照外し演算子で値までポインタを追いかける","id":"ch15-02-deref.html#a参照外し演算子で値までポインタを追いかける","title":"参照外し演算子で値までポインタを追いかける"},"ch15-02-deref.html#a独自のスマートポインタを定義する":{"body":"標準ライブラリが提供している Box 型に似たスマートポインタを構築して、スマートポインタは既定で 参照に比べてどう異なって振る舞うのか経験しましょう。それから、参照外し演算子を使う能力を追加する方法に目を向けましょう。 Box 型は究極的に1要素のタプル構造体として定義されているので、リスト15-8は、同じように MyBox 型を定義しています。 また、 Box に定義された new 関数と合致する new 関数も定義しています。 ファイル名: src/main.rs struct MyBox(T); impl MyBox { fn new(x: T) -> MyBox { MyBox(x) }\n} リスト15-8: MyBox 型を定義する MyBox という構造体を定義し、ジェネリック引数の T を宣言しています。自分の型にどんな型の値も保持させたいからです。 MyBox 型は、型 T を1要素持つタプル構造体です。 MyBox::new 関数は型 T の引数を1つ取り、 渡した値を保持する MyBox インスタンスを返します。 試しにリスト15-7の main 関数をリスト15-8に追加し、 Box の代わりに定義した MyBox 型を使うよう変更してみてください。 コンパイラは MyBox を参照外しする方法がわからないので、リスト15-9のコードはコンパイルできません。 ファイル名: src/main.rs fn main() { let x = 5; let y = MyBox::new(x); assert_eq!(5, x); assert_eq!(5, *y);\n} リスト15-9: 参照と Box を使ったのと同じように MyBox を使おうとする こちらが結果として出るコンパイルエラーです: error[E0614]: type `MyBox<{integer}>` cannot be dereferenced\n(エラー: 型`MyBox<{integer}>`は参照外しできません) --> src/main.rs:14:19 |\n14 | assert_eq!(5, *y); | ^^ MyBox に参照外しの能力を実装していないので、参照外しできません。 * 演算子で参照外しできるようにするには、 Deref トレイトを実装します。","breadcrumbs":"スマートポインタ » 独自のスマートポインタを定義する","id":"ch15-02-deref.html#a独自のスマートポインタを定義する","title":"独自のスマートポインタを定義する"},"ch15-02-deref.html#a関数やメソッドで暗黙的な参照外し型強制":{"body":"参照外し型強制 は、コンパイラが関数やメソッドの実引数に行う便利なものです。参照外し型強制は、 Deref を実装する型への参照を Deref が元の型を変換できる型への参照に変換します。参照外し型強制は、 特定の型の値への参照を関数やメソッド定義の引数型と一致しない引数として関数やメソッドに渡すときに自動的に発生します。 一連の deref メソッドの呼び出しが、提供した型を引数が必要とする型に変換します。 参照外し型強制は、関数やメソッド呼び出しを書くプログラマが & や * を多くの明示的な参照や参照外しとして追記する必要がないように、 Rustに追加されました。また、参照外し型強制のおかげで参照あるいはスマートポインタのどちらかで動くコードをもっと書くことができます。 参照外し型強制が実際に動いていることを確認するため、リスト15-8で定義した MyBox と、 リスト15-10で追加した Deref の実装を使用しましょう。リスト15-11は、 文字列スライス引数のある関数の定義を示しています: ファイル名: src/main.rs fn hello(name: &str) { println!(\"Hello, {}!\", name);\n} リスト15-11: 型 &str の引数 name のある hello 関数 hello 関数は、文字列スライスを引数として呼び出すことができます。例えば、 hello(\"Rust\") などです。 参照外し型強制により、 hello を型 MyBox の値への参照とともに呼び出すことができます。リスト15-12のようにですね: ファイル名: src/main.rs # use std::ops::Deref;\n#\n# struct MyBox(T);\n#\n# impl MyBox {\n# fn new(x: T) -> MyBox {\n# MyBox(x)\n# }\n# }\n#\n# impl Deref for MyBox {\n# type Target = T;\n#\n# fn deref(&self) -> &T {\n# &self.0\n# }\n# }\n#\n# fn hello(name: &str) {\n# println!(\"Hello, {}!\", name);\n# }\n#\nfn main() { let m = MyBox::new(String::from(\"Rust\")); hello(&m);\n} リスト15-12: hello を MyBox 値とともに呼び出し、参照外し型強制のおかげで動く ここで、 hello 関数を引数 &m とともに呼び出しています。この引数は、 MyBox 値への参照です。 リスト15-10で MyBox に Deref トレイトを実装したので、コンパイラは deref を呼び出すことで、 &MyBox を &String に変換できるのです。標準ライブラリは、 String に文字列スライスを返す Deref の実装を提供していて、 この実装は、 Deref のAPIドキュメンテーションに載っています。コンパイラはさらに deref を呼び出して、 &String を &str に変換し、これは hello 関数の定義と合致します。 Rustに参照外し型強制が実装されていなかったら、リスト15-12のコードの代わりにリスト15-13のコードを書き、 型 &MyBox の値で hello を呼び出さなければならなかったでしょう。 ファイル名: src/main.rs # use std::ops::Deref;\n#\n# struct MyBox(T);\n#\n# impl MyBox {\n# fn new(x: T) -> MyBox {\n# MyBox(x)\n# }\n# }\n#\n# impl Deref for MyBox {\n# type Target = T;\n#\n# fn deref(&self) -> &T {\n# &self.0\n# }\n# }\n#\n# fn hello(name: &str) {\n# println!(\"Hello, {}!\", name);\n# }\n#\nfn main() { let m = MyBox::new(String::from(\"Rust\")); hello(&(*m)[..]);\n} リスト15-13: Rustに参照外し型強制がなかった場合に書かなければならないであろうコード (*m) が MyBox を String に参照外ししています。そして、 & と [..] により、 文字列全体と等しい String の文字列スライスを取り、 hello のシグニチャと一致するわけです。 参照外し型強制のないコードは、これらの記号が関係するので、読むのも書くのも理解するのもより難しくなります。 参照外し型強制により、コンパイラはこれらの変換を自動的に扱えるのです。 Deref トレイトが関係する型に定義されていると、コンパイラは、型を分析し必要なだけ Deref::deref を使用して、 参照を得、引数の型と一致させます。 Deref::deref が挿入される必要のある回数は、コンパイル時に解決されるので、 参照外し型強制を活用するための実行時の代償は何もありません。","breadcrumbs":"スマートポインタ » 関数やメソッドで暗黙的な参照外し型強制","id":"ch15-02-deref.html#a関数やメソッドで暗黙的な参照外し型強制","title":"関数やメソッドで暗黙的な参照外し型強制"},"ch15-02-deref.html#boxt-を参照のように使う":{"body":"リスト15-6のコードを参照の代わりに Box を使うように書き直すことができます; 参照外し演算子は、リスト15-7に示したように動くでしょう: ファイル名: src/main.rs fn main() { let x = 5; let y = Box::new(x); assert_eq!(5, x); assert_eq!(5, *y);\n} リスト15-7: Box に対して参照外し演算子を使用する リスト15-7とリスト15-6の唯一の違いは、ここでは、 x の値を指す参照ではなく、 x の値を指すボックスのインスタンスに y をセットしていることです。 最後のアサートで参照外し演算子を使用して y が参照だった時のようにボックスのポインタを追いかけることができます。 次に、独自のボックス型を定義することで参照外し演算子を使用させてくれる Box について何が特別なのかを探究します。","breadcrumbs":"スマートポインタ » Box を参照のように使う","id":"ch15-02-deref.html#boxt-を参照のように使う","title":"Box を参照のように使う"},"ch15-02-deref.html#deref-トレイトでスマートポインタを普通の参照のように扱う":{"body":"Deref トレイトを実装することで 参照外し演算子 の * (掛け算やグロブ演算子とは違います)の振る舞いをカスタマイズすることができます。 スマートポインタを普通の参照のように扱えるように Deref を実装することで、 参照に対して処理を行うコードを書き、そのコードをスマートポインタとともにも使用できます。 まずは、参照外し演算子が普通の参照に対して動作するところを見ましょう。それから Box のように振る舞う独自の型を定義し、 参照外し演算子が新しく定義した型に対して参照のように動作しない理由を確認しましょう。 Deref トレイトを実装することでスマートポインタが参照と似た方法で動作するようにできる方法を探求します。 そして、Rustの 参照外し型強制 機能と、それにより参照やスマートポインタと協調できる方法を見ます。","breadcrumbs":"スマートポインタ » Deref トレイトでスマートポインタを普通の参照のように扱う","id":"ch15-02-deref.html#deref-トレイトでスマートポインタを普通の参照のように扱う","title":"Deref トレイトでスマートポインタを普通の参照のように扱う"},"ch15-02-deref.html#deref-トレイトを実装して型を参照のように扱う":{"body":"第10章で議論したように、トレイトを実装するには、トレイトの必須メソッドに実装を提供する必要があります。 Deref トレイトは標準ライブラリで提供されていますが、 self を借用し、 内部のデータへの参照を返す deref という1つのメソッドを実装する必要があります。リスト15-10には、 MyBox の定義に追記する Deref の実装が含まれています: ファイル名: src/main.rs use std::ops::Deref; # struct MyBox(T);\nimpl Deref for MyBox { type Target = T; fn deref(&self) -> &T { &self.0 }\n} リスト15-10: MyBox に Deref を実装する type Target = T; という記法は、 Deref トレイトが使用する関連型を定義しています。関連型は、 ジェネリック引数を宣言する少しだけ異なる方法ですが、今は気にする必要はありません; 第19章でより詳しく講義します。 deref メソッドの本体を &self.0 で埋めているので、 deref は * 演算子でアクセスしたい値への参照を返します。 リスト15-9の MyBox に * を呼び出す main 関数はこれでコンパイルでき、アサートも通ります! Deref がなければ、コンパイラは & 参照しか参照外しできなくなります。 deref メソッドによりコンパイラは、 Deref を実装するあらゆる型の値を取り、 deref メソッドを呼び出して参照外しの仕方を知っている & 参照を得る能力を獲得するのです。 リスト15-9に *y を入力した時、水面下でコンパイラは、実際にはこのようなコードを走らせていました: *(y.deref()) コンパイラは、 * 演算子を deref メソッド、それから何の変哲もない参照外しの呼び出しに置き換えるので、 deref メソッドを呼び出す必要があるかどうかを考える必要はないわけです。このRustの機能により、 普通の参照か Deref を実装した型であるかどうかに関わらず、等しく機能するコードを書かせてくれます。 deref メソッドが値への参照を返し、 *(y.deref()) のかっこの外の何の変哲もない参照外しがそれでも必要な理由は、 所有権システムです。 deref メソッドが値への参照ではなく、値を直接返したら、値は self から外にムーブされてしまいます。 今回の場合や、参照外し演算子を使用する多くの場合には MyBox の中の値の所有権を奪いたくはありません。 * 演算子は、コードで * を打つたびに、ただ1回、 deref メソッドの呼び出し、そして * 演算子の呼び出しに置き換えられることに注意してください。 * 演算子の置き換えは、無限に繰り返されないので、型 i32 に行き着き、リスト15-9で assert_eq! の 5 と合致します。","breadcrumbs":"スマートポインタ » Deref トレイトを実装して型を参照のように扱う","id":"ch15-02-deref.html#deref-トレイトを実装して型を参照のように扱う","title":"Deref トレイトを実装して型を参照のように扱う"},"ch15-03-drop.html#drop-トレイトで片付け時にコードを走らせる":{"body":"スマートポインタパターンにとって重要な2番目のトレイトは、 Drop であり、 これのおかげで値がスコープを抜けそうになった時に起こることをカスタマイズできます。 どんな型に対しても Drop トレイトの実装を提供することができ、指定したコードは、 ファイルやネットワーク接続などのリソースを解放するのに活用できます。 Drop をスマートポインタの文脈で導入しています。 Drop トレイトの機能は、ほぼ常にスマートポインタを実装する時に使われるからです。 例えば、 Box は Drop をカスタマイズしてボックスが指しているヒープの領域を解放しています。 ある言語では、プログラマがスマートポインタのインスタンスを使い終わる度にメモリやリソースを解放するコードを呼ばなければなりません。 忘れてしまったら、システムは詰め込みすぎになりクラッシュする可能性があります。Rustでは、 値がスコープを抜ける度に特定のコードが走るよう指定でき、コンパイラはこのコードを自動的に挿入します。 結果として、特定の型のインスタンスを使い終わったプログラムの箇所全部にクリーンアップコードを配置するのに配慮する必要はありません。 それでもリソースをリークすることはありません。 Drop トレイトを実装することで値がスコープを抜けた時に走るコードを指定してください。 Drop トレイトは、 self への可変参照を取る drop という1つのメソッドを実装する必要があります。 いつRustが drop を呼ぶのか確認するために、今は println! 文のある drop を実装しましょう。 リスト15-14は、唯一の独自の機能が、インスタンスがスコープを抜ける時に Dropping CustomSmartPointer! と出力するだけの、 CustomSmartPointer 構造体です。この例は、コンパイラがいつ drop 関数を走らせるかをデモしています。 ファイル名: src/main.rs struct CustomSmartPointer { data: String,\n} impl Drop for CustomSmartPointer { fn drop(&mut self) { // CustomSmartPointerをデータ`{}`とともにドロップするよ println!(\"Dropping CustomSmartPointer with data `{}`!\", self.data); }\n} fn main() { let c = CustomSmartPointer { data: String::from(\"my stuff\") }; // 俺のもの let d = CustomSmartPointer { data: String::from(\"other stuff\") }; // 別のもの println!(\"CustomSmartPointers created.\"); // CustomSmartPointerが生成された\n} リスト15-14: クリーンアップコードを配置する Drop トレイトを実装する CustomSmartPointer 構造体 Drop トレイトは、初期化処理に含まれるので、インポートする必要はありません。 CustomSmartPointer に Drop トレイトを実装し、 println! を呼び出す drop メソッドの実装を提供しています。 drop 関数の本体は、自分の型のインスタンスがスコープを抜ける時に走らせたいあらゆるロジックを配置する場所です。 ここで何らかのテキストを出力し、コンパイラがいつ drop を呼ぶのかデモしています。 main で、 CustomSmartPointer のインスタンスを2つ作り、それから CustomSmartPointers created. と出力しています。 main の最後で、 CustomSmartPointer のインスタンスはスコープを抜け、コンパイラは最後のメッセージを出力しながら、 drop メソッドに置いたコードを呼び出します。 drop メソッドを明示的に呼び出す必要はなかったことに注意してください。 このプログラムを実行すると、以下のような出力が出ます: CustomSmartPointers created.\nDropping CustomSmartPointer with data `other stuff`!\nDropping CustomSmartPointer with data `my stuff`! インスタンスがスコープを抜けた時に指定したコードを呼び出しながらコンパイラは、 drop を自動的に呼び出してくれました。 変数は、生成されたのと逆の順序でドロップされるので、 d は c より先にドロップされました。 この例は、 drop メソッドの動き方を見た目で案内するだけですが、通常は、メッセージ出力ではなく、 自分の型が走らせる必要のあるクリーンアップコードを指定するでしょう。","breadcrumbs":"スマートポインタ » Drop トレイトで片付け時にコードを走らせる","id":"ch15-03-drop.html#drop-トレイトで片付け時にコードを走らせる","title":"Drop トレイトで片付け時にコードを走らせる"},"ch15-03-drop.html#stdmemdrop-で早期に値をドロップする":{"body":"残念ながら、自動的な drop 機能を無効化することは、単純ではありません。通常、 drop を無効化する必要はありません; Drop トレイトの最重要な要点は、自動的に考慮されることです。ですが、時として、値を早期に片付けたくなる可能性があります。 一例は、ロックを管理するスマートポインタを使用する時です: 同じスコープの他のコードがロックを獲得できるように、 ロックを解放する drop メソッドを強制的に走らせたくなる可能性があります。Rustは、 Drop トレイトの drop メソッドを手動で呼ばせてくれません; スコープが終わる前に値を強制的にドロップさせたいなら、 代わりに標準ライブラリが提供する std::mem::drop 関数を呼ばなければなりません。 リスト15-14の main 関数を変更して手動で Drop トレイトの drop メソッドを呼び出そうとしたら、 コンパイルエラーになるでしょう。リスト15-15のようにですね: ファイル名: src/main.rs fn main() { let c = CustomSmartPointer { data: String::from(\"some data\") }; println!(\"CustomSmartPointer created.\"); c.drop(); // mainの終端の前にCustomSmartPointerがドロップされた println!(\"CustomSmartPointer dropped before the end of main.\");\n} リスト15-15: Drop トレイトから drop メソッドを手動で呼び出し、早期に片付けようとする このコードをコンパイルしてみようとすると、こんなエラーが出ます: error[E0040]: explicit use of destructor method\n(エラー: デストラクタメソッドを明示的に使用しています) --> src/main.rs:14:7 |\n14 | c.drop(); | ^^^^ explicit destructor calls not allowed 明示的に drop を呼び出すことは許されていないことをこのエラーメッセージは述べています。 エラーメッセージは デストラクタ という専門用語を使っていて、これは、 インスタンスを片付ける関数の一般的なプログラミング専門用語です。 デストラクタ は、 コンストラクタ に類似していて、これはインスタンスを生成します。Rustの drop 関数は、 1種の特定のデストラクタです。 コンパイラはそれでも、 main の終端で値に対して自動的に drop を呼び出すので、 drop を明示的に呼ばせてくれません。 コンパイラが2回同じ値を片付けようとするので、これは 二重解放 エラーになるでしょう。 値がスコープを抜けるときに drop が自動的に挿入されるのを無効化できず、 drop メソッドを明示的に呼ぶこともできません。 よって、値を早期に片付けさせる必要があるなら、 std::mem::drop 関数を使用できます。 std::mem::drop 関数は、 Drop トレイトの drop メソッドとは異なります。 早期に強制的にドロップさせたい値を引数で渡すことで呼びます。この関数は初期化処理に含まれているので、 リスト15-15の main を変更して drop 関数を呼び出せます。リスト15-16のようにですね: ファイル名: src/main.rs # struct CustomSmartPointer {\n# data: String,\n# }\n#\n# impl Drop for CustomSmartPointer {\n# fn drop(&mut self) {\n# println!(\"Dropping CustomSmartPointer!\");\n# }\n# }\n#\nfn main() { let c = CustomSmartPointer { data: String::from(\"some data\") }; println!(\"CustomSmartPointer created.\"); drop(c); // CustomSmartPointerはmainが終わる前にドロップされた println!(\"CustomSmartPointer dropped before the end of main.\");\n} リスト15-16: 値がスコープを抜ける前に明示的にドロップするために std::mem::drop を呼び出す このコードを実行すると、以下のように出力されます: CustomSmartPointer created.\nDropping CustomSmartPointer with data `some data`!\nCustomSmartPointer dropped before the end of main. Dropping CustomSmartPointer with data `some data`! というテキストが、 CustomSmartPointer created. と CustomSmartPointer dropped before the end of main. テキストの間に出力されるので、 drop メソッドのコードがその時点で呼び出されて c をドロップしたことを示しています。 Drop トレイト実装で指定されたコードをいろんな方法で使用し、片付けを便利で安全にすることができます: 例を挙げれば、これを使用して独自のメモリアロケータを作ることもできるでしょう! Drop トレイトとRustの所有権システムがあれば、 コンパイラが自動的に行うので、片付けを覚えておく必要はなくなります。 まだ使用中の値を間違って片付けてしまうことに起因する問題を心配する必要もなくて済みます: 参照が常に有効であると確認してくれる所有権システムが、値が最早使用されなくなった時に drop が1回だけ呼ばれることを保証してくれるのです。 これで Box とスマートポインタの特徴の一部を調査したので、標準ライブラリに定義されている他のスマートポインタをいくつか見ましょう。","breadcrumbs":"スマートポインタ » std::mem::drop で早期に値をドロップする","id":"ch15-03-drop.html#stdmemdrop-で早期に値をドロップする","title":"std::mem::drop で早期に値をドロップする"},"ch15-04-rc.html#rct-でデータを共有する":{"body":"リスト15-5のコンスリストの例に回帰しましょう。 Box を使って定義したことを思い出してください。 今回は、両方とも3番目のリストの所有権を共有する2つのリストを作成します。 これは概念的には図15-3のような見た目になります: 図15-3: 3番目のリスト、 a の所有権を共有する2つのリスト、 b と c 5と10を含むリスト a を作ります。さらにもう2つリストを作ります: 3で始まる b と4で始まる c です。 b と c のどちらもそれから5と10を含む最初の a リストに続きます。換言すれば、 どちらのリストも5と10を含む最初のリストを共有しています。 List の定義を使用して Box とともにこの筋書きを実装しようとしても、うまくいきません。 リスト15-17のようにですね: ファイル名: src/main.rs enum List { Cons(i32, Box), Nil,\n} use List::{Cons, Nil}; fn main() { let a = Cons(5, Box::new(Cons(10, Box::new(Nil)))); let b = Cons(3, Box::new(a)); let c = Cons(4, Box::new(a));\n} リスト15-17: 3番目のリストの所有権を共有しようとする Box を使った2つのリストを存在させることはできないとデモする このコードをコンパイルすると、こんなエラーが出ます: error[E0382]: use of moved value: `a` --> src/main.rs:13:30 |\n12 | let b = Cons(3, Box::new(a)); | - value moved here\n13 | let c = Cons(4, Box::new(a)); | ^ value used here after move | = note: move occurs because `a` has type `List`, which does not implement the `Copy` trait Cons 列挙子は、保持しているデータを所有するので、 b リストを作成する時に、 a が b にムーブされ、 b が a を所有します。それから c を作る際に再度 a を使用しようとすると、 a はムーブ済みなので、できないわけです。 Cons の定義を代わりに参照を保持するように変更することもできますが、そうしたら、 ライフタイム引数を指定しなければなりません。ライフタイム引数を指定することで、 リストの各要素が最低でもリスト全体と同じ期間だけ生きることを指定することになります。 例えば、借用チェッカーは let a = Cons(10, &Nil); をコンパイルさせてくれません。 一時的な Nil 値が、 a が参照を得られるより前にドロップされてしまうからです。 代わりに、 List の定義をリスト15-18のように、 Box の箇所に Rc を使うように変更します。 これで各 Cons 列挙子は、値と List を指す Rc を保持するようになりました。 b を作る際、 a の所有権を奪うのではなく、 a が保持している Rc をクローンします。それによって、 参照の数が1から2に増え、 a と b にその Rc にあるデータの所有権を共有させます。 また、 c を生成する際にも a をクローンするので、参照の数は2から3になります。 Rc::clone を呼ぶ度に、 Rc 内のデータの参照カウントが増え、参照が0にならない限りデータは片付けられません。 ファイル名: src/main.rs enum List { Cons(i32, Rc), Nil,\n} use List::{Cons, Nil};\nuse std::rc::Rc; fn main() { let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); let b = Cons(3, Rc::clone(&a)); let c = Cons(4, Rc::clone(&a));\n} リスト15-18: Rc を使用する List の定義 初期化処理に含まれていないので、 use 文を追加して Rc をスコープに導入する必要があります。 main で5と10を保持するリストを作成し、 a の新しい Rc に格納しています。それから、 b と c を作成する際に、 Rc::clone 関数を呼び出し、引数として a の Rc への参照を渡しています。 Rc::clone(&a) ではなく、 a.clone() を呼ぶこともできますが、Rustのしきたりは、この場合 Rc::clone を使うことです。 Rc::clone の実装は、多くの型の clone 実装のように、全てのデータのディープコピーをすることではありません。 Rc::clone の呼び出しは、参照カウントをインクリメントするだけであり、時間はかかりません。 データのディープコピーは時間がかかることもあります。参照カウントに Rc::clone を使うことで、 視覚的にディープコピーをする類のクローンと参照カウントを増やす種類のクローンを区別することができます。 コード内でパフォーマンスの問題を探す際、ディープコピーのクローンだけを考慮し、 Rc::clone の呼び出しを無視できるのです。","breadcrumbs":"スマートポインタ » Rc でデータを共有する","id":"ch15-04-rc.html#rct-でデータを共有する","title":"Rc でデータを共有する"},"ch15-04-rc.html#rct-は参照カウント方式のスマートポインタ":{"body":"大多数の場合、所有権は明らかです: 一体どの変数が与えられた値を所有しているかわかるのです。 ところが、単独の値が複数の所有者を持つ可能性のある場合もあります。例えば、グラフデータ構造では、 複数の辺が同じノードを指す可能性があり、概念的にそのノードはそれを指す全ての辺に所有されるわけです。 指す辺がなくならない限り、ノードは片付けられるべきではありません。 複数の所有権を可能にするため、Rustには Rc という型があり、これは、 reference counting (参照カウント)の省略形です。 Rc 型は、値がまだ使用中かどうか決定する値への参照の数を追跡します。値への参照が0なら、どの参照も無効にすることなく、 値は片付けられます。 Rc を家族部屋のテレビと想像してください。1人がテレビを見に部屋に入ったら、テレビをつけます。 他の人も部屋に入ってテレビを観ることができます。最後の人が部屋を離れる時、 もう使用されていないので、テレビを消します。他の人がまだ観ているのに誰かがテレビを消したら、 残りのテレビ視聴者が騒ぐでしょう! ヒープにプログラムの複数箇所で読む何らかのデータを確保したいけれど、 コンパイル時にはどの部分が最後にデータを使用し終わるか決定できない時に Rc 型を使用します。 どの部分が最後に終わるかわかっているなら、 単にその部分をデータの所有者にして、コンパイル時に強制される普通の所有権ルールが効果を発揮するでしょう。 Rc は、シングルスレッドの筋書きで使用するためだけのものであることに注意してください。 第16章で並行性について議論する時に、マルチスレッドプログラムで参照カウントをする方法を講義します。","breadcrumbs":"スマートポインタ » Rc は、参照カウント方式のスマートポインタ","id":"ch15-04-rc.html#rct-は参照カウント方式のスマートポインタ","title":"Rc は、参照カウント方式のスマートポインタ"},"ch15-04-rc.html#rct-をクローンすると参照カウントが増える":{"body":"a の Rc への参照を作ったりドロップする毎に参照カウントが変化するのが確かめられるように、 リスト15-18の動く例を変更しましょう。 リスト15-19で、リスト c を囲む内側のスコープができるよう main を変更します; そうすれば、 c がスコープを抜けるときに参照カウントがどう変化するか確認できます。 ファイル名: src/main.rs # enum List {\n# Cons(i32, Rc),\n# Nil,\n# }\n#\n# use List::{Cons, Nil};\n# use std::rc::Rc;\n#\nfn main() { let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil))))); // a生成後のカウント = {} println!(\"count after creating a = {}\", Rc::strong_count(&a)); let b = Cons(3, Rc::clone(&a)); // b生成後のカウント = {} println!(\"count after creating b = {}\", Rc::strong_count(&a)); { let c = Cons(4, Rc::clone(&a)); // c生成後のカウント = {} println!(\"count after creating c = {}\", Rc::strong_count(&a)); } // cがスコープを抜けた後のカウント = {} println!(\"count after c goes out of scope = {}\", Rc::strong_count(&a));\n} リスト15-19: 参照カウントを出力する プログラム内で参照カウントが変更される度に、参照カウントを出力します。参照カウントは、 Rc::strong_count 関数を呼び出すことで得られます。 Rc 型には weak_count もあるので、 この関数は count ではなく strong_count と命名されています; weak_count の使用目的は、 「循環参照を回避する」節で確かめます。 このコードは、以下の出力をします: count after creating a = 1\ncount after creating b = 2\ncount after creating c = 3\ncount after c goes out of scope = 2 a の Rc は最初1という参照カウントであることがわかります; そして、 clone を呼び出す度に、 カウントは1ずつ上がります。 c がスコープを抜けると、カウントは1下がります。参照カウントを増やすのに、 Rc::clone を呼ばなければいけなかったみたいに参照カウントを減らすのに関数を呼び出す必要はありません: Rc 値がスコープを抜けるときに Drop トレイトの実装が自動的に参照カウントを減らします。 この例でわからないことは、 b そして a が、 main の終端でスコープを抜ける時に、カウントが0になり、 その時点で Rc が完全に片付けられることです。 Rc を使用すると、単独の値に複数の所有者を持たせることができ、 所有者のいずれかが存在している限り、値が有効であり続けることをカウントは保証します。 不変参照経由で、 Rc は読み取り専用にプログラムの複数箇所間でデータを共有させてくれます。 Rc が複数の可変参照を存在させることも許可してくれたら、第4章で議論した借用ルールの1つを侵害する虞(おそれ)があります: 同じ場所への複数の可変借用は、データ競合や矛盾を引き起こすことがあるのです。しかし、 データを可変化する能力はとても有用です!次の節では、内部可変性パターンと、 Rc と絡めて使用してこの不変性制限を手がけられる RefCell 型について議論します。","breadcrumbs":"スマートポインタ » Rc をクローンすると、参照カウントが増える","id":"ch15-04-rc.html#rct-をクローンすると参照カウントが増える","title":"Rc をクローンすると、参照カウントが増える"},"ch15-05-interior-mutability.html#a内部可変性-不変値への可変借用":{"body":"借用規則の結果は、不変値がある時、可変で借用することはできないということです。 例えば、このコードはコンパイルできません: fn main() { let x = 5; let y = &mut x;\n} このコードをコンパイルしようとしたら、以下のようなエラーが出るでしょう: error[E0596]: cannot borrow immutable local variable `x` as mutable\n(エラー: 不変なローカル変数`x`を可変で借用することはできません) --> src/main.rs:3:18 |\n2 | let x = 5; | - consider changing this to `mut x`\n3 | let y = &mut x; | ^ cannot borrow mutably ですが、メソッド内で値が自身を可変化するけれども、他のコードにとっては、 不変に見えることが有用な場面もあります。その値のメソッドの外のコードは、その値を可変化することはできないでしょう。 RefCell を使うことは、内部可変性を取得する能力を得る1つの方法です。しかし、 RefCell は借用規則を完全に回避するものではありません: コンパイラの借用チェッカーは、内部可変性を許可し、 借用規則は代わりに実行時に精査されます。この規則を侵害したら、コンパイルエラーではなく panic! になるでしょう。 RefCell を使用して不変値を可変化する実践的な例に取り組み、それが役に立つ理由を確認しましょう。 内部可変性のユースケース: モックオブジェクト テストダブル は、テスト中に別の型の代わりに使用される型の一般的なプログラミングの概念です。 モックオブジェクト は、テスト中に起きることを記録するテストダブルの特定の型なので、 正しい動作が起きたことをアサートできます。 編注 : テストダブルとは、ソフトウェアテストにおいて、テスト対象が依存しているコンポーネントを置き換える代用品のこと。 Rustには、他の言語でいうオブジェクトは存在せず、また、他の言語のように標準ライブラリにモックオブジェクトの機能が組み込まれてもいません。 ですが、同じ目的をモックオブジェクトとして提供する構造体を作成することは確実にできます。 以下が、テストを行う筋書きです: 値を最大値に対して追跡し、現在値がどれくらい最大値に近いかに基づいてメッセージを送信するライブラリを作成します。 このライブラリは、ユーザが行うことのできるAPIコールの数の割り当てを追跡するのに使用することができるでしょう。 作成するライブラリは、値がどれくらい最大に近いかと、いつどんなメッセージになるべきかを追いかける機能を提供するだけです。 このライブラリを使用するアプリケーションは、メッセージを送信する機構を提供すると期待されるでしょう: アプリケーションは、アプリケーションにメッセージを置いたり、メールを送ったり、テキストメッセージを送るなどできるでしょう。 ライブラリはその詳細を知る必要はありません。必要なのは、提供する Messenger と呼ばれるトレイトを実装している何かなのです。 リスト15-20は、ライブラリのコードを示しています: ファイル名: src/lib.rs pub trait Messenger { fn send(&self, msg: &str);\n} pub struct LimitTracker<'a, T: 'a + Messenger> { messenger: &'a T, value: usize, max: usize,\n} impl<'a, T> LimitTracker<'a, T> where T: Messenger { pub fn new(messenger: &T, max: usize) -> LimitTracker { LimitTracker { messenger, value: 0, max, } } pub fn set_value(&mut self, value: usize) { self.value = value; let percentage_of_max = self.value as f64 / self.max as f64; if percentage_of_max >= 0.75 && percentage_of_max < 0.9 { // 警告: 割り当ての75%以上を使用してしまいました self.messenger.send(\"Warning: You've used up over 75% of your quota!\"); } else if percentage_of_max >= 0.9 && percentage_of_max < 1.0 { // 切迫した警告: 割り当ての90%以上を使用してしまいました self.messenger.send(\"Urgent warning: You've used up over 90% of your quota!\"); } else if percentage_of_max >= 1.0 { // エラー: 割り当てを超えています self.messenger.send(\"Error: You are over your quota!\"); } }\n} リスト15-20: 値が最大値にどれくらい近いかを追跡し、特定のレベルの時に警告するライブラリ このコードの重要な部分の1つは、 Messenger トレイトには、 self への不変参照とメッセージのテキストを取る send というメソッドが1つあることです。 これが、モックオブジェクトが持つ必要のあるインターフェイスなのです。もう1つの重要な部分は、 LimitTracker の set_value メソッドの振る舞いをテストしたいということです。 value 引数に渡すものを変えることができますが、 set_value はアサートを行えるものは何も返してくれません。 LimitTracker を Messenger トレイトを実装する何かと、 max の特定の値で生成したら、 value に異なる数値を渡した時にメッセンジャーは適切なメッセージを送ると指示されると言えるようになりたいです。 send を呼び出す時にメールやテキストメッセージを送る代わりに送ると指示されたメッセージを追跡するだけのモックオブジェクトが必要です。 モックオブジェクトの新規インスタンスを生成し、モックオブジェクトを使用する LimitTracker を生成し、 LimitTracker の set_value を呼び出し、それからモックオブジェクトに期待しているメッセージがあることを確認できます。 リスト15-21は、それだけをするモックオブジェクトを実装しようとするところを示しますが、借用チェッカーが許可してくれません: ファイル名: src/lib.rs #[cfg(test)]\nmod tests { use super::*; struct MockMessenger { sent_messages: Vec, } impl MockMessenger { fn new() -> MockMessenger { MockMessenger { sent_messages: vec![] } } } impl Messenger for MockMessenger { fn send(&self, message: &str) { self.sent_messages.push(String::from(message)); } } #[test] fn it_sends_an_over_75_percent_warning_message() { let mock_messenger = MockMessenger::new(); let mut limit_tracker = LimitTracker::new(&mock_messenger, 100); limit_tracker.set_value(80); assert_eq!(mock_messenger.sent_messages.len(), 1); }\n} リスト15-21: 借用チェッカーが許可してくれない MockMessenger を実装しようとする このテストコードは String の Vec で送信すると指示されたメッセージを追跡する sent_messages フィールドのある MockMessenger 構造体を定義しています。 また、空のメッセージリストから始まる新しい MockMessenger 値を作るのを便利にしてくれる関連関数の new も定義しています。 それから MockMessenger に Messenger トレイトを実装しているので、 LimitTracker に MockMessenger を与えられます。 send メソッドの定義で引数として渡されたメッセージを取り、 sent_messages の MockMessenger リストに格納しています。 テストでは、 max 値の75%以上になる何かに value をセットしろと LimitTracker が指示される時に起きることをテストしています。 まず、新しい MockMessenger を生成し、空のメッセージリストから始まります。そして、 新しい LimitTracker を生成し、新しい MockMessenger の参照と100という max 値を与えます。 LimitTracker の set_value メソッドは80という値で呼び出し、これは100の75%を上回っています。 そして、 MockMessenger が追いかけているメッセージのリストが、今は1つのメッセージを含んでいるはずとアサートします。 ところが、以下のようにこのテストには1つ問題があります: error[E0596]: cannot borrow immutable field `self.sent_messages` as mutable\n(エラー: 不変なフィールド`self.sent_messages`を可変で借用できません) --> src/lib.rs:52:13 |\n51 | fn send(&self, message: &str) { | ----- use `&mut self` here to make mutable\n52 | self.sent_messages.push(String::from(message)); | ^^^^^^^^^^^^^^^^^^ cannot mutably borrow immutable field send メソッドは self への不変参照を取るので、 MockMessenger を変更してメッセージを追跡できないのです。 代わりに &mut self を使用するというエラーテキストからの提言を選ぶこともできないのです。 そうしたら、 send のシグニチャが、 Messenger トレイト定義のシグニチャと一致しなくなるからです(気軽に試してエラーメッセージを確認してください)。 これは、内部可変性が役に立つ場面なのです! sent_messages を RefCell 内部に格納し、 そうしたら send メッセージは、 sent_messages を変更して見かけたメッセージを格納できるようになるでしょう。 リスト15-22は、それがどんな感じかを示しています: ファイル名: src/lib.rs #[cfg(test)]\nmod tests { use super::*; use std::cell::RefCell; struct MockMessenger { sent_messages: RefCell>, } impl MockMessenger { fn new() -> MockMessenger { MockMessenger { sent_messages: RefCell::new(vec![]) } } } impl Messenger for MockMessenger { fn send(&self, message: &str) { self.sent_messages.borrow_mut().push(String::from(message)); } } #[test] fn it_sends_an_over_75_percent_warning_message() { // --snip--\n# let mock_messenger = MockMessenger::new();\n# let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);\n# limit_tracker.set_value(75); assert_eq!(mock_messenger.sent_messages.borrow().len(), 1); }\n} リスト15-22: 外側の値は不変と考えられる一方で RefCell で内部の値を可変化する さて、 sent_messages フィールドは、 Vec ではなく、型 RefCell> になりました。 new 関数で、空のベクタの周りに RefCell> を新しく作成しています。 send メソッドの実装については、最初の引数はそれでも self への不変借用で、トレイト定義と合致しています。 RefCell> の borrow_mut を self.sent_messages に呼び出し、 RefCell> の中の値への可変参照を得て、これはベクタになります。 それからベクタへの可変参照に push を呼び出して、テスト中に送られるメッセージを追跡しています。 行わなければならない最後の変更は、アサート内部にあります: 内部のベクタにある要素の数を確認するため、 RefCell> に borrow を呼び出し、ベクタへの不変参照を得ています。 RefCell の使用法を見かけたので、動作の仕方を深掘りしましょう! RefCell で実行時に借用を追いかける 不変および可変参照を作成する時、それぞれ & と &mut 記法を使用します。 RefCell では、 borrow と borrow_mut メソッドを使用し、これらは RefCell に所属する安全なAPIの一部です。 borrow メソッドは、スマートポインタ型の Ref を返し、 borrow_mut はスマートポインタ型の RefMut を返します。 どちらの型も Deref を実装しているので、普通の参照のように扱うことができます。 RefCell は、現在活動中の Ref と RefMut スマートポインタの数を追いかけます。 borrow を呼び出す度に、 RefCell は活動中の不変参照の数を増やします。 Ref の値がスコープを抜けたら、 不変参照の数は1下がります。コンパイル時の借用規則と全く同じように、 RefCell はいかなる時も、 複数の不変借用または1つの可変借用を持たせてくれるのです。 これらの規則を侵害しようとすれば、参照のようにコンパイルエラーになるのではなく、 RefCell の実装は実行時にパニックするでしょう。リスト15-23は、リスト15-22の send 実装に対する変更を示しています。 同じスコープで2つの可変借用が活動するようわざと生成し、 RefCell が実行時にこれをすることを阻止してくれるところを説明しています。 ファイル名: src/lib.rs impl Messenger for MockMessenger { fn send(&self, message: &str) { let mut one_borrow = self.sent_messages.borrow_mut(); let mut two_borrow = self.sent_messages.borrow_mut(); one_borrow.push(String::from(message)); two_borrow.push(String::from(message)); }\n} リスト15-23: 同じスコープで2つの可変参照を生成して RefCell がパニックすることを確かめる borrow_mut から返ってきた RefMut スマートポインタに対して変数 one_borrow を生成しています。 そして、同様にして変数 two_borrow にも別の可変借用を生成しています。これにより同じスコープで2つの可変参照ができ、 これは許可されないことです。このテストを自分のライブラリ用に走らせると、リスト15-23のコードはエラーなくコンパイルできますが、 テストは失敗するでしょう: ---- tests::it_sends_an_over_75_percent_warning_message stdout ---- thread 'tests::it_sends_an_over_75_percent_warning_message' panicked at\n'already borrowed: BorrowMutError', src/libcore/result.rs:906:4 (スレッド'tests::it_sends_an_over_75_percent_warning_message'は、\n'すでに借用されています: BorrowMutError', src/libcore/result.rs:906:4でパニックしました)\nnote: Run with `RUST_BACKTRACE=1` for a backtrace. コードは、 already borrowed: BorrowMutError というメッセージとともにパニックしたことに注目してください。 このようにして RefCell は実行時に借用規則の侵害を扱うのです。 コンパイル時ではなく実行時に借用エラーをキャッチするということは、開発過程の遅い段階でコードのミスを発見し、 コードをプロダクションにデプロイする時まで発見しない可能性もあることを意味します。また、 コンパイル時ではなく、実行時に借用を追いかける結果として、少し実行時にパフォーマンスを犠牲にするでしょう。 しかしながら、 RefCell を使うことで、不変値のみが許可される文脈で使用しつつ、 自身を変更して見かけたメッセージを追跡するモックオブジェクトを書くことが可能になります。 代償はありますが、 RefCell を使用すれば、普通の参照よりも多くの機能を得ることができるわけです。","breadcrumbs":"スマートポインタ » 内部可変性: 不変値への可変借用","id":"ch15-05-interior-mutability.html#a内部可変性-不変値への可変借用","title":"内部可変性: 不変値への可変借用"},"ch15-05-interior-mutability.html#rct-と-refcellt-を組み合わせることで可変なデータに複数の所有者を持たせる":{"body":"RefCell の一般的な使用法は、 Rc と組み合わせることにあります。 Rc は何らかのデータに複数の所有者を持たせてくれるけれども、 そのデータに不変のアクセスしかさせてくれないことを思い出してください。 RefCell を抱える Rc があれば、 複数の所有者を持ち そして 、可変化できる値を得ることができるのです。 例を挙げれば、 Rc を使用して複数のリストに別のリストの所有権を共有させたリスト15-18のコンスリストの例を思い出してください。 Rc は不変値だけを抱えるので、一旦生成したら、リストの値はどれも変更できません。 RefCell を含めて、 リストの値を変更する能力を得ましょう。 RefCell を Cons 定義で使用することで、 リスト全てに格納されている値を変更できることをリスト15-24は示しています: ファイル名: src/main.rs #[derive(Debug)]\nenum List { Cons(Rc>, Rc), Nil,\n} use List::{Cons, Nil};\nuse std::rc::Rc;\nuse std::cell::RefCell; fn main() { let value = Rc::new(RefCell::new(5)); let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil))); let b = Cons(Rc::new(RefCell::new(6)), Rc::clone(&a)); let c = Cons(Rc::new(RefCell::new(10)), Rc::clone(&a)); *value.borrow_mut() += 10; println!(\"a after = {:?}\", a); println!(\"b after = {:?}\", b); println!(\"c after = {:?}\", c);\n} リスト15-24: Rc> で可変化できる List を生成する Rc> のインスタンスの値を生成し、 value という名前の変数に格納しているので、 直接後ほどアクセスすることができます。そして、 a に value を持つ Cons 列挙子で List を生成しています。 value から a に所有権を移したり、 a が value から借用するのではなく、 a と value どちらにも中の 5 の値の所有権を持たせるよう、 value をクローンする必要があります。 リスト a を Rc に包んでいるので、リスト b と c を生成する時に、どちらも a を参照できます。 リスト15-18ではそうしていました。 a 、 b 、 c のリストを作成した後、 value の値に10を足しています。これを value の borrow_mut を呼び出すことで行い、 これは、第5章で議論した自動参照外し機能(「 -> 演算子はどこに行ったの?」節をご覧ください)を使用して、 Rc を内部の RefCell 値に参照外ししています。 borrow_mut メソッドは、 RefMut スマートポインタを返し、それに対して参照外し演算子を使用し、中の値を変更します。 a 、 b 、 c を出力すると、全て5ではなく、変更された15という値になっていることがわかります。 a after = Cons(RefCell { value: 15 }, Nil)\nb after = Cons(RefCell { value: 6 }, Cons(RefCell { value: 15 }, Nil))\nc after = Cons(RefCell { value: 10 }, Cons(RefCell { value: 15 }, Nil)) このテクニックは非常に綺麗です! RefCell を使用することで表面上は不変な List 値を持てます。 しかし、内部可変性へのアクセスを提供する RefCell のメソッドを使用できるので、必要な時にはデータを変更できます。 借用規則を実行時に精査することでデータ競合を防ぎ、時としてデータ構造でちょっとのスピードを犠牲にこの柔軟性を得るのは価値があります。 標準ライブラリには、 Cell などの内部可変性を提供する他の型もあり、この型は、内部値への参照を与える代わりに、 値は Cell の内部や外部へコピーされる点を除き似ています。また Mutex もあり、 これはスレッド間で使用するのが安全な内部可変性を提供します; 第16章でその使いみちについて議論しましょう。 これらの型の違いをより詳しく知るには、標準ライブラリのドキュメンテーションをチェックしてください。","breadcrumbs":"スマートポインタ » Rc と RefCell を組み合わせることで可変なデータに複数の所有者を持たせる","id":"ch15-05-interior-mutability.html#rct-と-refcellt-を組み合わせることで可変なデータに複数の所有者を持たせる","title":"Rc と RefCell を組み合わせることで可変なデータに複数の所有者を持たせる"},"ch15-05-interior-mutability.html#refcellt-で実行時に借用規則を強制する":{"body":"Rc と異なり、 RefCell 型は、保持するデータに対して単独の所有権を表します。では、 どうして RefCell が Box のような型と異なるのでしょうか?第4章で学んだ借用規則を思い出してください: いかなる時も(以下の両方ではなく、)1つの可変参照かいくつもの不変参照の どちらか が可能になる 参照は常に有効でなければならない。 参照と Box では、借用規則の不変条件は、コンパイル時に強制されています。 RefCell では、 これらの不変条件は、 実行時に 強制されます。参照でこれらの規則を破ったら、コンパイルエラーになりました。 RefCell でこれらの規則を破ったら、プログラムはパニックし、終了します。 コンパイル時に借用規則を精査することの利点は、エラーが開発過程の早い段階で捕捉されることと、 あらかじめ全ての分析が終わるので、実行パフォーマンスへの影響がないことです。それらの理由により、 多くの場合でコンパイル時に借用規則を精査することが最善の選択肢であり、これがRustの既定になっているのです。 借用規則を実行時に代わりに精査する利点は、コンパイル時の精査では許容されない特定のメモリ安全な筋書きが許容されることです。 Rustコンパイラのような静的解析は、本質的に保守的です。コードの特性には、コードを解析するだけでは検知できないものもあります: 最も有名な例は停止性問題であり、この本の範疇を超えていますが、調べると面白い話題です。 不可能な分析もあるので、Rustのコンパイラが、コードが所有権規則に応じていると確証を得られない場合、 正しいプログラムを拒否する可能性があります; このように、保守的なのです。コンパイラが不正なプログラムを受け入れたら、 ユーザは、コンパイラが行う保証を信じることはできなくなるでしょう。しかしながら、 コンパイラが正当なプログラムを拒否するのなら、プログラマは不便に思うでしょうが、悲劇的なことは何も起こり得ません。 コードが借用規則に従っているとプログラマは確証を得ているが、コンパイラがそれを理解し保証することができない時に RefCell 型は有用です。 Rc と類似して、 RefCell もシングルスレッドの筋書きで使用するためのものであり、 試しにマルチスレッドの文脈で使ってみようとすると、コンパイルエラーを出します。 RefCell の機能をマルチスレッドのプログラムで得る方法については、第16章で語ります。 こちらに Box , Rc , RefCell を選択する理由を要約しておきます: Rc は、同じデータに複数の所有者を持たせてくれる; Box と RefCell は単独の所有者。 Box では、不変借用も可変借用もコンパイル時に精査できる; Rc では不変借用のみがコンパイル時に精査できる; RefCell では、不変借用も可変借用も実行時に精査される。 RefCell は実行時に精査される可変借用を許可するので、 RefCell が不変でも、 RefCell 内の値を可変化できる。 不変な値の中の値を可変化することは、 内部可変性 パターンです。内部可変性が有用になる場面を見て、 それが可能になる方法を調査しましょう。","breadcrumbs":"スマートポインタ » RefCell で実行時に借用規則を強制する","id":"ch15-05-interior-mutability.html#refcellt-で実行時に借用規則を強制する","title":"RefCell で実行時に借用規則を強制する"},"ch15-05-interior-mutability.html#refcellt-と内部可変性パターン":{"body":"内部可変性は、そのデータへの不変参照がある時でさえもデータを可変化できるRustでのデザインパターンです: 普通、この行動は借用規則により許可されません。データを可変化するために、このパターンは、データ構造内で unsafe コードを使用して、 可変性と借用を支配するRustの通常の規則を捻じ曲げています。まだ、unsafeコードについては講義していません; 第19章で行います。たとえ、コンパイラが保証できなくても、借用規則に実行時に従うことが保証できる時、 内部可変性パターンを使用した型を使用できます。関係する unsafe コードはそうしたら、安全なAPIにラップされ、 外側の型は、それでも不変です。 内部可変性パターンに従う RefCell 型を眺めてこの概念を探究しましょう。","breadcrumbs":"スマートポインタ » RefCell と内部可変性パターン","id":"ch15-05-interior-mutability.html#refcellt-と内部可変性パターン","title":"RefCell と内部可変性パターン"},"ch15-06-reference-cycles.html#aまとめ":{"body":"この章は、スマートポインタを使用してRustが既定で普通の参照に対して行うのと異なる保証や代償を行う方法を講義しました。 Box 型は、既知のサイズで、ヒープに確保されたデータを指します。 Rc 型は、ヒープのデータへの参照の数を追跡するので、 データは複数の所有者を保有できます。内部可変性のある RefCell 型は、不変型が必要だけれども、 その型の中の値を変更する必要がある時に使用できる型を与えてくれます; また、コンパイル時ではなく実行時に借用規則を強制します。 Deref と Drop トレイトについても議論しましたね。これらは、スマートポインタの多くの機能を可能にしてくれます。 メモリリークを引き起こす循環参照と Weak でそれを回避する方法も探究しました。 この章で興味をそそられ、独自のスマートポインタを実装したくなったら、もっと役に立つ情報を求めて、 “The Rustonomicon” をチェックしてください。 訳注: 日本語版のThe Rustonomiconは こちら です。 次は、Rustでの並行性について語ります。もういくつか新しいスマートポインタについてさえも学ぶでしょう。","breadcrumbs":"スマートポインタ » まとめ","id":"ch15-06-reference-cycles.html#aまとめ","title":"まとめ"},"ch15-06-reference-cycles.html#a循環参照させる":{"body":"リスト15-25の List enumの定義と tail メソッドから始めて、どう循環参照が起こる可能性があるのかとその回避策を見ましょう: ファイル名: src/main.rs # fn main() {}\nuse std::rc::Rc;\nuse std::cell::RefCell;\nuse List::{Cons, Nil}; #[derive(Debug)]\nenum List { Cons(i32, RefCell>), Nil,\n} impl List { fn tail(&self) -> Option<&RefCell>> { match *self { Cons(_, ref item) => Some(item), Nil => None, } }\n} リスト15-25: Cons 列挙子が参照しているものを変更できるように RefCell を抱えているコンスリストの定義 リスト15-5の List 定義の別バリエーションを使用しています。 Cons 列挙子の2番目の要素はこれで RefCell> になり、 リスト15-24のように i32 値を変更する能力があるのではなく、 Cons 列挙子が指している List 値の先を変えたいということです。 また、 tail メソッドを追加して Cons 列挙子があるときに2番目の要素にアクセスするのが便利になるようにしています。 リスト15-26でリスト15-25の定義を使用する main 関数を追加しています。このコードは、 a にリストを、 b に a のリストを指すリストを作成します。それから a のリストを変更して b を指し、循環参照させます。 その流れの中に過程のいろんな場所での参照カウントを示す println! 文が存在しています。 ファイル名: src/main.rs # use List::{Cons, Nil};\n# use std::rc::Rc;\n# use std::cell::RefCell;\n# #[derive(Debug)]\n# enum List {\n# Cons(i32, RefCell>),\n# Nil,\n# }\n#\n# impl List {\n# fn tail(&self) -> Option<&RefCell>> {\n# match *self {\n# Cons(_, ref item) => Some(item),\n# Nil => None,\n# }\n# }\n# }\n#\nfn main() { let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil)))); // aの最初の参照カウント = {} println!(\"a initial rc count = {}\", Rc::strong_count(&a)); // aの次の要素は = {:?} println!(\"a next item = {:?}\", a.tail()); let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a)))); // b作成後のaの参照カウント = {} println!(\"a rc count after b creation = {}\", Rc::strong_count(&a)); // bの最初の参照カウント = {} println!(\"b initial rc count = {}\", Rc::strong_count(&b)); // bの次の要素 = {:?} println!(\"b next item = {:?}\", b.tail()); if let Some(link) = a.tail() { *link.borrow_mut() = Rc::clone(&b); } // aを変更後のbの参照カウント = {} println!(\"b rc count after changing a = {}\", Rc::strong_count(&b)); // aを変更後のaの参照カウント = {} println!(\"a rc count after changing a = {}\", Rc::strong_count(&a)); // Uncomment the next line to see that we have a cycle; // it will overflow the stack // 次の行のコメントを外して循環していると確認してください; スタックオーバーフローします // println!(\"a next item = {:?}\", a.tail()); // aの次の要素 = {:?}\n} リスト15-26: 2つの List 値がお互いを指して循環参照する 最初のリストが 5, Nil の List 値を保持する Rc インスタンスを変数 a に生成します。 そして、値10と a のリストを指す別の List 値を保持する Rc インスタンスを変数 b に生成します。 a が Nil ではなく b を指すように変更して、循環させます。 tail メソッドを使用して、 a の RefCell> への参照を得ることで循環させて、この参照は変数 link に配置します。 それから RefCell> の borrow_mut メソッドを使用して中の値を Nil 値を持つ Rc から、 b の Rc に変更します。 最後の println! を今だけコメントアウトしたまま、このコードを実行すると、こんな出力が得られます: a initial rc count = 1\na next item = Some(RefCell { value: Nil })\na rc count after b creation = 2\nb initial rc count = 1\nb next item = Some(RefCell { value: Cons(5, RefCell { value: Nil }) })\nb rc count after changing a = 2\na rc count after changing a = 2 a のリストを b を指すように変更した後の a と b の Rc インスタンスの参照カウントは2です。 main の終端で、コンパイラはまず b をドロップしようとし、 a と b の各 Rc インスタンスのカウントを1減らします。 しかしながら、それでも a は b にあった Rc を参照しているので、その Rc のカウントは0ではなく1になり、 その Rc がヒープに確保していたメモリはドロップされません。メモリはただ、カウント1のままそこに永遠に居座るのです。 この循環参照を可視化するために、図15-4に図式を作成しました: 図15-4: お互いを指すリスト a と b の循環参照 最後の println! のコメントを外してプログラムを実行したら、 a が b を指して、 b が a を指してと、 スタックがオーバーフローするまでコンパイラはこの循環を出力しようとするでしょう。 この場合、循環参照を作る直後にプログラムは終了します。この循環の結果は、それほど悲壮なものではありません。しかしながら、 より複雑なプログラムが多くのメモリを循環で確保し長い間その状態を保ったら、プログラムは必要以上のメモリを使用し、 使用可能なメモリを枯渇させてシステムを参らせてしまう可能性があります。 循環参照は簡単にできることではありませんが、不可能というわけでもありません。 Rc 値を含む RefCell 値があるなどの内部可変性と参照カウントのある型がネストして組み合わさっていたら、 循環していないことを保証しなければなりません; コンパイラがそれを捕捉することを信頼できないのです。 循環参照をするのは、自動テストやコードレビューなどの他のソフトウェア開発手段を使用して最小化すべきプログラム上のロジックバグでしょう。 循環参照を回避する別の解決策は、ある参照は所有権を表現して他の参照はしないというようにデータ構造を再構成することです。 結果として、所有権のある関係と所有権のない関係からなる循環ができ、所有権のある関係だけが、値がドロップされうるかどうかに影響します。 リスト15-25では、常に Cons 列挙子にリストを所有してほしいので、データ構造を再構成することはできません。 親ノードと子ノードからなるグラフを使った例に目を向けて、どんな時に所有権のない関係が循環参照を回避するのに適切な方法になるか確認しましょう。","breadcrumbs":"スマートポインタ » 循環参照させる","id":"ch15-06-reference-cycles.html#a循環参照させる","title":"循環参照させる"},"ch15-06-reference-cycles.html#a循環参照はメモリをリークすることもある":{"body":"Rustのメモリ安全保証により誤って絶対に片付けられることのないメモリ( メモリリーク として知られています)を生成してしまいにくくなりますが、 不可能にはなりません。コンパイル時にデータ競合を防ぐのと同じようにメモリリークを完全に回避することは、 Rustの保証の一つではなく、メモリリークはRustにおいてはメモリ安全であることを意味します。 Rustでは、 Rc と RefCell を使用してメモリリークを許可するとわかります: 要素がお互いに循環して参照する参照を生成することも可能ということです。循環の各要素の参照カウントが絶対に0にならないので、 これはメモリリークを起こし、値は絶対にドロップされません。","breadcrumbs":"スマートポインタ » 循環参照は、メモリをリークすることもある","id":"ch15-06-reference-cycles.html#a循環参照はメモリをリークすることもある","title":"循環参照は、メモリをリークすることもある"},"ch15-06-reference-cycles.html#a循環参照を回避する--rct-を-weakt-に変換する":{"body":"ここまで、 Rc::clone を呼び出すと Rc インスタンスの strong_count が増えることと、 strong_count が0になった時に Rc インスタンスは片付けられることをデモしてきました。 Rc::downgrade を呼び出し、 Rc への参照を渡すことで、 Rc インスタンス内部の値への 弱い参照 (weak reference)を作ることもできます。 Rc::downgrade を呼び出すと、型 Weak のスマートポインタが得られます。 Rc インスタンスの strong_count を1増やす代わりに、 Rc::downgrade を呼び出すと、 weak_count が1増えます。 strong_count 同様、 Rc 型は weak_count を使用して、幾つの Weak 参照が存在しているかを追跡します。 違いは、 Rc が片付けられるのに、 weak_count が0である必要はないということです。 強い参照は、 Rc インスタンスの所有権を共有する方法です。弱い参照は、所有権関係を表現しません。 ひとたび、関係する値の強い参照カウントが0になれば、弱い参照が関わる循環はなんでも破壊されるので、 循環参照にはなりません。 Weak が参照する値はドロップされてしまっている可能性があるので、 Weak が指す値に何かをするには、 値がまだ存在することを確認しなければなりません。 Weak の upgrade メソッドを呼び出すことでこれをしてください。 このメソッドは Option> を返します。 Rc 値がまだドロップされていなければ、 Some の結果が、 Rc 値がドロップ済みなら、 None の結果が得られます。 upgrade が Option を返すので、 コンパイラは、 Some ケースと None ケースが扱われていることを確かめてくれ、無効なポインタは存在しません。 例として、要素が次の要素を知っているだけのリストを使うのではなく、要素が子要素 と 親要素を知っている木を作りましょう。 木データ構造を作る: 子ノードのある Node 手始めに子ノードを知っているノードのある木を構成します。独自の i32 値と子供の Node 値への参照を抱える Node という構造体を作ります: ファイル名: src/main.rs use std::rc::Rc;\nuse std::cell::RefCell; #[derive(Debug)]\nstruct Node { value: i32, children: RefCell>>,\n} Node に子供を所有してほしく、木の各 Node に直接アクセスできるよう、その所有権を変数と共有したいです。 こうするために、 Vec 要素を型 Rc の値になるよう定義しています。どのノードが他のノードの子供になるかも変更したいので、 Vec> の周りの children を RefCell にしています。 次にこの構造体定義を使って値3と子供なしの leaf という1つの Node インスタンスと、 値5と leaf を子要素の一つとして持つ branch という別のインスタンスを作成します。 リスト15-27のようにですね: ファイル名: src/main.rs # use std::rc::Rc;\n# use std::cell::RefCell;\n#\n# #[derive(Debug)]\n# struct Node {\n# value: i32,\n# children: RefCell>>,\n# }\n#\nfn main() { let leaf = Rc::new(Node { value: 3, children: RefCell::new(vec![]), }); let branch = Rc::new(Node { value: 5, children: RefCell::new(vec![Rc::clone(&leaf)]), });\n} リスト15-27: 子供なしの leaf ノードと leaf を子要素に持つ branch ノードを作る leaf の Rc をクローンし、 branch に格納しているので、 leaf の Node は leaf と branch という2つの所有者を持つことになります。 branch.children を通して branch から leaf へ辿ることはできるものの、 leaf から branch へ辿る方法はありません。 理由は、 leaf には branch への参照がなく、関係していることを知らないからです。 leaf に branch が親であることを知ってほしいです。 次はそれを行います。 子供から親に参照を追加する 子供に親の存在を気付かせるために、 Node 構造体定義に parent フィールドを追加する必要があります。 parent の型を決める際に困ったことになります。 Rc を含むことができないのはわかります。 そうしたら、 leaf.parent が branch を指し、 branch.children が leaf を指して循環参照になり、 strong_count 値が絶対に0にならなくなってしまうからです。 この関係を別の方法で捉えると、親ノードは子供を所有すべきです: 親ノードがドロップされたら、 子ノードもドロップされるべきなのです。ですが、子供は親を所有するべきではありません: 子ノードをドロップしても、親はまだ存在するべきです。弱い参照を使う場面ですね! 従って、 Rc の代わりに parent の型を Weak を使ったもの、具体的には RefCell> にします。 さあ、 Node 構造体定義はこんな見た目になりました: ファイル名: src/main.rs use std::rc::{Rc, Weak};\nuse std::cell::RefCell; #[derive(Debug)]\nstruct Node { value: i32, parent: RefCell>, children: RefCell>>,\n} ノードは親ノードを参照できるものの、所有はしないでしょう。リスト15-28で、 leaf ノードが親の branch を参照できるよう、この新しい定義を使用するように main を更新します: ファイル名: src/main.rs # use std::rc::{Rc, Weak};\n# use std::cell::RefCell;\n#\n# #[derive(Debug)]\n# struct Node {\n# value: i32,\n# parent: RefCell>,\n# children: RefCell>>,\n# }\n#\nfn main() { let leaf = Rc::new(Node { value: 3, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![]), }); // leafの親 = {:?} println!(\"leaf parent = {:?}\", leaf.parent.borrow().upgrade()); let branch = Rc::new(Node { value: 5, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![Rc::clone(&leaf)]), }); *leaf.parent.borrow_mut() = Rc::downgrade(&branch); println!(\"leaf parent = {:?}\", leaf.parent.borrow().upgrade());\n} リスト15-28: 親ノードの branch への弱い参照がある leaf ノード leaf ノードを作成することは、 parent フィールドの例外を除いてリスト15-27での leaf ノードの作成法の見た目に似ています: leaf は親なしで始まるので、新しく空の Weak 参照インスタンスを作ります。 この時点で upgrade メソッドを使用して leaf の親への参照を得ようとすると、 None 値になります。 このことは、最初の println! 文の出力でわかります: leaf parent = None branch ノードを作る際、 branch には親ノードがないので、こちらも parent フィールドには新しい Weak 参照が入ります。 それでも、 leaf は branch の子供になっています。一旦 branch に Node インスタンスができたら、 leaf を変更して親への Weak 参照を与えることができます。 leaf の parent フィールドには、 RefCell> の borrow_mut メソッドを使用して、それから Rc::downgrade 関数を使用して、 branch の Rc から branch への Weak 参照を作ります。 再度 leaf の親を出力すると、今度は branch を保持する Some 列挙子が得られます: これで leaf が親にアクセスできるようになったのです! leaf を出力すると、リスト15-26で起こっていたような最終的にスタックオーバーフローに行き着く循環を避けることもできます; Weak 参照は、 (Weak) と出力されます: leaf parent = Some(Node { value: 5, parent: RefCell { value: (Weak) },\nchildren: RefCell { value: [Node { value: 3, parent: RefCell { value: (Weak) },\nchildren: RefCell { value: [] } }] } }) 無限の出力が欠けているということは、このコードは循環参照しないことを示唆します。 このことは、 Rc::strong_count と Rc::weak_count を呼び出すことで得られる値を見てもわかります。 strong_count と weak_count への変更を可視化する 新しい内部スコープを作り、 branch の作成をそのスコープに移動することで、 Rc インスタンスの strong_count と weak_count 値がどう変化するかを眺めましょう。 そうすることで、 branch が作成され、それからスコープを抜けてドロップされる時に起こることが確認できます。 変更は、リスト15-29に示してあります: ファイル名: src/main.rs # use std::rc::{Rc, Weak};\n# use std::cell::RefCell;\n#\n# #[derive(Debug)]\n# struct Node {\n# value: i32,\n# parent: RefCell>,\n# children: RefCell>>,\n# }\n#\nfn main() { let leaf = Rc::new(Node { value: 3, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![]), }); println!( // leafのstrong_count = {}, weak_count = {} \"leaf strong = {}, weak = {}\", Rc::strong_count(&leaf), Rc::weak_count(&leaf), ); { let branch = Rc::new(Node { value: 5, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![Rc::clone(&leaf)]), }); *leaf.parent.borrow_mut() = Rc::downgrade(&branch); println!( // branchのstrong_count = {}, weak_count = {} \"branch strong = {}, weak = {}\", Rc::strong_count(&branch), Rc::weak_count(&branch), ); println!( \"leaf strong = {}, weak = {}\", Rc::strong_count(&leaf), Rc::weak_count(&leaf), ); } println!(\"leaf parent = {:?}\", leaf.parent.borrow().upgrade()); println!( \"leaf strong = {}, weak = {}\", Rc::strong_count(&leaf), Rc::weak_count(&leaf), );\n} リスト15-29: 内側のスコープで branch を作成し、強弱参照カウントを調査する leaf 作成後、その Rc の強カウントは1、弱カウントは0になります。内側のスコープで branch を作成し、 leaf に紐付け、この時点でカウントを出力すると、 branch の Rc の強カウントは1、 弱カウントも1になります( leaf.parent が Weak で branch を指しているため)。 leaf のカウントを出力すると、強カウントが2になっていることがわかります。 branch が今は、 branch.children に格納された leaf の Rc のクローンを持っているからですが、 それでも弱カウントは0でしょう。 内側のスコープが終わると、 branch はスコープを抜け、 Rc の強カウントは0に減るので、 この Node はドロップされます。 leaf.parent からの弱カウント1は、 Node がドロップされるか否かには関係ないので、 メモリリークはしないのです! このスコープの終端以後に leaf の親にアクセスしようとしたら、再び None が得られます。 プログラムの終端で leaf の Rc の強カウントは1、弱カウントは0です。 変数 leaf が今では Rc への唯一の参照に再度なったからです。 カウントや値のドロップを管理するロジックは全て、 Rc や Weak とその Drop トレイトの実装に組み込まれています。 Node の定義で子供から親への関係は Weak 参照になるべきと指定することで、 循環参照やメモリリークを引き起こさずに親ノードに子ノードを参照させたり、その逆を行うことができます。","breadcrumbs":"スマートポインタ » 循環参照を回避する: Rc を Weak に変換する","id":"ch15-06-reference-cycles.html#a循環参照を回避する--rct-を-weakt-に変換する","title":"循環参照を回避する: Rc を Weak に変換する"},"ch16-00-concurrency.html#a恐れるな並行性":{"body":"並行性を安全かつ効率的に扱うことは、Rustの別の主な目標です。 並行プログラミング は、プログラムの異なる部分が独立して実行することであり、 並列プログラミング はプログラムの異なる部分が同時に実行することですが、多くのコンピュータが複数のプロセッサの利点を生かすようになるにつれ、 重要度を増しています。歴史的に、これらの文脈で行うプログラミングは困難で、エラーが起きやすいものでした: Rustはこれを変えると願っています。 当初、Rustチームは、メモリ安全性を保証することと、並行性問題を回避することは、 異なる方法で解決すべき別々の課題だと考えていました。時間とともに、チームは、所有権と型システムは、 メモリ安全性 と 並行性問題を管理する役に立つ一連の強力な道具であることを発見しました。 所有権と型チェックを活用することで、多くの並行性エラーは、実行時エラーではなくコンパイル時エラーになります。 故に、実行時に並行性のバグが起きた状況と全く同じ状況を再現しようと時間を浪費させるよりも、 不正なコードはコンパイルを拒み、問題を説明するエラーを提示するでしょう。結果として、 プロダクトになった後でなく、作業中にコードを修正できます。 Rustのこの方向性を 恐れるな!並行性 とニックネーム付けしました。これにより、潜在的なバグがなく、かつ、 新しいバグを導入することなく簡単にリファクタリングできるコードを書くことができます。 注釈: 簡潔性のため、並行または並列と述べることで正確を期するのではなく、 多くの問題を 並行 と割り切ってしまいます。この本がもし 並行性あるいは並列性 に関した本ならば、 詳述していたでしょう。この章に対しては、 並行 を使ったら、 脳内で 並行または並列 と置き換えてください。 多くの言語は、自分が提供する並行性問題を扱う解決策について独断的です。例えば、Erlangには、 メッセージ受け渡しの並行性に関する素晴らしい機能がありますが、スレッド間で状態を共有することに関しては、 曖昧な方法しかありません。可能な解決策の一部のみをサポートすることは、高級言語にとっては合理的な施策です。 なぜなら、高級言語は一部の制御を失う代わりに抽象化することから恩恵を受けるからです。ところが、 低級言語は、どんな場面でも最高のパフォーマンスで解決策を提供すると想定され、ハードウェアに関してほとんど抽象化はしません。 そのため、Rustは、自分の状況と必要性に適した方法が何であれ、問題をモデル化するためのいろんな道具を備えています。 こちらが、この章で講義する話題です: スレッドを生成して、複数のコードを同時に走らせる方法 チャンネルがスレッド間でメッセージを送る メッセージ受け渡し 並行性 複数のスレッドが何らかのデータにアクセスする 状態共有 並行性 標準ライブラリが提供する型だけでなく、ユーザが定義した型に対してもRustの並行性の安全保証を拡張する Sync と Send トレイト","breadcrumbs":"恐れるな!並行性","id":"ch16-00-concurrency.html#a恐れるな並行性","title":"恐れるな!並行性"},"ch16-01-threads.html#aスレッドで-move-クロージャを使用する":{"body":"move クロージャは、 thread::spawn とともによく使用されます。 あるスレッドのデータを別のスレッドで使用できるようになるからです。 第13章で、クロージャの引数リストの前に move キーワードを使用して、 クロージャに環境で使用している値の所有権を強制的に奪わせることができると述べました。 このテクニックは、あるスレッドから別のスレッドに値の所有権を移すために新しいスレッドを生成する際に特に有用です。 リスト16-1において、 thread::spawn に渡したクロージャには引数がなかったことに注目してください: 立ち上げたスレッドのコードでメインスレッドからのデータは何も使用していないのです。 立ち上げたスレッドでメインスレッドのデータを使用するには、立ち上げるスレッドのクロージャは、 必要な値をキャプチャしなければなりません。リスト16-3は、メインスレッドでベクタを生成し、 立ち上げたスレッドで使用する試みを示しています。しかしながら、すぐにわかるように、これはまだ動きません: ファイル名: src/main.rs use std::thread; fn main() { let v = vec![1, 2, 3]; let handle = thread::spawn(|| { // こちらがベクタ: {:?} println!(\"Here's a vector: {:?}\", v); }); handle.join().unwrap();\n} リスト16-3: 別のスレッドでメインスレッドが生成したベクタを使用しようとする クロージャは v を使用しているので、 v をキャプチャし、クロージャの環境の一部にしています。 thread::spawn はこのクロージャを新しいスレッドで走らせるので、 その新しいスレッド内で v にアクセスできるはずです。しかし、このコードをコンパイルすると、 以下のようなエラーが出ます: error[E0373]: closure may outlive the current function, but it borrows `v`,\nwhich is owned by the current function\n(エラー: クロージャは現在の関数よりも長生きするかもしれませんが、現在の関数が所有している\n`v`を借用しています) --> src/main.rs:6:32 |\n6 | let handle = thread::spawn(|| { | ^^ may outlive borrowed value `v`\n7 | println!(\"Here's a vector: {:?}\", v); | - `v` is borrowed here |\nhelp: to force the closure to take ownership of `v` (and any other referenced\nvariables), use the `move` keyword\n(助言: `v`(や他の参照されている変数)の所有権をクロージャに奪わせるには、`move`キーワードを使用してください) |\n6 | let handle = thread::spawn(move || { | ^^^^^^^ Rustは v のキャプチャ方法を 推論 し、 println! は v への参照のみを必要とするので、クロージャは、 v を借用しようとします。ですが、問題があります: コンパイラには、立ち上げたスレッドがどのくらいの期間走るのかわからないので、 v への参照が常に有効であるか把握できないのです。 リスト16-4は、 v への参照がより有効でなさそうな筋書きです: ファイル名: src/main.rs use std::thread; fn main() { let v = vec![1, 2, 3]; let handle = thread::spawn(|| { println!(\"Here's a vector: {:?}\", v); }); // いや〜! drop(v); // oh no! handle.join().unwrap();\n} リスト16-4: v をドロップするメインスレッドから v への参照をキャプチャしようとするクロージャを伴うスレッド このコードを実行できてしまうなら、立ち上げたスレッドはまったく実行されることなく即座にバックグラウンドに置かれる可能性があります。 立ち上げたスレッドは内部に v への参照を保持していますが、メインスレッドは、第15章で議論した drop 関数を使用して、 即座に v をドロップしています。そして、立ち上げたスレッドが実行を開始する時には、 v はもう有効ではなく、 参照も不正になるのです。あちゃー! リスト16-3のコンパイルエラーを修正するには、エラーメッセージのアドバイスを活用できます: help: to force the closure to take ownership of `v` (and any other referenced\nvariables), use the `move` keyword |\n6 | let handle = thread::spawn(move || { | ^^^^^^^ クロージャの前に move キーワードを付することで、コンパイラに値を借用すべきと推論させるのではなく、 クロージャに使用している値の所有権を強制的に奪わせます。リスト16-5に示したリスト16-3に対する変更は、 コンパイルでき、意図通りに動きます: ファイル名: src/main.rs use std::thread; fn main() { let v = vec![1, 2, 3]; let handle = thread::spawn(move || { println!(\"Here's a vector: {:?}\", v); }); handle.join().unwrap();\n} リスト16-5: move キーワードを使用してクロージャに使用している値の所有権を強制的に奪わせる move クロージャを使用していたら、メインスレッドが drop を呼び出すリスト16-4のコードはどうなるのでしょうか? move で解決するのでしょうか?残念ながら、違います; リスト16-4が試みていることは別の理由によりできないので、 違うエラーが出ます。クロージャに move を付与したら、 v をクロージャの環境にムーブするので、 最早メインスレッドで drop を呼び出すことは叶わなくなるでしょう。代わりにこのようなコンパイルエラーが出るでしょう: error[E0382]: use of moved value: `v`\n(エラー: ムーブされた値の使用: `v`) --> src/main.rs:10:10 |\n6 | let handle = thread::spawn(move || { | ------- value moved (into closure) here\n...\n10 | drop(v); // oh no! | ^ value used here after move | = note: move occurs because `v` has type `std::vec::Vec`, which does not implement the `Copy` trait (注釈: `v`の型が`std::vec::Vec`のためムーブが起きました。この型は、`Copy`トレイトを実装していません) 再三Rustの所有権規則が救ってくれました!リスト16-3のコードはエラーになりました。 コンパイラが一時的に保守的になり、スレッドに対して v を借用しただけだったからで、 これは、メインスレッドは理論上、立ち上げたスレッドの参照を不正化する可能性があることを意味します。 v の所有権を立ち上げたスレッドに移動するとコンパイラに指示することで、 メインスレッドはもう v を使用しないとコンパイラに保証しているのです。リスト16-4も同様に変更したら、 メインスレッドで v を使用しようとする際に所有権の規則に違反することになります。 move キーワードにより、Rustの保守的な借用のデフォルトが上書きされるのです; 所有権の規則を侵害させてくれないのです。 スレッドとスレッドAPIの基礎知識を得たので、スレッドで できる ことを見ていきましょう。","breadcrumbs":"恐れるな!並行性 » スレッドで move クロージャを使用する","id":"ch16-01-threads.html#aスレッドで-move-クロージャを使用する","title":"スレッドで move クロージャを使用する"},"ch16-01-threads.html#aスレッドを使用してコードを同時に走らせる":{"body":"多くの現代のOSでは、実行中のプログラムのコードは プロセス で走り、OSは同時に複数のプロセスを管理します。 自分のプログラム内で、独立した部分を同時に実行できます。これらの独立した部分を走らせる機能を スレッド と呼びます。 プログラム内の計算を複数のスレッドに分けると、パフォーマンスが改善します。プログラムが同時に複数の作業をするからですが、 複雑度も増します。スレッドは同時に走らせることができるので、異なるスレッドのコードが走る順番に関して、 本来的に保証はありません。これは例えば以下のような問題を招きます: スレッドがデータやリソースに矛盾した順番でアクセスする競合状態 2つのスレッドがお互いにもう一方が持っているリソースを使用し終わるのを待ち、両者が継続するのを防ぐデッドロック 特定の状況でのみ起き、確実な再現や修正が困難なバグ Rustは、スレッドを使用する際の悪影響を軽減しようとしていますが、それでも、マルチスレッドの文脈でのプログラミングでは、 注意深い思考とシングルスレッドで走るプログラムとは異なるコード構造が必要です。 プログラミング言語によってスレッドはいくつかの方法で実装されています。多くのOSで、新規スレッドを生成するAPIが提供されています。 言語がOSのAPIを呼び出してスレッドを生成するこのモデルを時に 1:1 と呼び、1つのOSスレッドに対して1つの言語スレッドを意味します。 多くのプログラミング言語がスレッドの独自の特別な実装を提供しています。プログラミング言語が提供するスレッドは、 グリーン スレッドとして知られ、このグリーンスレッドを使用する言語は、それを異なる数のOSスレッドの文脈で実行します。 このため、グリーンスレッドのモデルは M:N モデルと呼ばれます: M 個のグリーンスレッドに対して、 N 個のOSスレッドがあり、 M と N は必ずしも同じ数字ではありません。 各モデルには、それだけの利点と代償があり、Rustにとって最も重要な代償は、ランタイムのサポートです。 ランタイム は、混乱しやすい用語で文脈によって意味も変わります。 この文脈での ランタイム とは、言語によって全てのバイナリに含まれるコードのことを意味します。 言語によってこのコードの大小は決まりますが、非アセンブリ言語は全てある量の実行時コードを含みます。 そのため、口語的に誰かが「ノーランタイム」と言ったら、「小さいランタイム」のことを意味することがしばしばあります。 ランタイムが小さいと機能も少ないですが、バイナリのサイズも小さくなるという利点があり、 その言語を他の言語とより多くの文脈で組み合わせることが容易になります。多くの言語では、 より多くの機能と引き換えにランタイムのサイズが膨れ上がるのは、受け入れられることですが、 Rustにはほとんどゼロのランタイムが必要でパフォーマンスを維持するためにCコードを呼び出せることを妥協できないのです。 M:Nのグリーンスレッドモデルは、スレッドを管理するのにより大きな言語ランタイムが必要です。よって、 Rustの標準ライブラリは、1:1スレッドの実装のみを提供しています。Rustはそのような低級言語なので、 例えば、むしろどのスレッドがいつ走るかのより詳細な制御や、より低コストの文脈切り替えなどの一面をオーバーヘッドと引き換えるなら、 M:Nスレッドの実装をしたクレートもあります。 今やRustにおけるスレッドを定義したので、標準ライブラリで提供されているスレッド関連のAPIの使用法を探究しましょう。","breadcrumbs":"恐れるな!並行性 » スレッドを使用してコードを同時に走らせる","id":"ch16-01-threads.html#aスレッドを使用してコードを同時に走らせる","title":"スレッドを使用してコードを同時に走らせる"},"ch16-01-threads.html#join-ハンドルで全スレッドの終了を待つ":{"body":"リスト16-1のコードは、メインスレッドが終了するためにほとんどの場合、立ち上げたスレッドがすべて実行されないだけでなく、 立ち上げたスレッドが実行されるかどうかも保証できません。原因は、スレッドの実行順に保証がないからです。 thread::spawn の戻り値を変数に保存することで、立ち上げたスレッドが実行されなかったり、 完全には実行されなかったりする問題を修正することができます。 thread:spawn の戻り値の型は JoinHandle です。 JoinHandle は、その join メソッドを呼び出したときにスレッドの終了を待つ所有された値です。 リスト16-2は、リスト16-1で生成したスレッドの JoinHandle を使用し、 join を呼び出して、 main が終了する前に、立ち上げたスレッドが確実に完了する方法を示しています: ファイル名: src/main.rs use std::thread;\nuse std::time::Duration; fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!(\"hi number {} from the spawned thread!\", i); thread::sleep(Duration::from_millis(1)); } }); for i in 1..5 { println!(\"hi number {} from the main thread!\", i); thread::sleep(Duration::from_millis(1)); } handle.join().unwrap();\n} リスト16-2: thread::spawn の JoinHandle を保存してスレッドが完了するのを保証する ハンドルに対して join を呼び出すと、ハンドルが表すスレッドが終了するまで現在実行中のスレッドをブロックします。 スレッドを ブロック するとは、そのスレッドが動いたり、終了したりすることを防ぐことです。 join の呼び出しをメインスレッドの for ループの後に配置したので、リスト16-2を実行すると、 以下のように出力されるはずです: hi number 1 from the main thread!\nhi number 2 from the main thread!\nhi number 1 from the spawned thread!\nhi number 3 from the main thread!\nhi number 2 from the spawned thread!\nhi number 4 from the main thread!\nhi number 3 from the spawned thread!\nhi number 4 from the spawned thread!\nhi number 5 from the spawned thread!\nhi number 6 from the spawned thread!\nhi number 7 from the spawned thread!\nhi number 8 from the spawned thread!\nhi number 9 from the spawned thread! 2つのスレッドが代わる代わる実行されていますが、 handle.join() 呼び出しのためにメインスレッドは待機し、 立ち上げたスレッドが終了するまで終わりません。 ですが、代わりに handle.join() を for ループの前に移動したらどうなるのか確認しましょう。こんな感じに: ファイル名: src/main.rs use std::thread;\nuse std::time::Duration; fn main() { let handle = thread::spawn(|| { for i in 1..10 { println!(\"hi number {} from the spawned thread!\", i); thread::sleep(Duration::from_millis(1)); } }); handle.join().unwrap(); for i in 1..5 { println!(\"hi number {} from the main thread!\", i); thread::sleep(Duration::from_millis(1)); }\n} メインスレッドは、立ち上げたスレッドが終了するまで待ち、それから for ループを実行するので、 以下のように出力はもう混ざらないでしょう: hi number 1 from the spawned thread!\nhi number 2 from the spawned thread!\nhi number 3 from the spawned thread!\nhi number 4 from the spawned thread!\nhi number 5 from the spawned thread!\nhi number 6 from the spawned thread!\nhi number 7 from the spawned thread!\nhi number 8 from the spawned thread!\nhi number 9 from the spawned thread!\nhi number 1 from the main thread!\nhi number 2 from the main thread!\nhi number 3 from the main thread!\nhi number 4 from the main thread! どこで join を呼ぶかといったほんの些細なことが、スレッドが同時に走るかどうかに影響することもあります。","breadcrumbs":"恐れるな!並行性 » join ハンドルで全スレッドの終了を待つ","id":"ch16-01-threads.html#join-ハンドルで全スレッドの終了を待つ","title":"join ハンドルで全スレッドの終了を待つ"},"ch16-01-threads.html#spawn-で新規スレッドを生成する":{"body":"新規スレッドを生成するには、 thread::spawn 関数を呼び出し、 新規スレッドで走らせたいコードを含むクロージャ(クロージャについては第13章で語りました)を渡します。 リスト16-1の例は、メインスレッドと新規スレッドからテキストを出力します: ファイル名: src/main.rs use std::thread;\nuse std::time::Duration; fn main() { thread::spawn(|| { for i in 1..10 { // やあ!立ち上げたスレッドから数字{}だよ! println!(\"hi number {} from the spawned thread!\", i); thread::sleep(Duration::from_millis(1)); } }); for i in 1..5 { // メインスレッドから数字{}だよ! println!(\"hi number {} from the main thread!\", i); thread::sleep(Duration::from_millis(1)); }\n} リスト16-1: メインスレッドが別のものを出力する間に新規スレッドを生成して何かを出力する この関数では、新しいスレッドは、実行が終わったかどうかにかかわらず、メインスレッドが終了したら停止することに注意してください。 このプログラムからの出力は毎回少々異なる可能性がありますが、だいたい以下のような感じでしょう: hi number 1 from the main thread!\nhi number 1 from the spawned thread!\nhi number 2 from the main thread!\nhi number 2 from the spawned thread!\nhi number 3 from the main thread!\nhi number 3 from the spawned thread!\nhi number 4 from the main thread!\nhi number 4 from the spawned thread!\nhi number 5 from the spawned thread! thread::sleep を呼び出すと、少々の間、スレッドの実行を止め、違うスレッドを走らせることができます。 スレッドはおそらく切り替わるでしょうが、保証はありません: OSがスレッドのスケジュールを行う方法によります。 この実行では、コード上では立ち上げられたスレッドのprint文が先に現れているのに、メインスレッドが先に出力しています。また、 立ち上げたスレッドには i が9になるまで出力するよう指示しているのに、メインスレッドが終了する前の5までしか到達していません。 このコードを実行してメインスレッドの出力しか目の当たりにできなかったり、オーバーラップがなければ、 範囲の値を増やしてOSがスレッド切り替えを行う機会を増やしてみてください。","breadcrumbs":"恐れるな!並行性 » spawn で新規スレッドを生成する","id":"ch16-01-threads.html#spawn-で新規スレッドを生成する","title":"spawn で新規スレッドを生成する"},"ch16-02-message-passing.html#aチャンネルと所有権の転送":{"body":"安全な並行コードを書く手助けをしてくれるので、所有権規則は、メッセージ送信で重要な役割を担っています。 並行プログラミングでエラーを回避することは、Rustプログラム全体で所有権について考える利点です。 実験をしてチャンネルと所有権がともに動いて、どう問題を回避するかをお見せしましょう: val 値を立ち上げたスレッドで、チャンネルに送った 後 に使用を試みます。 リスト16-9のコードのコンパイルを試みて、このコードが許容されない理由を確認してください: ファイル名: src/main.rs use std::thread;\nuse std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let val = String::from(\"hi\"); tx.send(val).unwrap(); // valは{} println!(\"val is {}\", val); }); let received = rx.recv().unwrap(); println!(\"Got: {}\", received);\n} リスト16-9: チャンネルに送信後に val の使用を試みる ここで、 tx.send 経由でチャンネルに送信後に val を出力しようとしています。これを許可するのは、悪い考えです: 一旦、値が他のスレッドに送信されたら、再度値を使用しようとする前にそのスレッドが変更したりドロップできてしまいます。 可能性として、その別のスレッドの変更により、矛盾していたり存在しないデータのせいでエラーが発生したり、 予期しない結果になるでしょう。ですが、リスト16-9のコードのコンパイルを試みると、Rustはエラーを返します: error[E0382]: use of moved value: `val` --> src/main.rs:10:31 |\n9 | tx.send(val).unwrap(); | --- value moved here\n10 | println!(\"val is {}\", val); | ^^^ value used here after move | = note: move occurs because `val` has type `std::string::String`, which does\nnot implement the `Copy` trait 並行性のミスがコンパイルエラーを招きました。 send 関数は引数の所有権を奪い、 値がムーブされると、受信側が所有権を得るのです。これにより、送信後に誤って再度値を使用するのを防いでくれます; 所有権システムが、万事問題ないことを確認してくれます。","breadcrumbs":"恐れるな!並行性 » チャンネルと所有権の転送","id":"ch16-02-message-passing.html#aチャンネルと所有権の転送","title":"チャンネルと所有権の転送"},"ch16-02-message-passing.html#aメッセージ受け渡しを使ってスレッド間でデータを転送する":{"body":"人気度を増してきている安全な並行性を保証する一つのアプローチが メッセージ受け渡し で、 スレッドやアクターがデータを含むメッセージを相互に送り合うことでやり取りします。 こちらが、 Go言語のドキュメンテーション のスローガンにある考えです: 「メモリを共有することでやり取りするな; 代わりにやり取りすることでメモリを共有しろ」 メッセージ送信並行性を達成するためにRustに存在する一つの主な道具は、 チャンネル で、 Rustの標準ライブラリが実装を提供しているプログラミング概念です。プログラミングのチャンネルは、 水の流れのように考えることができます。小川とか川ですね。アヒルのおもちゃやボートみたいなものを流れに置いたら、 水路の終端まで下流に流れていきます。 プログラミングにおけるチャンネルは、2分割できます: 転送機と受信機です。転送機はアヒルのおもちゃを川に置く上流になり、 受信機は、アヒルのおもちゃが行き着く下流になります。コードのある箇所が送信したいデータとともに転送機のメソッドを呼び出し、 別の部分がメッセージが到着していないか受信側を調べます。転送機と受信機のどちらかがドロップされると、 チャンネルは 閉じられた と言います。 ここで、1つのスレッドが値を生成し、それをチャンネルに送信し、別のスレッドがその値を受け取り、 出力するプログラムに取り掛かります。チャンネルを使用してスレッド間に単純な値を送り、 機能の説明を行います。一旦、そのテクニックに慣れてしまえば、チャンネルを使用してチャットシステムや、 多くのスレッドが計算の一部を担い、結果をまとめる1つのスレッドにその部分を送るようなシステムを実装できるでしょう。 まず、リスト16-6において、チャンネルを生成するものの、何もしません。 チャンネル越しにどんな型の値を送りたいのかコンパイラがわからないため、 これはまだコンパイルできないことに注意してください。 ファイル名: src/main.rs use std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel();\n# tx.send(()).unwrap();\n} リスト16-6: チャンネルを生成し、2つの部品を tx と rx に代入する mpsc::channel 関数で新しいチャンネルを生成しています; mpsc は multiple producer, single consumer を表しています。 簡潔に言えば、Rustの標準ライブラリがチャンネルを実装している方法は、1つのチャンネルが値を生成する複数の 送信 側と、 その値を消費するたった1つの 受信 側を持つことができるということを意味します。 複数の小川が互いに合わさって1つの大きな川になるところを想像してください: どの小川を通っても、送られたものは最終的に1つの川に行き着きます。今は、1つの生成器から始めますが、 この例が動作するようになったら、複数の生成器を追加します。 mpsc::channel 関数はタプルを返し、1つ目の要素は、送信側、2つ目の要素は受信側になります。 tx と rx という略称は、多くの分野で伝統的に 転送機 と 受信機 にそれぞれ使用されているので、 変数をそのように名付けて、各終端を示します。タプルを分配するパターンを伴う let 文を使用しています; let 文でパターンを使用することと分配については、第18章で議論しましょう。このように let 文を使うと、 mpsc::channel で返ってくるタプルの部品を抽出するのが便利になります。 立ち上げたスレッドがメインスレッドとやり取りするように、転送機を立ち上げたスレッドに移動し、 1文字列を送らせましょう。リスト16-7のようにですね。川の上流にアヒルのおもちゃを置いたり、 チャットのメッセージをあるスレッドから別のスレッドに送るみたいですね。 ファイル名: src/main.rs use std::thread;\nuse std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let val = String::from(\"hi\"); tx.send(val).unwrap(); });\n} リスト16-7: tx を立ち上げたスレッドに移動し、「やあ」を送る 今回も、 thread::spawn を使用して新しいスレッドを生成し、それから move を使用して、 立ち上げたスレッドが tx を所有するようにクロージャに tx をムーブしています。立ち上げたスレッドは、 メッセージをチャンネルを通して送信できるように、チャンネルの送信側を所有する必要があります。 転送側には、送信したい値を取る send メソッドがあります。 send メソッドは Result 型を返すので、 既に受信側がドロップされ、値を送信する場所がなければ、送信処理はエラーを返します。 この例では、エラーの場合には、パニックするように unwrap を呼び出しています。ですが、実際のアプリケーションでは、 ちゃんと扱うでしょう: 第9章に戻ってちゃんとしたエラー処理の方法を再確認してください。 リスト16-8において、メインスレッドのチャンネルの受信側から値を得ます。 アヒルのおもちゃを川の終端で水から回収したり、チャットメッセージを取得するみたいですね。 ファイル名: src/main.rs use std::thread;\nuse std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let val = String::from(\"hi\"); tx.send(val).unwrap(); }); let received = rx.recv().unwrap(); // 値は{}です println!(\"Got: {}\", received);\n} リスト16-8: 「やあ」の値をメインスレッドで受け取り、出力する チャンネルの受信側には有用なメソッドが2つあります: recv と try_recv です。 receive の省略形である recv を使っています。これは、メインスレッドの実行をブロックし、 値がチャンネルを流れてくるまで待機します。一旦値が送信されたら、 recv はそれを Result に含んで返します。 チャンネルの送信側が閉じたら、 recv はエラーを返し、もう値は来ないと通知します。 try_recv メソッドはブロックせず、代わりに即座に Result を返します: メッセージがあったら、それを含む Ok 値、今回は何もメッセージがなければ、 Err 値です。 メッセージを待つ間にこのスレッドにすることが他にあれば、 try_recv は有用です: try_recv を頻繁に呼び出し、メッセージがあったら処理し、それ以外の場合は、 再度チェックするまでちょっとの間、他の作業をするループを書くことができるでしょう。 この例では、簡潔性のために recv を使用しました; メッセージを待つこと以外にメインスレッドがすべき作業はないので、 メインスレッドをブロックするのは適切です。 リスト16-8のコードを実行したら、メインスレッドから値が出力されるところを目撃するでしょう: Got: hi 完璧です!","breadcrumbs":"恐れるな!並行性 » メッセージ受け渡しを使ってスレッド間でデータを転送する","id":"ch16-02-message-passing.html#aメッセージ受け渡しを使ってスレッド間でデータを転送する","title":"メッセージ受け渡しを使ってスレッド間でデータを転送する"},"ch16-02-message-passing.html#a複数の値を送信し受信側が待機するのを確かめる":{"body":"リスト16-8のコードはコンパイルでき、動きましたが、2つの個別のスレッドがお互いにチャンネル越しに会話していることは、 明瞭に示されませんでした。リスト16-10において、リスト16-8のコードが並行に動いていることを証明する変更を行いました: 立ち上げたスレッドは、複数のメッセージを送信し、各メッセージ間で、1秒待機します。 ファイル名: src/main.rs use std::thread;\nuse std::sync::mpsc;\nuse std::time::Duration; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { // スレッドからやあ(hi from the thread) let vals = vec![ String::from(\"hi\"), String::from(\"from\"), String::from(\"the\"), String::from(\"thread\"), ]; for val in vals { tx.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); } }); for received in rx { println!(\"Got: {}\", received); }\n} リスト16-10: 複数のメッセージを送信し、メッセージ間で停止する 今回は、メインスレッドに送信したい文字列のベクタを立ち上げたスレッドが持っています。 それらを繰り返し、各々個別に送信し、 Duration の値1秒とともに thread::sleep 関数を呼び出すことで、 メッセージ間で停止します。 メインスレッドにおいて、最早 recv 関数を明示的に呼んではいません: 代わりに、 rx をイテレータとして扱っています。受信した値それぞれを出力します。 チャンネルが閉じられると、繰り返しも終わります。 リスト16-10のコードを走らせると、各行の間に1秒の待機をしつつ、以下のような出力を目の当たりにするはずです: Got: hi\nGot: from\nGot: the\nGot: thread メインスレッドの for ループには停止したり、遅れせたりするコードは何もないので、 メインスレッドが立ち上げたスレッドから値を受け取るのを待機していることがわかります。","breadcrumbs":"恐れるな!並行性 » 複数の値を送信し、受信側が待機するのを確かめる","id":"ch16-02-message-passing.html#a複数の値を送信し受信側が待機するのを確かめる","title":"複数の値を送信し、受信側が待機するのを確かめる"},"ch16-02-message-passing.html#a転送機をクローンして複数の生成器を作成する":{"body":"mpsc は、 mutiple producer, single consumer の頭字語であると前述しました。 mpsc を使い、リスト16-10のコードを拡張して、全ての値を同じ受信機に送信する複数のスレッドを生成しましょう。 チャンネルの転送の片割れをクローンすることでそうすることができます。リスト16-11のようにですね: ファイル名: src/main.rs # use std::thread;\n# use std::sync::mpsc;\n# use std::time::Duration;\n#\n# fn main() {\n// --snip-- let (tx, rx) = mpsc::channel(); let tx1 = mpsc::Sender::clone(&tx);\nthread::spawn(move || { let vals = vec![ String::from(\"hi\"), String::from(\"from\"), String::from(\"the\"), String::from(\"thread\"), ]; for val in vals { tx1.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); }\n}); thread::spawn(move || { // 君のためにもっとメッセージを(more messages for you) let vals = vec![ String::from(\"more\"), String::from(\"messages\"), String::from(\"for\"), String::from(\"you\"), ]; for val in vals { tx.send(val).unwrap(); thread::sleep(Duration::from_secs(1)); }\n}); for received in rx { println!(\"Got: {}\", received);\n} // --snip--\n# } リスト16-11: 複数の生成器から複数のメッセージを送信する 今回、最初のスレッドを立ち上げる前に、チャンネルの送信側に対して clone を呼び出しています。 これにより、最初に立ち上げたスレッドに渡せる新しい送信ハンドルが得られます。 元のチャンネルの送信側は、2番目に立ち上げたスレッドに渡します。これにより2つスレッドが得られ、 それぞれチャンネルの受信側に異なるメッセージを送信します。 コードを実行すると、出力は以下のようなものになるはずです: Got: hi\nGot: more\nGot: from\nGot: messages\nGot: for\nGot: the\nGot: thread\nGot: you 別の順番で値が出る可能性もあります; システム次第です。並行性が面白いと同時に難しい部分でもあります。 異なるスレッドで色々な値を与えて thread::sleep で実験をしたら、走らせるたびにより非決定的になり、 毎回異なる出力をするでしょう。 チャンネルの動作方法を見たので、他の並行性に目を向けましょう。","breadcrumbs":"恐れるな!並行性 » 転送機をクローンして複数の生成器を作成する","id":"ch16-02-message-passing.html#a転送機をクローンして複数の生成器を作成する","title":"転送機をクローンして複数の生成器を作成する"},"ch16-03-shared-state.html#aミューテックスを使用して一度に1つのスレッドからデータにアクセスすることを許可する":{"body":"ミューテックスは、どんな時も1つのスレッドにしかなんらかのデータへのアクセスを許可しないというように、 \"mutual exclusion\"(相互排他)の省略形です。ミューテックスにあるデータにアクセスするには、 ミューテックスのロックを所望することでアクセスしたいことをまず、スレッドは通知しなければなりません。 ロックとは、現在誰がデータへの排他的アクセスを行なっているかを追跡するミューテックの一部をなすデータ構造です。 故に、ミューテックスはロックシステム経由で保持しているデータを 死守する (guarding)と解説されます。 ミューテックスは、2つの規則を覚えておく必要があるため、難しいという評判があります: データを使用する前にロックの獲得を試みなければならない。 ミューテックスが死守しているデータの使用が終わったら、他のスレッドがロックを獲得できるように、 データをアンロックしなければならない。 ミューテックスを現実世界の物で例えるなら、マイクが1つしかない会議のパネルディスカッションを思い浮かべてください。 パネリストが発言できる前に、マイクを使用したいと申し出たり、通知しなければなりません。マイクを受け取ったら、 話したいだけ話し、それから次に発言を申し出たパネリストにマイクを手渡します。パネリストが発言し終わった時に、 マイクを手渡すのを忘れていたら、誰も他の人は発言できません。共有されているマイクの管理がうまくいかなければ、 パネルは予定通りに機能しないでしょう! ミューテックスの管理は、正しく行うのに著しく技工を要することがあるので、多くの人がチャンネルに熱狂的になるわけです。 しかしながら、Rustの型システムと所有権規則のおかげで、ロックとアンロックをおかしくすることはありません。 Mutex のAPI ミューテックスの使用方法の例として、ミューテックスをシングルスレッドの文脈で使うことから始めましょう。 リスト16-12のようにですね: ファイル名: src/main.rs use std::sync::Mutex; fn main() { let m = Mutex::new(5); { let mut num = m.lock().unwrap(); *num = 6; } println!(\"m = {:?}\", m);\n} リスト16-12: 簡潔性のために Mutex のAPIをシングルスレッドの文脈で探究する 多くの型同様、 new という関連関数を使用して Mutex を生成します。ミューテックス内部のデータにアクセスするには、 lock メソッドを使用してロックを獲得します。この呼び出しは、現在のスレッドをブロックするので、 ロックを得られる順番が来るまで何も作業はできません。 ロックを保持している他のスレッドがパニックしたら、 lock の呼び出しは失敗するでしょう。その場合、 誰もロックを取得することは叶わないので、 unwrap すると決定し、そのような状況になったら、 このスレッドをパニックさせます。 ロックを獲得した後、今回の場合、 num と名付けられていますが、戻り値を中に入っているデータへの可変参照として扱うことができます。 型システムにより、 m の値を使用する前にロックを獲得していることが確認されます: Mutex は i32 ではないので、 i32 を使用できるようにするには、ロックを獲得し なければならない のです。忘れることはあり得ません; 型システムにより、それ以外の場合に内部の i32 にアクセスすることは許されません。 お察しかもしれませんが、 Mutex はスマートポインタです。より正確を期すなら、 lock の呼び出しが MutexGuard というスマートポインタを 返却 します。このスマートポインタが、 内部のデータを指す Deref を実装しています; このスマートポインタはさらに MutexGuard がスコープを外れた時に、 自動的にロックを解除する Drop 実装もしていて、これがリスト16-12の内部スコープの終わりで発生します。 結果として、ロックの解除が自動的に行われるので、ロックの解除を忘れ、 ミューテックスが他のスレッドで使用されるのを阻害するリスクを負いません。 ロックをドロップした後、ミューテックスの値を出力し、内部の i32 の値を6に変更できたことが確かめられるのです。 複数のスレッド間で Mutex を共有する さて、 Mutex を使って複数のスレッド間で値を共有してみましょう。10個のスレッドを立ち上げ、 各々カウンタの値を1ずつインクリメントさせるので、カウンタは0から10まで上がります。 以下の数例は、コンパイルエラーになることに注意し、そのエラーを使用して Mutex の使用法と、 コンパイラがそれを正しく活用する手助けをしてくれる方法について学びます。リスト16-13が最初の例です: ファイル名: src/main.rs use std::sync::Mutex;\nuse std::thread; fn main() { let counter = Mutex::new(0); let mut handles = vec![]; for _ in 0..10 { let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!(\"Result: {}\", *counter.lock().unwrap());\n} リスト16-13: Mutex により死守されているカウンタを10個のスレッドがそれぞれインクリメントする リスト16-12のように、 counter 変数を生成して Mutex の内部に i32 を保持しています。 次に、数値の範囲をマッピングして10個のスレッドを生成しています。 thread::spawn を使用して、 全スレッドに同じクロージャを与えています。このクロージャは、スレッド内にカウンタをムーブし、 lock メソッドを呼ぶことで Mutex のロックを獲得し、それからミューテックスの値に1を足します。 スレッドがクロージャを実行し終わったら、 num はスコープ外に出てロックを解除するので、 他のスレッドが獲得できるわけです。 メインスレッドで全てのjoinハンドルを収集します。それからリスト16-2のように、各々に対して join を呼び出し、 全スレッドが終了するのを確かめています。その時点で、メインスレッドはロックを獲得し、このプログラムの結果を出力します。 この例はコンパイルできないでしょうと仄めかしました。では、理由を探りましょう! error[E0382]: capture of moved value: `counter`\n(エラー: ムーブされた値をキャプチャしています: `counter`) --> src/main.rs:10:27 |\n9 | let handle = thread::spawn(move || { | ------- value moved (into closure) here\n10 | let mut num = counter.lock().unwrap(); | ^^^^^^^ value captured here after move | = note: move occurs because `counter` has type `std::sync::Mutex`, which does not implement the `Copy` trait error[E0382]: use of moved value: `counter` --> src/main.rs:21:29 |\n9 | let handle = thread::spawn(move || { | ------- value moved (into closure) here\n...\n21 | println!(\"Result: {}\", *counter.lock().unwrap()); | ^^^^^^^ value used here after move | = note: move occurs because `counter` has type `std::sync::Mutex`, which does not implement the `Copy` trait error: aborting due to 2 previous errors\n(エラー: 前述の2つのエラーによりアボート) エラーメッセージは、 counter 値はクロージャにムーブされ、それから lock を呼び出したときにキャプチャされていると述べています。 その説明は、所望した動作のように聞こえますが、許可されていないのです! プログラムを単純化してこれを理解しましょう。 for ループで10個スレッドを生成する代わりに、 ループなしで2つのスレッドを作るだけにしてどうなるか確認しましょう。 リスト16-13の最初の for ループを代わりにこのコードと置き換えてください: use std::sync::Mutex;\nuse std::thread; fn main() { let counter = Mutex::new(0); let mut handles = vec![]; let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); let handle2 = thread::spawn(move || { let mut num2 = counter.lock().unwrap(); *num2 += 1; }); handles.push(handle2); for handle in handles { handle.join().unwrap(); } println!(\"Result: {}\", *counter.lock().unwrap());\n} 2つのスレッドを生成し、2番目のスレッドの変数名を handle2 と num2 に変更しています。 今回このコードを走らせると、コンパイラは以下の出力をします: error[E0382]: capture of moved value: `counter` --> src/main.rs:16:24 |\n8 | let handle = thread::spawn(move || { | ------- value moved (into closure) here\n...\n16 | let mut num2 = counter.lock().unwrap(); | ^^^^^^^ value captured here after move | = note: move occurs because `counter` has type `std::sync::Mutex`, which does not implement the `Copy` trait error[E0382]: use of moved value: `counter` --> src/main.rs:26:29 |\n8 | let handle = thread::spawn(move || { | ------- value moved (into closure) here\n...\n26 | println!(\"Result: {}\", *counter.lock().unwrap()); | ^^^^^^^ value used here after move | = note: move occurs because `counter` has type `std::sync::Mutex`, which does not implement the `Copy` trait error: aborting due to 2 previous errors なるほど!最初のエラーメッセージは、 handle に紐づけられたスレッドのクロージャに counter がムーブされていることを示唆しています。 そのムーブにより、それに対して lock を呼び出し、結果を2番目のスレッドの num2 に保持しようとした時に、 counter をキャプチャすることを妨げています!ゆえに、コンパイラは、 counter の所有権を複数のスレッドに移すことはできないと教えてくれています。 これは、以前では確認しづらかったことです。なぜなら、スレッドはループの中にあり、 ループの違う繰り返しにある違うスレッドをコンパイラは指し示せないからです。 第15章で議論した複数所有権メソッドによりコンパイルエラーを修正しましょう。 複数のスレッドで複数の所有権 第15章で、スマートポインタの Rc を使用して参照カウントの値を作ることで、1つの値に複数の所有者を与えました。 同じことをここでもして、どうなるか見ましょう。リスト16-14で Rc に Mutex を包含し、 所有権をスレッドに移す前に Rc をクローンします。今やエラーを確認したので、 for ループの使用に立ち戻り、クロージャに move キーワードを使用し続けます。 ファイル名: src/main.rs use std::rc::Rc;\nuse std::sync::Mutex;\nuse std::thread; fn main() { let counter = Rc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Rc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!(\"Result: {}\", *counter.lock().unwrap());\n} リスト16-14: Rc を使用して複数のスレッドに Mutex を所有させようとする 再三、コンパイルし……別のエラーが出ました!コンパイラはいろんなことを教えてくれています。 error[E0277]: the trait bound `std::rc::Rc>:\nstd::marker::Send` is not satisfied in `[closure@src/main.rs:11:36:\n15:10 counter:std::rc::Rc>]`\n(エラー: トレイト境界`std::rc::Rc>:\nstd::marker::Send`は`[closure@src/main.rs:11:36:15:10\ncounter:std::rc::Rc>]`で満たされていません) --> src/main.rs:11:22 |\n11 | let handle = thread::spawn(move || { | ^^^^^^^^^^^^^ `std::rc::Rc>`\ncannot be sent between threads safely (`std::rc::Rc>`は、スレッド間で安全に送信できません) | = help: within `[closure@src/main.rs:11:36: 15:10\ncounter:std::rc::Rc>]`, the trait `std::marker::Send` is\nnot implemented for `std::rc::Rc>` (ヘルプ: `[closure@src/main.rs:11:36 15:10 counter:std::rc::Rc>]`内でトレイト`std::marker::Send`は、 `std::rc::Rc>`に対して実装されていません) = note: required because it appears within the type\n`[closure@src/main.rs:11:36: 15:10 counter:std::rc::Rc>]` (注釈: 型`[closure@src/main.rs:11:36 15:10 counter:std::rc::Rc>]`内に出現するので必要です) = note: required by `std::thread::spawn` (注釈: `std::thread::spawn`により必要とされています) おお、このエラーメッセージはとても長ったらしいですね!こちらが、注目すべき重要な部分です: 最初のインラインエラーは `std::rc::Rc>` cannot be sent between threads safely と述べています。この理由は、エラーメッセージの次に注目すべき重要な部分にあります。 洗練されたエラーメッセージは、 the trait bound `Send` is not satisfied と述べています。 Send については、次の節で語ります: スレッドとともに使用している型が並行な場面で使われることを意図したものであることを保証するトレイトの1つです。 残念ながら、 Rc はスレッド間で共有するには安全ではないのです。 Rc が参照カウントを管理する際、 clone が呼び出されるたびにカウントを追加し、クローンがドロップされるたびにカウントを差し引きます。 しかし、並行基本型を使用してカウントの変更が別のスレッドに妨害されないことを確認していないのです。 これは間違ったカウントにつながる可能性があり、今度はメモリリークや、使用し終わる前に値がドロップされることにつながる可能性のある潜在的なバグです。 必要なのは、いかにも Rc のようだけれども、参照カウントへの変更をスレッドセーフに行うものです。 Arc で原子的な参照カウント 幸いなことに、 Arc は Rc のような並行な状況で安全に使用できる型 です 。 a は atomic を表し、原子的に参照カウントする型を意味します。アトミックは、 ここでは詳しく講義しない並行性の別の基本型です: 詳細は、 std::sync::atomic の標準ライブラリドキュメンテーションを参照されたし。現時点では、 アトミックは、基本型のように動くけれども、スレッド間で共有しても安全なことだけ知っていれば良いです。 そうしたらあなたは、なぜ全ての基本型がアトミックでなく、標準ライブラリの型も標準で Arc を使って実装されていないのか疑問に思う可能性があります。 その理由は、スレッド安全性が、本当に必要な時だけ支払いたいパフォーマンスの犠牲とともに得られるものだからです。 シングルスレッドで値に処理を施すだけなら、アトミックが提供する保証を強制する必要がない方がコードはより速く走るのです。 例に回帰しましょう: Arc と Rc のAPIは同じなので、 use 行と new の呼び出しと clone の呼び出しを変更して、 プログラムを修正します。リスト16-15は、ようやくコンパイルでき、動作します: ファイル名: src/main.rs use std::sync::{Mutex, Arc};\nuse std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!(\"Result: {}\", *counter.lock().unwrap());\n} リスト16-15: Arc を使用して Mutex をラップし、所有権を複数のスレッド間で共有できるようにする このコードは、以下のように出力します: Result: 10 やりました!0から10まで数え上げました。これは、あまり印象的ではないように思えるかもしれませんが、 本当に Mutex とスレッド安全性についていろんなことを教えてくれました。このプログラムの構造を使用して、 カウンタをインクリメントする以上の複雑な処理を行うこともできるでしょう。この手法を使えば、 計算を独立した部分に小分けにし、その部分をスレッドに分割し、それから Mutex を使用して、 各スレッドに最終結果を更新させることができます。","breadcrumbs":"恐れるな!並行性 » ミューテックスを使用して一度に1つのスレッドからデータにアクセスすることを許可する","id":"ch16-03-shared-state.html#aミューテックスを使用して一度に1つのスレッドからデータにアクセスすることを許可する","title":"ミューテックスを使用して一度に1つのスレッドからデータにアクセスすることを許可する"},"ch16-03-shared-state.html#a状態共有並行性":{"body":"メッセージ受け渡しは、並行性を扱う素晴らしい方法ですが、唯一の方法ではありません。 Go言語ドキュメンテーションのスローガンのこの部分を再び考えてください: 「メモリを共有することでやり取りする。」 メモリを共有することでやり取りするとはどんな感じなのでしょうか?さらに、 なぜメッセージ受け渡しに熱狂的な人は、それを使わず、代わりに全く反対のことをするのでしょうか? ある意味では、どんなプログラミング言語のチャンネルも単独の所有権に類似しています。 一旦チャンネルに値を転送したら、その値は最早使用することがないからです。 メモリ共有並行性は、複数の所有権に似ています: 複数のスレッドが同時に同じメモリ位置にアクセスできるのです。 第15章でスマートポインタが複数の所有権を可能にするのを目の当たりにしたように、 異なる所有者を管理する必要があるので、複数の所有権は複雑度を増させます。 Rustの型システムと所有権規則は、この管理を正しく行う大きな助けになります。 例として、メモリ共有を行うより一般的な並行性の基本型の一つであるミューテックスを見てみましょう。","breadcrumbs":"恐れるな!並行性 » 状態共有並行性","id":"ch16-03-shared-state.html#a状態共有並行性","title":"状態共有並行性"},"ch16-03-shared-state.html#refcellt--rct-と-mutext--arct-の類似性":{"body":"counter は不変なのに、その内部にある値への可変参照を得ることができたことに気付いたでしょうか; つまり、 Mutex は、 Cell 系のように内部可変性を提供するわけです。 第15章で RefCell を使用して Rc の内容を可変化できるようにしたのと同様に、 Mutex を使用して Arc の内容を可変化しているのです。 気付いておくべき別の詳細は、 Mutex を使用する際にあらゆる種類のロジックエラーからは、 コンパイラは保護してくれないということです。第15章で Rc は、循環参照を生成してしまうリスクを伴い、 そうすると、2つの Rc の値がお互いを参照し合い、メモリリークを引き起こしてしまうことを思い出してください。 同様に、 Mutex は デッドロック を生成するリスクを伴っています。これは、処理が2つのリソースをロックする必要があり、 2つのスレッドがそれぞれにロックを1つ獲得して永久にお互いを待ちあってしまうときに起こります。 デッドロックに興味があるのなら、デッドロックのあるRustプログラムを組んでみてください; それからどんな言語でもいいので、ミューテックスに対してデッドロックを緩和する方法を調べて、 Rustで是非、それを実装してみてください。 Mutex と MutexGuard に関する標準ライブラリのAPIドキュメンテーションは、 役に立つ情報を提供してくれます。 Send と Sync トレイトと、それらを独自の型で使用する方法について語って、この章を締めくくります。","breadcrumbs":"恐れるな!並行性 » RefCell / Rc と Mutex / Arc の類似性","id":"ch16-03-shared-state.html#refcellt--rct-と-mutext--arct-の類似性","title":"RefCell / Rc と Mutex / Arc の類似性"},"ch16-04-extensible-concurrency-sync-and-send.html#aまとめ":{"body":"この本において並行性を見かけるのは、これで最後ではありません: 第20章のプロジェクトでは、 この章の概念をここで議論した微小な例よりもより現実的な場面で使用するでしょう。 前述のように、Rustによる並行性の取扱いのごく一部のみが言語仕様なので、多くの並行性の解決策は クレートとして実装されています。これらは標準ライブラリよりも迅速に進化するので、 マルチスレッド環境で使用すべき現在の最先端のクレートを必ずネットで検索してください。 Rustの標準ライブラリは、メッセージ受け渡しにチャンネルを、並行の文脈で安全に使用できる、 Mutex や Arc などのスマートポインタ型を提供しています。型システムと借用チェッカーにより、 これらの解決策を使用するコードがデータ競合や無効な参照に行き着かないことを保証してくれます。 一旦コードをコンパイルすることができたら、他の言語ではありふれている追跡困難な類のバグなしに、 複数のスレッドでも喜んで動くので安心できます。並行プログラミングは、もはや恐れるべき概念ではありません: 恐れることなく前進し、プログラムを並行にしてください! 次は、Rustプログラムが肥大化するにつれて問題をモデル化し、解決策を構造化する慣例的な方法について話します。 さらに、Rustのイディオムがオブジェクト指向プログラミングで馴染み深いかもしれないイディオムにどのように関連しているかについても議論します。","breadcrumbs":"恐れるな!並行性 » まとめ","id":"ch16-04-extensible-concurrency-sync-and-send.html#aまとめ","title":"まとめ"},"ch16-04-extensible-concurrency-sync-and-send.html#send-でスレッド間の所有権の転送を許可する":{"body":"Send マーカートレイトは、 Send を実装した型の所有権をスレッド間で転送できることを示唆します。 Rustのほとんどの型は Send ですが、 Rc を含めて一部例外があります: この型は、 Rc の値をクローンし、 クローンしたものの所有権を別のスレッドに転送しようとしたら、両方のスレッドが同時に参照カウントを更新できてしまうので、 Send になり得ません。このため、 Rc はスレッド安全性のためのパフォーマンスの犠牲を支払わなくても済む、 シングルスレッド環境で使用するために実装されているわけです。 故に、Rustの型システムとトレイト境界により、 Rc の値を不安全にスレッド間で誤って送信することが絶対ないよう保証してくれるのです。 リスト16-14でこれを試みた時には、 the trait Send is not implemented for Rc> というエラーが出ました。 Send の Arc に切り替えたら、コードはコンパイルできたわけです。 完全に Send の型からなる型も全て自動的に Send と印付けされます。生ポインタを除くほとんどの基本型も Send で、 生ポインタについては第19章で議論します。","breadcrumbs":"恐れるな!並行性 » Send でスレッド間の所有権の転送を許可する","id":"ch16-04-extensible-concurrency-sync-and-send.html#send-でスレッド間の所有権の転送を許可する","title":"Send でスレッド間の所有権の転送を許可する"},"ch16-04-extensible-concurrency-sync-and-send.html#send-と-sync-を手動で実装するのは非安全である":{"body":"Send と Sync トレイトから構成される型は自動的に Send と Sync にもなるので、 それらのトレイトを手動で実装する必要はありません。マーカートレイトとして、 実装すべきメソッドさえも何もありません。並行性に関連する不変条件を強制することに役立つだけなのです。 これらのトレイトを手動で実装するには、unsafeなRustコードを実装することが関わってきます。 unsafeなRustコードを使用することについては第19章で語ります; とりあえず、重要な情報は、 Send と Sync ではない部品からなる新しい並行な型を構成するには、安全性保証を保持するために、 注意深い思考が必要になるということです。 The Rustonomicon には、 これらの保証とそれを保持する方法についての情報がより多くあります。 訳注: 日本語版のThe Rustonomiconは こちら です。","breadcrumbs":"恐れるな!並行性 » Send と Sync を手動で実装するのは非安全である","id":"ch16-04-extensible-concurrency-sync-and-send.html#send-と-sync-を手動で実装するのは非安全である","title":"Send と Sync を手動で実装するのは非安全である"},"ch16-04-extensible-concurrency-sync-and-send.html#sync-で複数のスレッドからのアクセスを許可する":{"body":"Sync マーカートレイトは、 Sync を実装した型は、複数のスレッドから参照されても安全であることを示唆します。 言い換えると、 &T ( T への参照)が Send なら、型 T は Sync であり、参照が他のスレッドに安全に送信できることを意味します。 Send 同様、基本型は Sync であり、 Sync の型からのみ構成される型もまた Sync です。 Send ではなかったのと同じ理由で、スマートポインタの Rc もまた Sync ではありません。 RefCell 型(これについては第15章で話しました)と関連する Cell 系についても Sync ではありません。 RefCell が実行時に行う借用チェックの実装は、スレッド安全ではないのです。 スマートポインタの Mutex は Sync で、「複数のスレッド間で Mutex を共有する」節で見たように、 複数のスレッドでアクセスを共有するのに使用することができます。","breadcrumbs":"恐れるな!並行性 » Sync で複数のスレッドからのアクセスを許可する","id":"ch16-04-extensible-concurrency-sync-and-send.html#sync-で複数のスレッドからのアクセスを許可する","title":"Sync で複数のスレッドからのアクセスを許可する"},"ch16-04-extensible-concurrency-sync-and-send.html#sync-と-send-トレイトで拡張可能な並行性":{"body":"面白いことに、Rust言語には、 寡 少な並行性機能があります。この章でここまでに語った並行性機能のほとんどは、 標準ライブラリの一部であり、言語ではありません。並行性を扱う選択肢は、言語や標準ライブラリに制限されません; 独自の並行性機能を書いたり、他人が書いたものを利用したりできるのです。 ですが、2つの並行性概念が言語に埋め込まれています: std::marker トレイトの Sync と Send です。","breadcrumbs":"恐れるな!並行性 » Sync と Send トレイトで拡張可能な並行性","id":"ch16-04-extensible-concurrency-sync-and-send.html#sync-と-send-トレイトで拡張可能な並行性","title":"Sync と Send トレイトで拡張可能な並行性"},"ch17-00-oop.html#rustのオブジェクト指向プログラミング機能":{"body":"オブジェクト指向プログラミング(OOP)は、プログラムをモデル化する手段です。オブジェクトは、 1960年代のSimulaに端緒を発しています。このオブジェクトは、 お互いにメッセージを渡し合うというアラン・ケイ(Alan Kay)のプログラミングアーキテクチャに影響を及ぼしました。 彼は、このアーキテクチャを解説するために、 オブジェクト指向プログラミング という用語を造語しました。 多くの競合する定義がOOPが何かを解説しています; Rustをオブジェクト指向と区分する定義もありますし、 しない定義もあります。この章では、広くオブジェクト指向と捉えられる特定の特徴と、 それらの特徴がこなれたRustでどう表現されるかを探究します。それからオブジェクト指向のデザインパターンをRustで実装する方法を示し、 そうすることとRustの強みを活用して代わりの解決策を実装する方法の代償を議論します。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能","id":"ch17-00-oop.html#rustのオブジェクト指向プログラミング機能","title":"Rustのオブジェクト指向プログラミング機能"},"ch17-01-what-is-oo.html#aオブジェクトはデータと振る舞いを含む":{"body":"エーリヒ・ガンマ(Enoch Gamma)、リチャード・ヘルム(Richard Helm)、ラルフ・ジョンソン(Ralph Johnson)、 ジョン・ブリシディース(John Vlissides)(アディソン・ワズリー・プロ)により、 1994年に書かれた デザインパターン: 再利用可能なオブジェクト指向ソフトウェアの要素 という本は、 俗に 4人のギャングの本 ( 訳注 : the Gang of Four book; GoFとよく略される)と呼ばれ、オブジェクト指向デザインパターンのカタログです。 そこでは、OOPは以下のように定義されています: オブジェクト指向プログラムは、オブジェクトで構成される。オブジェクトは、 データとそのデータを処理するプロシージャを梱包している。このプロシージャは、 典型的に メソッド または オペレーション と呼ばれる。 この定義を使用すれば、Rustはオブジェクト指向です: 構造体とenumにはデータがありますし、 impl ブロックが構造体とenumにメソッドを提供します。メソッドのある構造体とenumは、 オブジェクトとは呼ばれないものの、GoFのオブジェクト定義によると、同じ機能を提供します。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能 » オブジェクトは、データと振る舞いを含む","id":"ch17-01-what-is-oo.html#aオブジェクトはデータと振る舞いを含む","title":"オブジェクトは、データと振る舞いを含む"},"ch17-01-what-is-oo.html#aオブジェクト指向言語の特徴":{"body":"言語がオブジェクト指向と考えられるのになければならない機能について、プログラミングコミュニティ内での総意はありません。 RustはOOPを含めた多くのプログラミングパラダイムに影響を受けています; 例えば、 第13章で関数型プログラミングに由来する機能を探究しました。議論はあるかもしれませんが、 OOP言語は特定の一般的な特徴を共有しています。具体的には、オブジェクトやカプセル化、 継承などです。それらの個々の特徴が意味するものとRustがサポートしているかを見ましょう。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能 » オブジェクト指向言語の特徴","id":"ch17-01-what-is-oo.html#aオブジェクト指向言語の特徴","title":"オブジェクト指向言語の特徴"},"ch17-01-what-is-oo.html#aカプセル化は実装詳細を隠蔽する":{"body":"OOPとよく紐づけられる別の側面は、カプセル化の思想です。これは、オブジェクトの実装詳細は、 そのオブジェクトを使用するコードにはアクセスできないことを意味します。故に、 オブジェクトと相互作用する唯一の手段は、その公開APIを通してです; オブジェクトを使用するコードは、 オブジェクトの内部に到達して、データや振る舞いを直接変更できるべきではありません。 このために、プログラマはオブジェクトの内部をオブジェクトを使用するコードを変更する必要なく、 変更しリファクタリングできます。 カプセル化を制御する方法は、第7章で議論しました: pub キーワードを使用して、 自分のコードのどのモジュールや型、関数、メソッドを公開するか決められ、 既定ではそれ以外のものは全て非公開になります。例えば、 i32 値のベクタを含むフィールドのある AveragedCollection という構造体を定義できます。 この構造体はさらに、ベクタの値の平均を含むフィールドを持てます。つまり、平均は誰かが必要とする度に、 オンデマンドで計算する必要はないということです。言い換えれば、 AveragedCollection は、 計算した平均をキャッシュしてくれるわけです。リスト17-1には、 AveragedCollection 構造体の定義があります: ファイル名: src/lib.rs pub struct AveragedCollection { list: Vec, average: f64,\n} リスト17-1: 整数のリストとコレクションの要素の平均を管理する AveragedCollection 構造体 構造体は、他のコードが使用できるように pub で印づけされていますが、構造体のフィールドは非公開のままです。 値が追加されたりリストから削除される度に、平均も更新されることを保証したいので、今回の場合重要です。 add や remove 、 average メソッドを構造体に実装することでこれをします。リスト17-2のようにですね: ファイル名: src/lib.rs # pub struct AveragedCollection {\n# list: Vec,\n# average: f64,\n# }\nimpl AveragedCollection { pub fn add(&mut self, value: i32) { self.list.push(value); self.update_average(); } pub fn remove(&mut self) -> Option { let result = self.list.pop(); match result { Some(value) => { self.update_average(); Some(value) }, None => None, } } pub fn average(&self) -> f64 { self.average } fn update_average(&mut self) { let total: i32 = self.list.iter().sum(); self.average = total as f64 / self.list.len() as f64; }\n} リスト17-2: AveragedCollection の add 、 remove 、 average 公開メソッドの実装 add 、 remove 、 average の公開メソッドが AveragedCollection のインスタンスを変更する唯一の方法になります。 要素が add メソッドを使用して list に追加されたり、 remove メソッドを使用して削除されたりすると、 各メソッドの実装が average フィールドの更新を扱う非公開の update_average メソッドも呼び出します。 list と average フィールドを非公開のままにしているので、外部コードが要素を list フィールドに直接追加したり削除したりする方法はありません; そうでなければ、 average フィールドは、 list が変更された時に同期されなくなる可能性があります。 average メソッドは average フィールドの値を返し、外部コードに average を読ませるものの、 変更は許可しません。 構造体 AveragedCollection の実装詳細をカプセル化したので、データ構造などの側面を将来容易に変更することができます。 例を挙げれば、 list フィールドに Vec ではなく HashSet を使うこともできます。 add 、 remove 、 average といった公開メソッドのシグニチャが同じである限り、 AveragedCollection を使用するコードは変更する必要がないでしょう。 代わりに list を公開にしたら、必ずしもこうはならないでしょう: HashSet と Vec は、 要素の追加と削除に異なるメソッドを持っているので、外部コードが直接 list を変更しているなら、 外部コードも変更しなければならない可能性が高いでしょう。 カプセル化が、言語がオブジェクト指向と考えられるのに必要な側面ならば、Rustはその条件を満たしています。 コードの異なる部分で pub を使用するかしないかという選択肢のおかげで、実装詳細をカプセル化することが可能になります。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能 » カプセル化は、実装詳細を隠蔽する","id":"ch17-01-what-is-oo.html#aカプセル化は実装詳細を隠蔽する","title":"カプセル化は、実装詳細を隠蔽する"},"ch17-01-what-is-oo.html#a型システムおよびコード共有としての継承":{"body":"継承 は、それによってオブジェクトが他のオブジェクトの定義から受け継ぐことができる機構であり、 それ故に、再定義する必要なく、親オブジェクトのデータと振る舞いを得ます。 言語がオブジェクト指向言語であるために継承がなければならないのならば、Rustは違います。 親構造体のフィールドとメソッドの実装を受け継ぐ構造体を定義する方法はありません。しかしながら、 継承がプログラミング道具箱にあることに慣れていれば、そもそも継承に手を伸ばす理由によって、 Rustで他の解決策を使用することができます。 継承を選択する理由は主に2つあります。1つ目は、コードの再利用です: ある型に特定の振る舞いを実装し、 継承により、その実装を他の型にも再利用できるわけです。デフォルトのトレイトメソッド実装を代わりに使用して、 Rustコードを共有でき、これは、リスト10-14で Summary トレイトに summarize メソッドのデフォルト実装を追加した時に見かけました。 Summary トレイトを実装する型は全て、追加のコードなく summarize メソッドが使用できます。 これは、親クラスにメソッドの実装があり、継承した子クラスにもそのメソッドの実装があることと似ています。 また、 Summary トレイトを実装する時に、 summarize メソッドのデフォルト実装を上書きすることもでき、 これは、親クラスから継承したメソッドの実装を子クラスが上書きすることに似ています。 継承を使用するもう1つの理由は、型システムに関連しています: 親の型と同じ箇所で子供の型を使用できるようにです。 これは、 多相性 (polymorphism)とも呼ばれ、複数のオブジェクトが特定の特徴を共有しているなら、 実行時にお互いに代用できることを意味します。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能 » 型システム、およびコード共有としての継承","id":"ch17-01-what-is-oo.html#a型システムおよびコード共有としての継承","title":"型システム、およびコード共有としての継承"},"ch17-01-what-is-oo.html#a多相性":{"body":"多くの人にとって、多相性は、継承の同義語です。ですが、実際には複数の型のデータを取り扱えるコードを指すより一般的な概念です。 継承について言えば、それらの型は一般的にはサブクラスです。 Rustは代わりにジェネリクスを使用して様々な可能性のある型を抽象化し、トレイト境界を使用してそれらの型が提供するものに制約を課します。 これは時に、 パラメータ境界多相性 (bounded parametric polymorphism)と呼ばれます。 継承は、近年、多くのプログラミング言語において、プログラムの設計解決策としては軽んじられています。 というのも、しばしば必要以上にコードを共有してしまう危険性があるからです。サブクラスは、 必ずしも親クラスの特徴を全て共有するべきではないのに、継承ではそうなってしまうのです。 これにより、プログラムの設計の柔軟性を失わせることもあります。また、道理に合わなかったり、メソッドがサブクラスには適用されないために、 エラーを発生させるようなサブクラスのメソッドの呼び出しを引き起こす可能性が出てくるのです。 さらに、サブクラスに1つのクラスからだけ継承させる言語もあり、さらにプログラムの設計の柔軟性が制限されます。 これらの理由により、継承ではなくトレイトオブジェクトを使用してRustは異なるアプローチを取っています。 Rustにおいて、トレイトオブジェクトがどう多相性を可能にするかを見ましょう。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能 » 多相性","id":"ch17-01-what-is-oo.html#a多相性","title":"多相性"},"ch17-02-trait-objects.html#aトレイトを実装する":{"body":"さて、 Draw トレイトを実装する型を追加しましょう。 Button 型を提供します。ここも、実際にGUIライブラリを実装することは、 この本の範疇を超えているので、 draw メソッドの本体は、何も有用な実装はしません。実装がどんな感じになるか想像するために、 Button 構造体は、 width 、 height 、 label フィールドを持っている可能性があります。 リスト17-7に示したようにですね: ファイル名: src/lib.rs # pub trait Draw {\n# fn draw(&self);\n# }\n#\npub struct Button { pub width: u32, pub height: u32, pub label: String,\n} impl Draw for Button { fn draw(&self) { // code to actually draw a button // 実際にボタンを描画するコード }\n} リスト17-7: Draw トレイトを実装するある Button 構造体 Button の width 、 height 、 label フィールドは、 TextField 型のように、 それらのフィールドプラス placeholder フィールドを代わりに持つ可能性のある他のコンポーネントのフィールドとは異なるでしょう。 スクリーンに描画したい型のコンポーネントはそれぞれ Draw トレイトを実装しますが、 Button がここでしているように、 draw メソッドでは異なるコードを使用してその特定の型を描画する方法を定義しています(実際のGUIコードは、 この章の範疇を超えるのでありませんが)。例えば、 Button には、ユーザがボタンをクリックした時に起こることに関連するメソッドを含む、 追加の impl ブロックがある可能性があります。この種のメソッドは、 TextField のような型には適用されません。 ライブラリの使用者が、 width 、 height 、 options フィールドのある SelectBox 構造体を実装しようと決めたら、 SelectBox 型にも Draw トレイトを実装します。リスト17-8のようにですね: ファイル名: src/main.rs extern crate gui;\nuse gui::Draw; struct SelectBox { width: u32, height: u32, options: Vec,\n} impl Draw for SelectBox { fn draw(&self) { // code to actually draw a select box //セレクトボックスを実際に描画するコード }\n} リスト17-8: gui を使用し、 SelectBox 構造体に Draw トレイトを実装する別のクレート ライブラリの使用者はもう、 main 関数を書き、 Screen インスタンスを生成できます。 Screen インスタンスには、 それぞれを Box に放り込んでトレイトオブジェクト化して SelectBox と Button を追加できます。 それから Screen インスタンスに対して run メソッドを呼び出すことができ、そうすると各コンポーネントの draw が呼び出されます。 リスト17-9は、この実装を示しています: ファイル名: src/main.rs use gui::{Screen, Button}; fn main() { let screen = Screen { components: vec![ Box::new(SelectBox { width: 75, height: 10, options: vec![ // はい String::from(\"Yes\"), // 多分 String::from(\"Maybe\"), // いいえ String::from(\"No\") ], }), Box::new(Button { width: 50, height: 10, // 了解 label: String::from(\"OK\"), }), ], }; screen.run();\n} リスト17-9: トレイトオブジェクトを使って同じトレイトを実装する異なる型の値を格納する ライブラリを記述した時点では、誰かが SelectBox 型を追加する可能性があるなんて知りませんでしたが、 Screen の実装は、新しい型を処理し、描画することができました。何故なら、 SelectBox は Draw 型、 つまり、 draw メソッドを実装しているからです。 この値の具体的な型ではなく、値が応答したメッセージにのみ関係するという概念は、 動的型付け言語の ダックタイピング に似た概念です: アヒルのように歩き、鳴くならば、 アヒルに違いないのです!リスト17-5の Screen の run の実装では、 run は、 各コンポーネントの実際の型がなんであるか知る必要はありません。コンポーネントが、 Button や SelectBox のインスタンスであるかを確認することはなく、コンポーネントの draw メソッドを呼び出すだけです。 components ベクタで Box を値の型として指定することで、 Screen を、 draw メソッドを呼び出せる値を必要とするように定義できたのです。 注釈: ダックタイピングについて ご存知かもしれませんが、ダックタイピングについて補足です。ダックタイピングとは、動的型付け言語やC++のテンプレートで使用される、 特定のフィールドやメソッドがあることを想定してコンパイルを行い、実行時に実際にあることを確かめるというプログラミング手法です。 ダック・テストという思考法に由来するそうです。 ダックタイピングの利点は、XMLやJSONなど、厳密なスキーマがないことが多い形式を扱いやすくなること、 欠点は、実行してみるまで動くかどうかわからないことでしょう。 トレイトオブジェクトとRustの型システムを使用してダックタイピングを活用したコードに似たコードを書くことの利点は、 実行時に値が特定のメソッドを実装しているか確認したり、値がメソッドを実装していない時にエラーになることを心配したりする必要は絶対になく、 とにかく呼び出せることです。コンパイラは、値が、トレイトオブジェクトが必要としているトレイトを実装していなければ、 コンパイルを通さないのです。 例えば、リスト17-10は、コンポーネントに String のある Screen を作成しようとした時に起こることを示しています: ファイル名: src/main.rs extern crate gui;\nuse gui::Screen; fn main() { let screen = Screen { components: vec![ Box::new(String::from(\"Hi\")), ], }; screen.run();\n} リスト17-10: トレイトオブジェクトのトレイトを実装しない型の使用を試みる String は Draw トレイトを実装していないので、このようなエラーが出ます: error[E0277]: the trait bound `std::string::String: gui::Draw` is not satisfied --> src/main.rs:7:13 | 7 | Box::new(String::from(\"Hi\")), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait gui::Draw is not implemented for `std::string::String` | = note: required for the cast to the object type `gui::Draw` このエラーは、渡すことを意図していないものを Screen に渡しているので、異なる型を渡すべきか、 Screen が draw を呼び出せるように String に Draw を実装するべきのどちらかであることを知らせてくれています。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能 » トレイトを実装する","id":"ch17-02-trait-objects.html#aトレイトを実装する","title":"トレイトを実装する"},"ch17-02-trait-objects.html#aトレイトオブジェクトで異なる型の値を許容する":{"body":"第8章で、ベクタの1つの制限は、たった1つの型の要素を保持することしかできないことだと述べました。 リスト8-10で整数、浮動小数点数、テキストを保持する列挙子のある SpreadsheetCell enumを定義して、 これを回避しました。つまり、各セルに異なる型のデータを格納しつつ、1行のセルを表すベクタを保持するということです。 コンパイル時にわかるある固定されたセットの型にしか取り替え可能な要素がならない場合には、 完璧な解決策です。 ところが、時として、ライブラリの使用者が特定の場面で合法になる型のセットを拡張できるようにしたくなることがあります。 これをどう実現する可能性があるか示すために、各アイテムに draw メソッドを呼び出してスクリーンに描画するという、 GUIツールで一般的なテクニックをしてあるリストの要素を走査する例のGUIツールを作ります。 GUIライブラリの構造を含む gui と呼ばれるライブラリクレートを作成します。 このクレートには、他人が使用できる Button や TextField などの型が包含されるかもしれません。 さらに、 gui の使用者は、描画可能な独自の型を作成したくなるでしょう: 例えば、 ある人は Image を追加し、別の人は SelectBox を追加するかもしれません。 この例のために本格的なGUIライブラリは実装するつもりはありませんが、部品がどう組み合わさるかは示します。 ライブラリの記述時点では、他のプログラマが作成したくなる可能性のある型全てを知る由もなければ、定義することもできません。 しかし、 gui は異なる型の多くの値を追いかけ、この異なる型の値に対して draw メソッドを呼び出す必要があることは、 確かにわかっています。 draw メソッドを呼び出した時に正確に何が起きるかを知っている必要はありません。 値にそのメソッドが呼び出せるようあることだけわかっていればいいのです。 継承のある言語でこれを行うには、 draw という名前のメソッドがある Component というクラスを定義するかもしれません。 Button 、 Image 、 SelectBox などの他のクラスは、 Component を継承し、故に draw メソッドを継承します。 個々に draw メソッドをオーバーライドして、独自の振る舞いを定義するものの、フレームワークは、 Component インスタンスであるかのようにその型全部を扱い、この型に対して draw を呼び出します。 ですが、Rustに継承は存在しないので、使用者に新しい型で拡張してもらうために gui ライブラリを構成する他の方法が必要です。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能 » トレイトオブジェクトで異なる型の値を許容する","id":"ch17-02-trait-objects.html#aトレイトオブジェクトで異なる型の値を許容する","title":"トレイトオブジェクトで異なる型の値を許容する"},"ch17-02-trait-objects.html#aトレイトオブジェクトにはオブジェクト安全性が必要":{"body":"トレイトオブジェクトには、 オブジェクト安全 なトレイトしか作成できません。 トレイトオブジェクトを安全にする特性全てを司る複雑な規則がありますが、実際には、2つの規則だけが関係があります。 トレイトは、トレイト内で定義されているメソッド全てに以下の特性があれば、オブジェクト安全になります。 戻り値の型が Self でない。 ジェネリックな型引数がない。 Self キーワードは、トレイトやメソッドを実装しようとしている型の別名です。トレイトオブジェクトは、 一旦、トレイトオブジェクトを使用したら、コンパイラにはそのトレイトを実装している具体的な型を知りようがないので、 オブジェクト安全でなければなりません。トレイトメソッドが具体的な Self 型を返すのに、 トレイトオブジェクトが Self の具体的な型を忘れてしまったら、メソッドが元の具体的な型を使用できる手段はなくなってしまいます。 同じことがトレイトを使用する時に具体的な型引数で埋められるジェネリックな型引数に対しても言えます: 具体的な型がトレイトを実装する型の一部になるのです。トレイトオブジェクトの使用を通して型が忘却されたら、 そのジェネリックな型引数を埋める型がなんなのか知る術はないのです。 メソッドがオブジェクト安全でないトレイトの例は、標準ライブラリの Clone トレイトです。 Clone トレイトの clone メソッドのシグニチャは以下のような感じです: pub trait Clone { fn clone(&self) -> Self;\n} String 型は Clone トレイトを実装していて、 String のインスタンスに対して clone メソッドを呼び出すと、 String のインスタンスが返ってきます。同様に、 Vec のインスタンスに対して clone を呼び出すと、 Vec のインスタンスが返ってきます。 clone のシグニチャは、 Self の代わりに入る型を知る必要があります。 それが、戻り値の型になるからです。 コンパイラは、トレイトオブジェクトに関していつオブジェクト安全の規則を侵害するようなことを試みているかを示唆します。 例えば、リスト17-4で Screen 構造体を実装して Draw トレイトではなく、 Clone トレイトを実装した型を保持しようとしたとしましょう。こんな感じで: pub struct Screen { pub components: Vec>,\n} こんなエラーになるでしょう: error[E0038]: the trait `std::clone::Clone` cannot be made into an object\n(エラー: `std::clone::Clone`トレイトは、オブジェクトにすることはできません) --> src/lib.rs:2:5 |\n2 | pub components: Vec>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` cannot be\nmade into an object | = note: the trait cannot require that `Self : Sized` (注釈: このトレイトは、`Self : Sized`を満たせません) このエラーは、このようにこのトレイトをトレイトオブジェクトとして使用することはできないことを意味しています。 オブジェクト安全性についての詳細に興味があるのなら、 Rust RFC 255 を参照されたし。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能 » トレイトオブジェクトには、オブジェクト安全性が必要","id":"ch17-02-trait-objects.html#aトレイトオブジェクトにはオブジェクト安全性が必要","title":"トレイトオブジェクトには、オブジェクト安全性が必要"},"ch17-02-trait-objects.html#aトレイトオブジェクトはダイナミックディスパッチを行う":{"body":"第10章の「ジェネリクスを使用したコードのパフォーマンス」節でジェネリクスに対してトレイト境界を使用した時に、 コンパイラが行う単相化過程の議論を思い出してください: コンパイラは、関数やメソッドのジェネリックでない実装を、 ジェネリックな型引数の箇所に使用している具体的な型に対して生成するのでした。単相化の結果吐かれるコードは、 スタティックディスパッチ を行い、これは、コンパイル時にコンパイラがどのメソッドを呼び出しているかわかる時のことです。 これは、 ダイナミックディスパッチ とは対照的で、この時、コンパイラは、コンパイル時にどのメソッドを呼び出しているのかわかりません。 ダイナミックディスパッチの場合、コンパイラは、実行時にどのメソッドを呼び出すか弾き出すコードを生成します。 トレイトオブジェクトを使用すると、コンパイラはダイナミックディスパッチを使用しなければなりません。 コンパイラは、トレイトオブジェクトを使用しているコードで使用される可能性のある型全てを把握しないので、 どの型に実装されたどのメソッドを呼び出すかわからないのです。代わりに実行時に、トレイトオブジェクト内でポインタを使用して、 コンパイラは、どのメソッドを呼ぶか知ります。スタティックディスパッチでは行われないこの検索が起きる時には、 実行時コストがあります。また、ダイナミックディスパッチは、コンパイラがメソッドのコードをインライン化することも妨げ、 そのため、ある種の最適化が不可能になります。ですが、リスト17-5で記述し、 リスト17-9ではサポートできたコードで追加の柔軟性を確かに得られたので、考慮すべき代償です。","breadcrumbs":"Rustのオブジェクト指向プログラミング機能 » トレイトオブジェクトは、ダイナミックディスパッチを行う","id":"ch17-02-trait-objects.html#aトレイトオブジェクトはダイナミックディスパッチを行う","title":"トレイトオブジェクトは、ダイナミックディスパッチを行う"},"ch17-02-trait-objects.html#a一般的な振る舞いにトレイトを定義する":{"body":"gui に欲しい振る舞いを実装するには、 draw という1つのメソッドを持つ Draw というトレイトを定義します。 それから トレイトオブジェクト を取るベクタを定義できます。トレイトオブジェクトは、 指定したトレイトを実装するある型のインスタンスを指します。 & 参照や Box スマートポインタなどの、 何らかのポインタを指定し、それから関係のあるトレイトを指定する(トレイトオブジェクトがポインタを使用しなければならない理由については、 第19章の「動的サイズ付け型とSizedトレイト」節で語ります)ことでトレイトオブジェクトを作成します。 ジェネリックまたは具体的な型があるところにトレイトオブジェクトは使用できます。どこでトレイトオブジェクトを使用しようと、 Rustの型システムは、コンパイル時にその文脈で使用されているあらゆる値がそのトレイトオブジェクトのトレイトを実装していることを保証します。 結果としてコンパイル時に可能性のある型を全て知る必要はなくなるのです。 Rustでは、構造体とenumを他の言語のオブジェクトと区別するために「オブジェクト」と呼ぶことを避けていることに触れましたね。 構造体やenumにおいて、構造体のフィールドのデータや impl ブロックの振る舞いは区分けされているものの、 他の言語では1つの概念に押し込められるデータと振る舞いは、しばしばオブジェクトと分類されます。 しかしながら、トレイトオブジェクトは、データと振る舞いをごちゃ混ぜにするという観点で他の言語のオブジェクトに近い です 。 しかし、トレイトオブジェクトは、データを追加できないという点で伝統的なオブジェクトと異なっています。 トレイトオブジェクトは、他の言語のオブジェクトほど一般的に有用ではありません: その特定の目的は、共通の振る舞いに対して抽象化を行うことです。 リスト17-3は、 draw という1つのメソッドを持つ Draw というトレイトを定義する方法を示しています: ファイル名: src/lib.rs pub trait Draw { fn draw(&self);\n} リスト17-3: Draw トレイトの定義 この記法は、第10章のトレイトの定義方法に関する議論で馴染み深いはずです。その次は、新しい記法です: リスト17-4では、 components というベクタを保持する Screen という名前の構造体を定義しています。 このベクタの型は Box で、これはトレイトオブジェクトです; Draw トレイトを実装する Box 内部の任意の型に対する代役です。 ファイル名: src/lib.rs # pub trait Draw {\n# fn draw(&self);\n# }\n#\npub struct Screen { pub components: Vec>,\n} リスト17-4: Draw トレイトを実装するトレイトオブジェクトのベクタを保持する components フィールドがある Screen 構造体の定義 Screen 構造体に、 components の各要素に対して draw メソッドを呼び出す run というメソッドを定義します。 リスト17-5のようにですね: ファイル名: src/lib.rs # pub trait Draw {\n# fn draw(&self);\n# }\n#\n# pub struct Screen {\n# pub components: Vec>,\n# }\n#\nimpl Screen { pub fn run(&self) { for component in self.components.iter() { component.draw(); } }\n} リスト17-5: 各コンポーネントに対して draw メソッドを呼び出す Screen の run メソッド これは、トレイト境界を伴うジェネリックな型引数を使用する構造体を定義するのとは異なる動作をします。 ジェネリックな型引数は、一度に1つの具体型にしか置き換えられないのに対して、トレイトオブジェクトは、 実行時にトレイトオブジェクトに対して複数の具体型で埋めることができます。例として、 ジェネリックな型とトレイト境界を使用してリスト17-6のように Screen 構造体を定義することもできました: ファイル名: src/lib.rs # pub trait Draw {\n# fn draw(&self);\n# }\n#\npub struct Screen { pub components: Vec,\n} impl Screen where T: Draw { pub fn run(&self) { for component in self.components.iter() { component.draw(); } }\n} リスト17-6: ジェネリクスとトレイト境界を使用した Screen 構造体と run メソッドの対立的な実装 こうすると、全てのコンポーネントの型が Button だったり、 TextField だったりする Screen のインスタンスに制限されてしまいます。 絶対に同種のコレクションしか持つ予定がないのなら、ジェネリクスとトレイト境界は、 定義がコンパイル時に具体的な型を使用するように単相化されるので、望ましいです。 一方で、メソッドがトレイトオブジェクトを使用すると、1つの Screen インスタンスが、 Box