Showing preview only (301K chars total). Download the full file or copy to clipboard to get everything.
Repository: ghmagazine/rustbook
Branch: master
Commit: 4dfc6ca40c5c
Files: 205
Total size: 209.7 KB
Directory structure:
gitextract_qeqog3z0/
├── .circleci/
│ └── config.yml
├── .gitignore
├── LICENSE
├── README.md
├── RELEASES.md
├── ch02/
│ ├── ex02/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── examples/
│ │ │ └── println.rs
│ │ └── tests/
│ │ └── integration_tests.rs
│ ├── examples/
│ │ └── README.md
│ ├── hello/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── tests/
│ │ └── integration_tests.rs
│ └── rpn/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── src/
│ │ └── main.rs
│ └── tests/
│ └── integration_tests.rs
├── ch03/
│ └── bitonic-sorter/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ └── benchmark.rs
│ ├── py-src/
│ │ ├── .gitignore
│ │ └── bitonic_sorter.py
│ ├── src/
│ │ ├── first.rs
│ │ ├── fourth.rs
│ │ ├── lib.rs
│ │ ├── second.rs
│ │ ├── third.rs
│ │ └── utils.rs
│ └── tests/
│ └── integration_tests.rs
├── ch04/
│ └── ex04/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ ├── ch04_01_unit.rs
│ │ ├── ch04_02_bool.rs
│ │ ├── ch04_03_integer.rs
│ │ ├── ch04_04_overflowed1.rs
│ │ ├── ch04_05_overflowed2.rs
│ │ ├── ch04_06_float.rs
│ │ ├── ch04_07_char.rs
│ │ ├── ch04_08_reference1.rs
│ │ ├── ch04_09_reference2.rs
│ │ ├── ch04_10_raw_pointer.rs
│ │ ├── ch04_11_fn_pointer.rs
│ │ ├── ch04_12_fn_pointer_vs_closure.rs
│ │ ├── ch04_13_tuple.rs
│ │ ├── ch04_14_array.rs
│ │ ├── ch04_15_slice1.rs
│ │ ├── ch04_16_slice2.rs
│ │ └── ch04_17_str.rs
│ └── tests/
│ └── integration_tests.rs
├── ch05/
│ └── ex05/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ ├── ch05_01_box.rs
│ │ ├── ch05_02_vec.rs
│ │ ├── ch05_03_boxed_slice.rs
│ │ ├── ch05_04_hash_map.rs
│ │ ├── ch05_05_string1.rs
│ │ ├── ch05_06_string2.rs
│ │ ├── ch05_07_string3.rs
│ │ ├── ch05_08_string4.rs
│ │ ├── ch05_09_range.rs
│ │ ├── ch05_10_option.rs
│ │ ├── ch05_11_result.rs
│ │ ├── ch05_12_type_alias.rs
│ │ ├── ch05_13_struct1.rs
│ │ ├── ch05_14_struct2.rs
│ │ ├── ch05_15_struct3.rs
│ │ ├── ch05_16_struct4.rs
│ │ ├── ch05_17_struct5.rs
│ │ ├── ch05_18_enum1.rs
│ │ ├── ch05_19_enum2.rs
│ │ ├── ch05_20_adv_types1.rs
│ │ ├── ch05_21_adv_types2.rs
│ │ ├── ch05_22_type_cast1.rs
│ │ ├── ch05_23_type_cast2.rs
│ │ ├── ch05_24_transmute.rs
│ │ ├── ch05_25_type_coercion1.rs
│ │ ├── ch05_26_type_coercion2.rs
│ │ ├── ch05_27_type_coercion3.rs
│ │ ├── ch05_28_type_coercion4.rs
│ │ └── ch05_29_type_coercion5.rs
│ └── tests/
│ └── integration_tests.rs
├── ch06/
│ └── leap-year/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── src/
│ │ └── main.rs
│ └── tests/
│ └── integration_tests.rs
├── ch07/
│ ├── ex07/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── examples/
│ │ │ ├── ch07_01_value_scope.rs
│ │ │ ├── ch07_02_move_semantics.rs
│ │ │ ├── ch07_03_nll.rs
│ │ │ ├── ch07_04_static_lifetime.rs
│ │ │ ├── ch07_05_rc.rs
│ │ │ ├── ch07_06_simple_refcell.rs
│ │ │ ├── ch07_07_tls_refcell.rs
│ │ │ ├── ch07_08_arc_rwlock.rs
│ │ │ ├── ch07_09_static_rwlock.rs
│ │ │ └── ch07_10_closure.rs
│ │ └── tests/
│ │ └── integration_tests.rs
│ └── toy-vec/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ ├── toy_vec_01.rs
│ │ ├── toy_vec_02.rs
│ │ └── toy_vec_03.rs
│ ├── src/
│ │ └── lib.rs
│ └── tests/
│ └── integration_tests.rs
├── ch08/
│ └── ex08/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ ├── ch08_01_trait_basics.rs
│ │ ├── ch08_02_trait_basics.rs
│ │ ├── ch08_03_trait_basics.rs
│ │ ├── ch08_04_trait_generics.rs
│ │ ├── ch08_05_trait_generics.rs
│ │ ├── ch08_06_overload.rs
│ │ ├── ch08_07_trait_object.rs
│ │ ├── ch08_08_existential_impl_trait.rs
│ │ ├── ch08_09_associated_const.rs
│ │ ├── ch08_10_associated_type.rs
│ │ ├── ch08_11_sized.rs
│ │ └── ch08_12_trait_techniques.rs
│ ├── examples-java/
│ │ └── Overload.java
│ └── tests/
│ └── integration_tests.rs
├── ch09/
│ └── parser/
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src/
│ └── main.rs
├── ch10/
│ └── wordcount/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── README.md
│ ├── src/
│ │ ├── lib.rs
│ │ └── main.rs
│ ├── tests/
│ │ ├── char.rs
│ │ ├── line.rs
│ │ └── utils/
│ │ └── mod.rs
│ └── text.txt
├── ch11/
│ ├── log-collector/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── api/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── cli/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── main.rs
│ │ ├── docker-compose.yml
│ │ ├── server/
│ │ │ ├── Cargo.toml
│ │ │ ├── diesel.toml
│ │ │ ├── migrations/
│ │ │ │ ├── .gitkeep
│ │ │ │ ├── 00000000000000_diesel_initial_setup/
│ │ │ │ │ ├── down.sql
│ │ │ │ │ └── up.sql
│ │ │ │ └── 2018-12-28-161332_create_logs/
│ │ │ │ ├── down.sql
│ │ │ │ └── up.sql
│ │ │ └── src/
│ │ │ ├── db.rs
│ │ │ ├── handlers.rs
│ │ │ ├── main.rs
│ │ │ ├── model.rs
│ │ │ └── schema.rs
│ │ └── test.csv
│ ├── start-aw/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── static/
│ │ └── test.txt
│ ├── static-files/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ └── templates/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── src/
│ │ └── main.rs
│ └── templates/
│ └── index.html.tera
├── ch12/
│ ├── c-api/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── main.c
│ │ └── src/
│ │ └── lib.rs
│ ├── cffi-ownership/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ ├── c_src/
│ │ │ └── ownership.c
│ │ └── src/
│ │ └── main.rs
│ ├── cffi_readline.rs
│ ├── ffi-global/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ ├── onigmo-rs/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── onigmo/
│ │ │ ├── Cargo.toml
│ │ │ ├── examples/
│ │ │ │ └── simple.rs
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── onigmo-sys/
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ ├── examples/
│ │ │ └── simple.rs
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── wrapper.h
│ ├── opaque/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ ├── ptr.rs
│ ├── ptr_ownership.rs
│ ├── repr-c/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ ├── small_cffi.rs
│ └── static-link/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── build.rs
│ ├── c_src/
│ │ └── fib.c
│ └── src/
│ └── main.rs
├── howto/
│ └── running-msvc-compiler.md
└── install/
├── docker.md
└── windows10-vcpkg.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .circleci/config.yml
================================================
version: 2
.template:
test: &cargo-test
docker:
- image: circleci/rust:1.32.0
steps:
- checkout
- restore_cache:
key: cargo-cache-1.32.0
- run: rustc --version --verbose
- run: cargo --version --verbose
- run: |
echo $WORK_DIR;
cd $WORK_DIR;
cargo clean;
cargo test
cargo test --examples
- save_cache:
key: cargo-cache
paths:
- "~/.cargo"
jobs:
ch02/hello:
<<: *cargo-test
environment:
- WORK_DIR: ch02/hello
ch02/ex02:
<<: *cargo-test
environment:
- WORK_DIR: ch02/ex02
ch02/rpn:
<<: *cargo-test
environment:
- WORK_DIR: ch02/rpn
ch03/bitonic-sorter:
<<: *cargo-test
environment:
- WORK_DIR: ch03/bitonic-sorter
ch04/ex04:
<<: *cargo-test
environment:
- WORK_DIR: ch04/ex04
ch05/ex05:
<<: *cargo-test
environment:
- WORK_DIR: ch05/ex05
ch06/leap-year:
<<: *cargo-test
environment:
- WORK_DIR: ch06/leap-year
ch07/ex07:
<<: *cargo-test
environment:
- WORK_DIR: ch07/ex07
ch07/toy-vec:
<<: *cargo-test
environment:
- WORK_DIR: ch07/toy-vec
ch08/ex08:
<<: *cargo-test
environment:
- WORK_DIR: ch08/ex08
ch08/Overload:
docker:
- image: circleci/openjdk:11
steps:
- checkout
- run: javac -version
- run:
name: test ch08/ex08/examples-java/Overload.java
command: |
javac Overload.java
java Overload
working_directory: ch08/ex08/examples-java/
# TODO: Add an integration test for the bin target.
ch09/parser:
<<: *cargo-test
environment:
- WORK_DIR: ch09/parser
ch10/wordcount:
<<: *cargo-test
environment:
- WORK_DIR: ch10/wordcount
# TODO: Add an integration test for the bin target.
ch11/log-collector:
<<: *cargo-test
environment:
- WORK_DIR: ch11/log-collector
# TODO: Add an integration test for the bin target.
ch11/start-aw:
<<: *cargo-test
environment:
- WORK_DIR: ch11/start-aw
# TODO: Add an integration test for the bin target.
ch11/static-files:
<<: *cargo-test
environment:
- WORK_DIR: ch11/static-files
# TODO: Add an integration test for the bin target.
ch11/templates:
<<: *cargo-test
environment:
- WORK_DIR: ch11/templates
# TODO: Add an integration test for the bin target.
ch12/c-api:
<<: *cargo-test
environment:
- WORK_DIR: ch12/c-api
# TODO: Add an integration test for the bin target.
ch12/cffi-ownership:
<<: *cargo-test
environment:
- WORK_DIR: ch12/cffi-ownership
# TODO: Add an integration test for the bin target.
ch12/ffi-global:
<<: *cargo-test
environment:
- WORK_DIR: ch12/ffi-global
ch12/onigmo:
docker:
- image: circleci/rust:1.32.0
steps:
- checkout
- restore_cache:
key: cargo-cache
- run: rustc --version --verbose
- run: cargo --version --verbose
- run:
name: install onigmo 6.1.3
command: |
sudo apt-get install -y curl make;
curl -L -O https://github.com/k-takata/Onigmo/archive/Onigmo-6.1.3.tar.gz
tar xf Onigmo-*.tar.gz
cd Onigmo-O*
./configure
make
sudo make install
working_directory: /tmp/onigmo-build
- run:
name: install libclang for bindgen, clang for stddef.h
command: |
sudo apt-get install libclang-dev clang
- run:
name: test onigmo-sys
command: |
cargo clean;
cargo test --all-targets
working_directory: ch12/onigmo-rs/onigmo-sys
- run:
name: test onigmo
command: |
cargo clean;
cargo test --all-targets
working_directory: ch12/onigmo-rs/onigmo
- save_cache:
key: cargo-cache
paths:
- "~/.cargo"
# TODO: Add an integration test for the bin target.
ch12/opaque:
<<: *cargo-test
environment:
- WORK_DIR: ch12/opaque
# TODO: Add an integration test for the bin target.
ch12/repr-c:
<<: *cargo-test
environment:
- WORK_DIR: ch12/repr-c
# TODO: Add an integration test for the bin target.
ch12/static-link:
<<: *cargo-test
environment:
- WORK_DIR: ch12/static-link
# TODO: Add an integration test for the bin target.
ch12/cffi-readline:
docker:
- image: circleci/rust:1.32.0
steps:
- checkout
- restore_cache:
key: cargo-cache
- run: rustc --version --verbose
- run: cargo --version --verbose
- run:
name: test ch12/cffi_readline.rs
command: |
rustc --test cffi_readline.rs
./cffi_readline
working_directory: ch12/
- save_cache:
key: cargo-cache
paths:
- "~/.cargo"
# TODO: Add an integration test for the bin target.
ch12/ptr_ownership:
docker:
- image: circleci/rust:1.32.0
steps:
- checkout
- restore_cache:
key: cargo-cache
- run:
name: test ch12/ptr_ownership.rs
command: |
rustc --test ptr_ownership.rs
./ptr_ownership
working_directory: ch12/
- run: rustc --version --verbose
- run: cargo --version --verbose
- save_cache:
key: cargo-cache
paths:
- "~/.cargo"
# TODO: Add an integration test for the bin target.
ch12/smal_-cffi:
docker:
- image: circleci/rust:1.32.0
steps:
- checkout
- restore_cache:
key: cargo-cache
- run:
name: test ch12/small_cffi
command: |
rustc --test small_cffi.rs
./small_cffi
working_directory: ch12/
- run: rustc --version --verbose
- run: cargo --version --verbose
- save_cache:
key: cargo-cache
paths:
- "~/.cargo"
workflows:
version: 2
build_and_test:
jobs:
- ch02/hello
- ch02/ex02
- ch02/rpn
- ch03/bitonic-sorter
- ch04/ex04
- ch05/ex05
- ch06/leap-year
- ch07/ex07
- ch07/toy-vec
- ch08/ex08
- ch08/Overload
- ch09/parser
- ch10/wordcount
- ch11/log-collector
- ch11/start-aw
- ch11/static-files
- ch11/templates
- ch12/c-api
- ch12/cffi-ownership
- ch12/ffi-global
- ch12/onigmo
- ch12/opaque
- ch12/repr-c
- ch12/static-link
- ch12/cffi-readline
- ch12/ptr_ownership
- ch12/smal_-cffi
================================================
FILE: .gitignore
================================================
# Visual Studio Codeの設定ファイルが置かれる
.vscode/
# rustfmtが作成するバックアップファイル
*.rs.bk
# Emacsが作成するバックアップファイル
*~
# macOSのFinderが作成する管理ファイル
.DS_Store
================================================
FILE: LICENSE
================================================
BSD 3-Clause License
Copyright (c) 2019, ghmagazine
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# 『実践Rust入門』のサンプルプログラム
**実践Rust入門**</br>
[言語仕様から開発手法まで]</br></br>
κeen(著)、河野達也(著)、小松礼人(著)</br>
B5判/576ページ/本体価格3,980円+税</br>
ISBN番号 978-4-297-10559-4</br>
技術評論社、2019年5月8日発行
## サンプルプログラム
本GitHubリポジトリでは『**実践Rust入門**』(以降 **本書**)に掲載されているサンプルプログラムを収録しています。
本書の各章に対応する`ch章番号`ディレクトリを参照してください。
### ダウンロード方法
[リリースページ][releases-page] にある`*.zip`または`*.tar.gz`のアーカイブファイルをダウンロード後、解凍してください。
どちらのアーカイブも内容は同じです。
WindowsとmacOSをお使いなら`*.zip`を、Linuxをお使いなら`*.tar.gz`をダウンロードすることをお勧めします。
それぞれのリリースの内容については [RELEASES.mdファイル][releases-md] を参照してください。
[releases-page]: https://github.com/ghmagazine/rustbook/releases
[releases-md]: ./RELEASES.md
### 改行文字について
ソースファイルの改行文字はLinuxやmacOS環境で使用されている`LF`文字になります。Windows環境ではアプリケーションによっては正しく改行されないかもしれません。2章を参考に、Visual Studio Code(VS Code)などのソースコードエディタを使われることをお勧めします。
## 本書で使用する追加ソフトウェアについて
### インストール方法
本書で使用する追加ソフトウェアのインストール方法については以下を参考にしてください。
- [DockerとDocker Composeのインストール][docker](11章で使用)
- Windows MSVC:[vcpkgパッケージマネージャのインストール][vcpkg](11章で使用)
[docker]: ./install/docker.md
[vcpkg]: ./install/windows10-vcpkg.md
なお、Rustツールチェインとリンカのインストール方法については本書の2章を参照してください。
### 使いかたなど
追加ソフトウェアの使いかたについては以下を参考にしてください。
- Windows MSVC:[C/C++コンパイラをコマンドプロンプトから実行する方法][msvc-compiler](12章で使用)
[msvc-compiler]: ./howto/running-msvc-compiler.md
## 動作確認の環境について
サンプルプログラムは以下のRust/OSバージョンにて動作確認済みです。
- Rust 1.32.0(2019年1月16日リリース)
* 2018 Editionを指定
- x86-64系のプロセッサで動作する以下のOS
* Ubuntu "Bionic" 18.04 LTS(64ビット)
* macOS Mojave 10.14
* Windows 10 (64ビット、Microsoft Visual C++ 2017)
## ご質問や不具合報告など
本書やサンプルプログラムの内容についてご質問などあるときは、以下の方法でご確認・ご連絡ください。
- [本書の公式サポートページの正誤表][errata] で報告・訂正されていないかご確認
- Slack **rust-jp**チームの `#rust-bicycle-book` チャネルで著者らに質問
* 参加登録URL: http://rust-jp.herokuapp.com/
- [本書の公式サポートページのお問い合わせフォーム][inquiry-form] で編集部へお問い合わせ
- サンプルプログラムの明らかなバグなら、本リポジトリの [issueページ][gh-issues] で直接ご報告いただいても構いません。(その場合でもSlackなどでご一報いただいてからの方がスムーズに対応できるかもしれません)
[errata]: https://gihyo.jp/book/2019/978-4-297-10559-4/support
[inquiry-form]: https://gihyo.jp/site/inquiry/book?ISBN=978-4-297-10559-4
[gh-issues]: https://github.com/ghmagazine/rustbook/issues
## 本書の書籍案内ページ
https://gihyo.jp/book/2019/978-4-297-10559-4
本書の概要や目次、正誤表などが掲載されています。
## ライセンス
本リポジトリのコンテンツは特に断り書きがない限り **三条項BSDライセンス** のもとで公開されています。
- https://github.com/ghmagazine/rustbook/blob/master/LICENSE
以下のディレクトリ配下のコンテンツは異なるライセンスで公開されています。
- `ch10/wordcount`: **MIT** と **Apache-2.0** のデュアルライセンス
================================================
FILE: RELEASES.md
================================================
# 1.0.0 (2019-04-24)
* サンプルプログラムの初版を公開
* サンプルプログラムの内容は以下を除いて書籍の**初版**に掲載されているものと同等
* `ch12/onigmo-rs/onigmo-sys/build.rs`:`bindgen_test_layout_max_align_t`のテスト失敗を防ぐためにRust 1.28.0以上向けにコードを生成するよう修正([diff][v1-0-0-onigmo-sys-build-diff])
* 書籍2-2-7項に掲載されている`ch02/examples/bin/println.rs`のファイルパスを、他の章のファイルのネーミングルールに合わせて[`ch02/ex02/examples/println.rs`][v1-0-0-ex02-println]に変更
[v1-0-0-onigmo-sys-build-diff]: https://github.com/ghmagazine/rustbook/commit/9bebf5b7c2a5f9f8a74323aa9613808f2aa2897b
[v1-0-0-ex02-println]: ./ch02/ex02/examples/println.rs
================================================
FILE: ch02/ex02/.gitignore
================================================
# Cargoが選択した依存ライブラリのバージョン郡
Cargo.lock
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch02/ex02/Cargo.toml
================================================
[package]
name = "ex02"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
[dev-dependencies]
cli_test_dir = "0.1"
================================================
FILE: ch02/ex02/examples/println.rs
================================================
fn main() {
// 引数としてフォーマット文字列と1つの文字列を受け取る
println!(
"Hello, {}!",
"Takashi",
);
// → 実行すると「Hello, Takashi!」と表示される
// 引数としてフォーマット文字列と3つの数値を受け取る
println!(
// {:.1}で小数点以下1桁まで表示
"半径 {:.1}、円周率 {:.3}、面積 {:.3}",
3.2,
std::f64::consts::PI,
// 半径の自乗 × 円周率
3.2f64.powi(2) * std::f64::consts::PI,
);
// → 実行すると「半径 3.2、円周率 3.142、面積 32.170」と表示される
}
================================================
FILE: ch02/ex02/tests/integration_tests.rs
================================================
use cli_test_dir::*;
// バイナリを実行して入出力を確認するテスト
// `cargo test` で実行できる
const CMD: &'static str = "./examples/println";
#[test]
fn verify_output() {
let testdir = TestDir::new(CMD, "Verify output");
let output = testdir
.cmd()
// .tee_output() // stdoutとstderrorを順に表示する。デバッグに便利
.expect_success();
assert_eq!(
output.stdout_str(),
r#"Hello, Takashi!
半径 3.2、円周率 3.142、面積 32.170
"#
);
}
================================================
FILE: ch02/examples/README.md
================================================
# 可変個引数の例について
**2019年4月22日**
本書 初版 第2章 2-2-7項で`println!`マクロの可変個引数のコード例が紹介されていますが、ディレクトリ名とファイル名に誤りがありましたので、訂正いたします。
- 誤:`ch02/examples/bin/println.rs`
- 正:[`ch02/ex02/examples/println.rs`][ex02-println]
正しい方のファイルをご覧ください。
[ex02-println]: ../ex02/examples/println.rs
================================================
FILE: ch02/hello/.gitignore
================================================
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch02/hello/Cargo.toml
================================================
[package] # パッケージセクションの始まり
name = "hello" # 名前
version = "0.1.0" # バージョン
authors = ["Rust Bicycle Book <bicycle-book@example.com>"] # 作者(複数指定可)
edition = "2018" # エディション
[dependencies] # 依存クレートセクションの始まり(いまは空)
[dev-dependencies] # ビルド時のみ必要な依存クレートセクションの始まり
cli_test_dir = "0.1" # 結合テスト(tests/integration_tests.rs)が必要とするクレート
================================================
FILE: ch02/hello/src/main.rs
================================================
// エントリポイントとなる関数
fn main() {
// println!はマクロ。stdout(標準出力)に"Hello, world!"と出力する
println!("Hello, world!");
}
================================================
FILE: ch02/hello/tests/integration_tests.rs
================================================
use cli_test_dir::*;
// バイナリを実行して入出力を確認するテスト
// `cargo test` で実行できる
#[test]
fn run_hello() {
let testdir = TestDir::new("./hello", "Run hello");
let output = testdir
.cmd()
// .tee_output() // stdoutとstderrorを順に表示する。デバッグに便利
.expect_success();
assert_eq!(output.stdout_str(), "Hello, world!\n");
assert!(output.stderr_str().is_empty());
}
================================================
FILE: ch02/rpn/.gitignore
================================================
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch02/rpn/Cargo.toml
================================================
[package]
name = "rpn"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
[dev-dependencies]
cli_test_dir = "0.1"
================================================
FILE: ch02/rpn/src/main.rs
================================================
fn main() {
// 変数expをRPN形式の文字列に束縛する
// このRPNは数式 6.1 + 5.2 * 4.3 - 3.4 / 2.5 * 1.6 と等しい
let exp = "6.1 5.2 4.3 * + 3.4 2.5 / 1.6 * -";
// rpn関数を呼び出して計算する。返された値にans変数を束縛する
let ans = rpn(exp);
// デバッグビルド時のみ、答えが正しいかチェックする
// 浮動小数点の計算誤差を考慮し、ここでは小数点以下4桁までの値を文字列に変換している
debug_assert_eq!("26.2840", format!("{:.4}", ans));
// expとansの値を表示する。ansは小数点以下4桁まで表示する
println!("{} = {:.4}", exp, ans);
}
// RPN形式の文字列expを受け取り、f64型の計算結果を返す
fn rpn(exp: &str) -> f64 {
// 変数stackを空のスタックに束縛する
// stackはミュータブル(mutable、可変)の変数で、値の変更を許す
let mut stack = Vec::new();
// expの要素をスペースで分割し、tokenをそれらに順に束縛する
// 要素がなくなるまで繰り返す
for token in exp.split_whitespace() {
// tokenがf64型の数値ならスタックに積む
if let Ok(num) = token.parse::<f64>() {
stack.push(num);
} else {
// tokenが数値でないなら、演算子なのか調べる
match token {
// tokenが演算子ならapply2関数で計算する
// |x, y| x + y はクロージャ
// 引数x、yを取り、x + yを計算して答えを返す
"+" => apply2(&mut stack, |x, y| x + y),
"-" => apply2(&mut stack, |x, y| x - y),
"*" => apply2(&mut stack, |x, y| x * y),
"/" => apply2(&mut stack, |x, y| x / y),
// tokenが演算子でないなら、エラーを起こして終了する
_ => panic!("Unknown operator: {}", token),
}
}
}
// スタックから数値を1つ取り出す。失敗したらエラーを起こして終了する
stack.pop().expect("Stack underflow")
}
// スタックから数値を2つ取り出し、F型のクロージャfunで計算し、結果をスタックに積む
fn apply2<F>(stack: &mut Vec<f64>, fun: F)
// F型のトレイト境界。本文参照
where
F: Fn(f64, f64) -> f64,
{
// 変数yとxをスタックの最後の2要素に束縛する
if let (Some(y), Some(x)) = (stack.pop(), stack.pop()) {
// クロージャfunで計算し、その結果に変数zを束縛する。
let z = fun(x, y);
// 変数zの値をスタックに積む
stack.push(z);
} else {
// スタックから要素が取り出せなかったときはエラーを起こして終了する
panic!("Stack underflow");
}
}
================================================
FILE: ch02/rpn/tests/integration_tests.rs
================================================
use cli_test_dir::*;
// バイナリを実行して入出力を確認するテスト
// `cargo test` で実行できる
#[test]
fn run_rpn() {
let testdir = TestDir::new("./rpn", "Run rpn");
let output = testdir
.cmd()
// .tee_output() // stdoutとstderrorを順に表示する。デバッグに便利
.expect_success();
assert_eq!(output.stdout_str(), "6.1 5.2 4.3 * + 3.4 2.5 / 1.6 * - = 26.2840\n");
assert!(output.stderr_str().is_empty());
}
================================================
FILE: ch03/bitonic-sorter/.gitignore
================================================
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch03/bitonic-sorter/Cargo.toml
================================================
[package]
name = "bitonic-sorter"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
num_cpus = "1.8"
rand = "0.6"
rand_pcg = "0.1"
rayon = "1.0"
[dev-dependencies]
cli_test_dir = "0.1"
regex = "1"
================================================
FILE: ch03/bitonic-sorter/examples/benchmark.rs
================================================
use num_cpus;
use bitonic_sorter::SortOrder;
// 第3段階のsort関数をseq_sortという別名で使用する
use bitonic_sorter::third::sort as seq_sort;
// 第4段階のsort関数をpar_sortという別名で使用する
use bitonic_sorter::fourth::sort as par_sort;
use bitonic_sorter::utils::{is_sorted_ascending, new_u32_vec};
use std::{env, f64};
use std::str::FromStr;
use std::time::Instant;
fn main() {
// 1つ目のコマンドライン引数を文字列として取得する
if let Some(n) = env::args().nth(1) {
// 文字列型からu32型への変換を試み、成功したらbitsに束縛する
// もし失敗したならエラーを起こして終了する
let bits = u32::from_str(&n).expect("error parsing argument");
// 順次ソートと並列ソートを実行する
run_sorts(bits);
} else {
// コマンドライン引数が指定されてなかったらヘルプメッセージを表示して
// ステータスコード1で終了する
eprintln!(
"Usage {} <number of elements in bits>",
env::args().nth(0).unwrap()
);
std::process::exit(1);
}
}
fn run_sorts(bits: u32) {
// 指定されたビット数からデータの要素数を求める
// 例:
// 28ビット → 要素数 268,435,456
// 26ビット → 要素数 67,108,864
let len = 2.0_f64.powi(bits as i32) as usize;
// ソートする要素数とデータの見積もりサイズを表示する
println!(
"sorting {} integers ({:.1} MB)",
len,
(len * std::mem::size_of::<u32>()) as f64 / 1024.0 / 1024.0
);
// プロセッサの物理コア数と論理コア数を表示する
println!(
"cpu info: {} physical cores, {} logical cores",
num_cpus::get_physical(),
num_cpus::get()
);
// 順次ソートを実行して、処理にかかった時間を得る
let seq_duration = timed_sort(&seq_sort, len, "seq_sort");
// 並列ソートを実行して、処理にかかった時間を得る
let par_duration = timed_sort(&par_sort, len, "par_sort");
// 並列ソートが順次ソートに対して何倍速かったのか表示する
println!("speed up: {:.2}x", seq_duration / par_duration);
}
fn timed_sort<F>(sorter: &F, len: usize, name: &str) -> f64
where
F: Fn(&mut [u32], &SortOrder) -> Result<(), String>,
{
// 要素数lenのu32型ベクタを生成する
let mut x = new_u32_vec(len);
// sorter関数を呼び出すことで、ソートを実行する
// かかった時間(dur)を記録する
let start = Instant::now();
sorter(&mut x[..], &SortOrder::Ascending).expect("Failed to sort: ");
let dur = start.elapsed();
// ソートした要素数とかかった時間(秒)を表示する
let nano_secs = dur.subsec_nanos() as f64 + dur.as_secs() as f64 * 1e9_f64;
println!(
"{}: sorted {} integers in {} seconds",
name,
len,
nano_secs / 1e9
);
// ソート結果が正しいか検証する
assert!(is_sorted_ascending(&x[..]));
nano_secs
}
================================================
FILE: ch03/bitonic-sorter/py-src/.gitignore
================================================
*.pyc
================================================
FILE: ch03/bitonic-sorter/py-src/bitonic_sorter.py
================================================
# -*- coding:utf-8-unix -*-
u"Bitonic Merge Sortモジュール。"
# Pythonによるsort関数
def sort(x, up):
u"""
リストxの要素を、upで指定された向きにソートする。upがTrueなら昇順、
Falseなら降順になる。xの要素数は2のべき乗でなければならない
(さもなければソート結果がおかしくなる)
"""
if len(x) <= 1:
# 要素数が1以下になったら終わり
return x
else:
# ステップ1a
# リストの前半(first)は昇順、後半(second)は降順でソートする
mid_point = len(x) // 2 # `//`は整数除算
first = sort(x[:mid_point], True)
second = sort(x[mid_point:], False)
# ステップ1b
# 2分割したリストを1つに結合する
x1 = first + second
# ステップ2:サブソートに進む
return _sub_sort(x1, up)
def _sub_sort(x, up):
u"""
バイトニックにソートされたリストxの前半と後半を、upで指定された向きに
比較、交換し、前半と後半それぞれについて再帰的にサブソートを適用する
"""
if len(x) == 1:
# 要素数が1以下になったら終わり
return x
else:
# ステップ2a
# 要素数nのバイトニック列の要素をn/2要素おきに比較して
# upで指定された順序(昇順または降順)になるように交換する
_compare_and_swap(x, up)
# ステップ2b
# データ列を半分に分割し、それぞれに対して_sub_sortを繰り返す
mid_point = len(x) // 2
first = _sub_sort(x[:mid_point], up)
second = _sub_sort(x[mid_point:], up)
# ステップ2c
# 2分割したデータ列を1つに結合する
return first + second
def _compare_and_swap(x, up):
u"""
要素数nのバイトニック列の要素をn/2要素おきに比較して、upで指定された
順序(昇順または降順)になるよう交換する(ステップ2a)
"""
mid_point = len(x) // 2
for i in range(mid_point):
if (x[i] > x[mid_point + i]) == up:
# 要素を交換する
x[i], x[mid_point + i] = x[mid_point + i], x[i]
================================================
FILE: ch03/bitonic-sorter/src/first.rs
================================================
// pubはこのsort関数が他のモジュールからアクセスできることを示す
// 引数xの型 `&mut [u32]` について
// &は値をポインタ経由で借用することを示す(借用については7章で説明)
// mutは値が変更可能であることを示す
// u32型は32ビット符号なし整数
// [u32]型はu32のスライス(現時点でスライスは1次元の配列と考えてよい)
pub fn sort(x: &mut [u32], up: bool) {
if x.len() > 1 {
let mid_point = x.len() / 2;
sort(&mut x[..mid_point], true);
sort(&mut x[mid_point..], false);
sub_sort(x, up);
}
}
fn sub_sort(x: &mut [u32], up: bool) {
if x.len() > 1 {
compare_and_swap(x, up);
let mid_point = x.len() / 2;
sub_sort(&mut x[..mid_point], up);
sub_sort(&mut x[mid_point..], up);
}
}
fn compare_and_swap(x: &mut [u32], up: bool) {
let mid_point = x.len() / 2;
for i in 0..mid_point {
if (x[i] > x[mid_point + i]) == up {
// 要素を交換する
x.swap(i, mid_point + i);
}
}
}
// このモジュールはcargo testを実行したときのみコンパイルされる
#[cfg(test)]
mod tests {
// 親モジュール(first)のsort関数を使用する
use super::sort;
// #[test]の付いた関数はcargo testとしたときに実行される
#[test]
fn sort_u32_ascending() {
// テストデータとしてu32型のベクタを作成しxに束縛する
// sort関数によって内容が更新されるので、可変を表すmutキーワードが必要
let mut x = vec![10, 30, 11, 20, 4, 330, 21, 110];
// xのスライスを作成し、sort関数を呼び出す
// `&mut x`は`&mut x[..]`と書いてもいい
sort(&mut x, true);
// xの要素が昇順にソートされていることを確認する
assert_eq!(x, vec![4, 10, 11, 20, 21, 30, 110, 330]);
}
#[test]
fn sort_u32_descending() {
let mut x = vec![10, 30, 11, 20, 4, 330, 21, 110];
sort(&mut x, false);
// xの要素が降順にソートされていることを確認する
assert_eq!(x, vec![330, 110, 30, 21, 20, 11, 10, 4]);
}
}
================================================
FILE: ch03/bitonic-sorter/src/fourth.rs
================================================
use super::SortOrder;
use rayon;
use std::cmp::Ordering;
// 並列に処理するかを決める、しきい値
const PARALLEL_THRESHOLD: usize = 4096;
pub fn sort<T: Ord + Send>(x: &mut [T], order: &SortOrder) -> Result<(), String> {
match *order {
SortOrder::Ascending => sort_by(x, &|a, b| a.cmp(b)),
SortOrder::Descending => sort_by(x, &|a, b| b.cmp(a)),
}
}
pub fn sort_by<T, F>(x: &mut [T], comparator: &F) -> Result<(), String>
where T: Send,
F: Sync + Fn(&T, &T) -> Ordering
{
if x.len().is_power_of_two() {
do_sort(x, true, comparator);
Ok(())
} else {
Err(format!("The length of x is not a power of two. (x.len(): {})", x.len()))
}
}
fn do_sort<T, F>(x: &mut [T], forward: bool, comparator: &F)
where T: Send,
F: Sync + Fn(&T, &T) -> Ordering
{
if x.len() > 1 {
let mid_point = x.len() / 2;
// xをmid_pointを境にした2つの可変の借用に分割し
// firstとsecondに束縛する
let (first, second) = x.split_at_mut(mid_point);
// xの分割後の要素数をしきい値(PARALLEL_THRESHOLD)と比較する
if mid_point >= PARALLEL_THRESHOLD {
// しきい値以上なら並列にソートする(並列処理)
rayon::join(|| do_sort(first, true, comparator),
|| do_sort(second, false, comparator));
} else {
// しきい値未満なら順番にソートする(順次処理)
do_sort(first, true, comparator);
do_sort(second, false, comparator);
}
sub_sort(x, forward, comparator);
}
}
fn sub_sort<T, F>(x: &mut [T], forward: bool, comparator: &F)
where T: Send,
F: Sync + Fn(&T, &T) -> Ordering
{
if x.len() > 1 {
compare_and_swap(x, forward, comparator);
let mid_point = x.len() / 2;
let (first, second) = x.split_at_mut(mid_point);
if mid_point >= PARALLEL_THRESHOLD {
rayon::join(|| sub_sort(first, forward, comparator),
|| sub_sort(second, forward, comparator));
} else {
sub_sort(first, forward, comparator);
sub_sort(second, forward, comparator);
}
}
}
fn compare_and_swap<T, F>(x: &mut [T], forward: bool, comparator: &F)
where F: Fn(&T, &T) -> Ordering
{
let swap_condition = if forward {
Ordering::Greater
} else {
Ordering::Less
};
let mid_point = x.len() / 2;
for i in 0..mid_point {
if comparator(&x[i], &x[mid_point + i]) == swap_condition {
x.swap(i, mid_point + i);
}
}
}
#[cfg(test)]
mod tests {
use super::{sort, sort_by};
use crate::SortOrder::*;
use crate::utils::{new_u32_vec, is_sorted_ascending, is_sorted_descending};
#[derive(Debug, PartialEq)]
struct Student {
first_name: String,
last_name: String,
age: u8,
}
impl Student {
fn new(first_name: &str, last_name: &str, age: u8) -> Self {
Self {
first_name: first_name.to_string(),
last_name: last_name.to_string(),
age,
}
}
}
#[test]
fn sort_to_fail() {
let mut x = vec![10, 30, 11]; // x.len() が2のべき乗になっていない。
assert!(sort(&mut x, &Ascending).is_err());
}
#[test]
fn sort_u32_ascending() {
let mut x: Vec<u32> = vec![10, 30, 11, 20, 4, 330, 21, 110];
assert_eq!(sort(&mut x, &Ascending), Ok(()));
assert_eq!(x, vec![4, 10, 11, 20, 21, 30, 110, 330]);
}
#[test]
fn sort_u32_descending() {
let mut x: Vec<u32> = vec![10, 30, 11, 20, 4, 330, 21, 110];
assert_eq!(sort(&mut x, &Descending), Ok(()));
assert_eq!(x, vec![330, 110, 30, 21, 20, 11, 10, 4]);
}
#[test]
fn sort_u32_large() {
{
let mut x = new_u32_vec(65536);
assert_eq!(sort(&mut x, &Ascending), Ok(()));
assert!(is_sorted_ascending(&x));
}
{
let mut x = new_u32_vec(65536);
assert_eq!(sort(&mut x, &Descending), Ok(()));
assert!(is_sorted_descending(&x));
}
}
#[test]
fn sort_str_ascending() {
let mut x = vec!["Rust", "is", "fast", "and", "memory-efficient", "with", "no", "GC"];
assert_eq!(sort(&mut x, &Ascending), Ok(()));
assert_eq!(x, vec!["GC", "Rust", "and", "fast", "is", "memory-efficient", "no", "with"]);
}
#[test]
fn sort_str_descending() {
let mut x = vec!["Rust", "is", "fast", "and", "memory-efficient", "with", "no", "GC"];
assert_eq!(sort(&mut x, &Descending), Ok(()));
assert_eq!(x, vec!["with", "no", "memory-efficient", "is", "fast", "and", "Rust", "GC"]);
}
#[test]
fn sort_students_by_age_ascending() {
let taro = Student::new("Taro", "Yamada", 16);
let hanako = Student::new("Hanako", "Yamada", 14);
let kyoko = Student::new("Kyoko", "Ito", 15);
let ryosuke = Student::new("Ryosuke", "Hayashi", 17);
let mut x = vec![&taro, &hanako, &kyoko, &ryosuke];
let expected = vec![&hanako, &kyoko, &taro, &ryosuke];
assert_eq!(
// sort_by関数でソートする。第2引数はソート順を決めるクロージャ
// 引数に2つのStudent構造体をとり、ageフィールドの値をcmpメソッドで
// 比較することで大小を決定する
sort_by(&mut x, &|a, b| a.age.cmp(&b.age)),
Ok(())
);
assert_eq!(x, expected);
}
#[test]
fn sort_students_by_name_ascending() {
let taro = Student::new("Taro", "Yamada", 16);
let hanako = Student::new("Hanako", "Yamada", 14);
let kyoko = Student::new("Kyoko", "Ito", 15);
let ryosuke = Student::new("Ryosuke", "Hayashi", 17);
let mut x = vec![&taro, &hanako, &kyoko, &ryosuke];
let expected = vec![&ryosuke, &kyoko, &hanako, &taro];
assert_eq!(sort_by(&mut x,
// まずlast_nameを比較する
&|a, b| a.last_name.cmp(&b.last_name)
// もしlast_nameが等しくない(LessまたはGreater)ならそれを返す
// last_nameが等しい(Equal)ならfirst_nameを比較する
.then_with(|| a.first_name.cmp(&b.first_name))), Ok(())
);
assert_eq!(x, expected);
}
}
================================================
FILE: ch03/bitonic-sorter/src/lib.rs
================================================
pub mod utils;
// 第1段階:初歩的な実装。u32型の値のソートのみに対応
pub mod first;
// 第2段階:ジェネリクスでさまざまなデータ型に対応
pub mod second;
// 第3段階:クロージャでソート順をカスタマイズ
pub mod third;
// 最終形:並列ソート
pub mod fourth;
pub enum SortOrder {
Ascending, // 昇順
Descending, // 降順
}
================================================
FILE: ch03/bitonic-sorter/src/second.rs
================================================
use super::SortOrder;
// 成功時はOK(())を、失敗時はErr(文字列)を返す
pub fn sort<T: Ord>(x: &mut [T], order: &SortOrder) -> Result<(), String> {
if x.len().is_power_of_two() {
match *order {
SortOrder::Ascending => do_sort(x, true),
SortOrder::Descending => do_sort(x, false),
};
Ok(())
} else {
Err(format!("The length of x is not a power of two. (x.len(): {})", x.len()))
}
}
fn do_sort<T: Ord>(x: &mut [T], up: bool) {
if x.len() > 1 {
let mid_point = x.len() / 2;
do_sort(&mut x[..mid_point], true);
do_sort(&mut x[mid_point..], false);
sub_sort(x, up);
}
}
fn sub_sort<T: Ord>(x: &mut [T], up: bool) {
if x.len() > 1 {
compare_and_swap(x, up);
let mid_point = x.len() / 2;
sub_sort(&mut x[..mid_point], up);
sub_sort(&mut x[mid_point..], up);
}
}
fn compare_and_swap<T: Ord>(x: &mut [T], up: bool) {
let mid_point = x.len() / 2;
for i in 0..mid_point {
if (x[i] > x[mid_point + i]) == up {
x.swap(i, mid_point + i);
}
}
}
#[cfg(test)]
mod tests {
use super::sort;
use crate::SortOrder::*;
#[test]
fn sort_to_fail() {
let mut x = vec![10, 30, 11]; // x.len() が2のべき乗になっていない。
assert!(sort(&mut x, &Ascending).is_err()); // 戻り値はErr
}
#[test]
fn sort_u32_ascending() {
let mut x: Vec<u32> = vec![10, 30, 11, 20, 4, 330, 21, 110];
assert_eq!(sort(&mut x, &Ascending), Ok(()));
assert_eq!(x, vec![4, 10, 11, 20, 21, 30, 110, 330]);
}
#[test]
fn sort_u32_descending() {
let mut x: Vec<u32> = vec![10, 30, 11, 20, 4, 330, 21, 110];
assert_eq!(sort(&mut x, &Descending), Ok(()));
assert_eq!(x, vec![330, 110, 30, 21, 20, 11, 10, 4]);
}
#[test]
fn sort_str_ascending() {
// 文字列のベクタを作り、ソートする
let mut x = vec!["Rust", "is", "fast", "and", "memory-efficient", "with", "no", "GC"];
assert_eq!(sort(&mut x, &Ascending), Ok(()));
assert_eq!(x, vec!["GC", "Rust", "and", "fast", "is", "memory-efficient", "no", "with"]);
}
#[test]
fn sort_str_descending() {
let mut x = vec!["Rust", "is", "fast", "and", "memory-efficient", "with", "no", "GC"];
assert_eq!(sort(&mut x, &Descending), Ok(()));
assert_eq!(x, vec!["with", "no", "memory-efficient", "is", "fast", "and", "Rust", "GC"]);
}
}
================================================
FILE: ch03/bitonic-sorter/src/third.rs
================================================
use super::SortOrder;
use std::cmp::Ordering;
pub fn sort<T: Ord>(x: &mut [T], order: &SortOrder) -> Result<(), String> {
// do_sortを呼ぶ代わりに、sort_by を呼ぶようにする
// is_power_of_twoはsort_byが呼ぶので、ここからは削除した
match *order {
// 昇順ならa.cmp(b)、降順ならb.cmp(a)を行う
SortOrder::Ascending => sort_by(x, &|a, b| a.cmp(b)),
SortOrder::Descending => sort_by(x, &|a, b| b.cmp(a)),
}
}
pub fn sort_by<T, F>(x: &mut [T], comparator: &F) -> Result<(), String>
where F: Fn(&T, &T) -> Ordering
{
if x.len().is_power_of_two() {
do_sort(x, true, comparator);
Ok(())
} else {
Err(format!("The length of x is not a power of two. (x.len(): {})", x.len()))
}
}
fn do_sort<T, F>(x: &mut [T], forward: bool, comparator: &F)
where F: Fn(&T, &T) -> Ordering
{
if x.len() > 1 {
let mid_point = x.len() / 2;
// xをバイトニックにソートする
// 第2引数がtrueのときはcomparatorで示される順序でソート
do_sort(&mut x[..mid_point], true, comparator);
// 第2引数がfalseのときはcomparatorとは逆順でソート
do_sort(&mut x[mid_point..], false, comparator);
sub_sort(x, forward, comparator);
}
}
fn sub_sort<T, F>(x: &mut [T], forward: bool, comparator: &F)
where F: Fn(&T, &T) -> Ordering
{
if x.len() > 1 {
compare_and_swap(x, forward, comparator);
let mid_point = x.len() / 2;
sub_sort(&mut x[..mid_point], forward, comparator);
sub_sort(&mut x[mid_point..], forward, comparator);
}
}
fn compare_and_swap<T, F>(x: &mut [T], forward: bool, comparator: &F)
where F: Fn(&T, &T) -> Ordering
{
// 比較に先立ちforward(bool値)をOrdering値に変換しておく
let swap_condition = if forward {
Ordering::Greater
} else {
Ordering::Less
};
let mid_point = x.len() / 2;
for i in 0..mid_point {
// comparatorクロージャで2要素を比較し、返されたOrderingのバリアントが
// swap_conditionと等しいなら要素を交換する
if comparator(&x[i], &x[mid_point + i]) == swap_condition {
x.swap(i, mid_point + i);
}
}
}
#[cfg(test)]
mod tests {
use super::{sort, sort_by};
use crate::SortOrder::*;
use crate::utils::{new_u32_vec, is_sorted_ascending, is_sorted_descending};
// 構造体Studentを定義する
// 構造体は関連する値を1つにまとめたデータ構造。複数のデータフィールドを持つ
// deriveアトリビュートを使い、DebugトレイトとPartialEqトレイトの実装を自動導出する
#[derive(Debug, PartialEq)]
struct Student {
first_name: String, // first_name(名前)フィールド。String型
last_name: String, // last_name(苗字)フィールド。String型
age: u8, // age(年齢)フィールド。u8型(8ビット符号なし整数)
}
// implブロックを使うと対象の型に関連関数やメソッドを実装できる
impl Student {
// 関連関数newを定義する
fn new(first_name: &str, last_name: &str, age: u8) -> Self {
// 構造体Studentを初期化して返す。Selfはimpl対象の型(Student)の別名
Self {
// to_stringメソッドで&str型の引数からString型の値を作る。詳しくは5章で説明
first_name: first_name.to_string(), // first_nameフィールドに値を設定
last_name: last_name.to_string(), // last_nameフィールドに値を設定
age, // ageフィールドにage変数の値を設定
// フィールドと変数が同じ名前のときは、このように省略形で書ける
}
}
}
#[test]
fn sort_to_fail() {
let mut x = vec![10, 30, 11]; // x.len() が2のべき乗になっていない。
assert!(sort(&mut x, &Ascending).is_err());
}
#[test]
fn sort_u32_ascending() {
let mut x: Vec<u32> = vec![10, 30, 11, 20, 4, 330, 21, 110];
assert_eq!(sort(&mut x, &Ascending), Ok(()));
assert_eq!(x, vec![4, 10, 11, 20, 21, 30, 110, 330]);
}
#[test]
fn sort_u32_descending() {
let mut x: Vec<u32> = vec![10, 30, 11, 20, 4, 330, 21, 110];
assert_eq!(sort(&mut x, &Descending), Ok(()));
assert_eq!(x, vec![330, 110, 30, 21, 20, 11, 10, 4]);
}
#[test]
fn sort_u32_large() {
{
// 乱数で65,536要素のデータ列を作る(65,536は2の16乗)
let mut x = new_u32_vec(65536);
// 昇順にソートする
assert_eq!(sort(&mut x, &Ascending), Ok(()));
// ソート結果が正しいことを検証する
assert!(is_sorted_ascending(&x));
}
{
let mut x = new_u32_vec(65536);
assert_eq!(sort(&mut x, &Descending), Ok(()));
assert!(is_sorted_descending(&x));
}
}
#[test]
fn sort_str_ascending() {
let mut x = vec!["Rust", "is", "fast", "and", "memory-efficient", "with", "no", "GC"];
assert_eq!(sort(&mut x, &Ascending), Ok(()));
assert_eq!(x, vec!["GC", "Rust", "and", "fast", "is", "memory-efficient", "no", "with"]);
}
#[test]
fn sort_str_descending() {
let mut x = vec!["Rust", "is", "fast", "and", "memory-efficient", "with", "no", "GC"];
assert_eq!(sort(&mut x, &Descending), Ok(()));
assert_eq!(x, vec!["with", "no", "memory-efficient", "is", "fast", "and", "Rust", "GC"]);
}
#[test]
// 年齢で昇順にソートする
fn sort_students_by_age_ascending() {
// 4人分のテストデータを作成
let taro = Student::new("Taro", "Yamada", 16);
let hanako = Student::new("Hanako", "Yamada", 14);
let kyoko = Student::new("Kyoko", "Ito", 15);
let ryosuke = Student::new("Ryosuke", "Hayashi", 17);
// ソート対象のベクタを作成する
let mut x = vec![&taro, &hanako, &kyoko, &ryosuke];
// ソート後の期待値を作成する
let expected = vec![&hanako, &kyoko, &taro, &ryosuke];
assert_eq!(
// sort_by関数でソートする。第2引数はソート順を決めるクロージャ
// 引数に2つのStudent構造体をとり、ageフィールドの値をcmpメソッドで
// 比較することで大小を決定する
sort_by(&mut x, &|a, b| a.age.cmp(&b.age)),
Ok(())
);
// 結果を検証する
assert_eq!(x, expected);
}
#[test]
fn sort_students_by_name_ascending() {
let taro = Student::new("Taro", "Yamada", 16);
let hanako = Student::new("Hanako", "Yamada", 14);
let kyoko = Student::new("Kyoko", "Ito", 15);
let ryosuke = Student::new("Ryosuke", "Hayashi", 17);
let mut x = vec![&taro, &hanako, &kyoko, &ryosuke];
let expected = vec![&ryosuke, &kyoko, &hanako, &taro];
assert_eq!(sort_by(&mut x,
// まずlast_nameを比較する
&|a, b| a.last_name.cmp(&b.last_name)
// もしlast_nameが等しくない(LessまたはGreater)ならそれを返す
// last_nameが等しい(Equal)ならfirst_nameを比較する
.then_with(|| a.first_name.cmp(&b.first_name))), Ok(())
);
assert_eq!(x, expected);
}
}
================================================
FILE: ch03/bitonic-sorter/src/utils.rs
================================================
use rand::{Rng, SeedableRng};
use rand::distributions::Standard;
use rand_pcg::Pcg64Mcg;
pub fn new_u32_vec(n: usize) -> Vec<u32> {
// RNGを初期化する。再現性を持たせるため毎回同じシード値を使う
let mut rng = Pcg64Mcg::from_seed([0; 16]);
// rng.sample_iter()は乱数を無限に生成するイテレータを返す
// take(n)は元のイテレータから最初のn要素だけを取り出すイテレータを返す
// collect()はイテレータから値を収集して、ベクタやハッシュマップのような
// コレクションに格納する
rng.sample_iter(&Standard).take(n).collect()
}
pub fn is_sorted_ascending<T: Ord>(x: &[T]) -> bool {
// windows(2)は元のイテレータから1要素刻みで2要素ずつ値を取り出す
// 新しいイテレータを返す。たとえば元が[1, 2, 3, 4]なら
// [1, 2]、[2, 3], [3, 4]を順に返す
//
// all(..)はイテレータから値(例:[1, 2])を取り出し、クロージャに渡す
// クロージャがfalseを返したら、そこで処理を打ち切りfalseを返す
// クロージャがtrueを返している間は、イテレータから次の値を取り出し
// クロージャへ与え続ける。イテレータの値が尽きるまで(Noneになるまで)
// クロージャが一度もfalseを返さなかったら、all(..)はtrueを返す
x.windows(2).all(|pair| pair[0] <= pair[1])
}
pub fn is_sorted_descending<T: Ord>(x: &[T]) -> bool {
x.windows(2).all(|pair| pair[0] >= pair[1])
}
================================================
FILE: ch03/bitonic-sorter/tests/integration_tests.rs
================================================
use cli_test_dir::*;
// バイナリを実行して入出力を確認するテスト
// `cargo test` で実行できる
#[test]
fn run_benchmark() {
let testdir = TestDir::new("./examples/benchmark", "Run benchmark");
let output = testdir
.cmd()
.arg("20")
// .tee_output() // stdoutとstderrorを順に表示する。デバッグに便利
.expect_success();
use regex::Regex;
let re = Regex::new(
r#"(?m)\Asorting 1048576 integers \(4.0 MB\)
cpu info: \d+ physical cores, \d+ logical cores
seq_sort: sorted 1048576 integers in \d+\.\d+ seconds
par_sort: sorted 1048576 integers in \d+\.\d+ seconds
speed up: \d+\.\d+x
\z"#,
)
.expect("Failed to compile a regex pattern");
assert!(re.is_match(output.stdout_str()));
assert!(output.stderr_str().is_empty());
}
================================================
FILE: ch04/ex04/.gitignore
================================================
# Cargoが選択した依存ライブラリのバージョン郡
Cargo.lock
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch04/ex04/Cargo.toml
================================================
[package]
name = "ex04"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
[dev-dependencies]
cli_test_dir = "0.1"
regex = "1"
================================================
FILE: ch04/ex04/examples/ch04_01_unit.rs
================================================
// 戻り値の型を省略。コンパイラは戻り値がユニット型だと解釈する
fn hello() {
println!("Hello");
}
fn main() {
// 関数を呼び出し、(ないはずの)戻り値に変数retを束縛する
let ret = hello();
// アサーションでretの値がユニット値と等しいことを検査する
assert_eq!(ret, ());
// size_of::<型>()は、その型の値がメモリ上で占める大きさをバイト数で返す
assert_eq!(std::mem::size_of::<()>(), 0); // 0バイト
}
================================================
FILE: ch04/ex04/examples/ch04_02_bool.rs
================================================
#[allow(unused_variables)]
fn main() {
let b1 = true;
let b2 = !b1; // false、否定
let n1 = 8;
let n2 = 12;
let b3 = n1 >= 10; // false
let b4 = n2 >= 10; // true
let b5 = b3 && b4; // false、ショートサーキット論理積
let b6 = b3 || b4; // true、ショートサーキット論理和
assert_eq!(std::mem::size_of::<bool>(), 1); // サイズは1バイト
}
================================================
FILE: ch04/ex04/examples/ch04_03_integer.rs
================================================
#[allow(unused_variables)]
fn main() {
let n1 = 10_000; // i32型(整数リテラルのデフォルトの型)
let n2 = 0u8; // u8型(サフィックスで型を指定)
let n3 = -100_isize; // isize型(同上)
// 型推論が働く例
let n4 = 10; // n4はisize型になる。なぜなら、
let n5 = n3 + n4; // ここでisize型のn3に加算しているから
let h1 = 0xff; // i32型、16進数
let o1 = 0o744; // i32型、8進数
let b1 = 0b1010_0110_1110_1001; // i32型、2進数
let n6 = b'A'; // ASCII文字'A'の文字コード65u8を得る
assert_eq!(n6, 65u8);
}
================================================
FILE: ch04/ex04/examples/ch04_04_overflowed1.rs
================================================
fn main() {
let n1 = std::u8::MAX; // u8型の最大値は255u8
let n2 = 1u8;
// 答えは256だがu8型では表現できない(オーバーフロー)
let n3 = n1 + n2;
println!("{}", n3);
}
================================================
FILE: ch04/ex04/examples/ch04_05_overflowed2.rs
================================================
fn main() {
let n1 = 200u8;
let n2 = 3u8;
// n1 × n2 = 600を計算する
// std::u8::MAXは255なので桁あふれする
// 検査付き乗算 → Noneになる。
assert_eq!(n1.checked_mul(n2), None);
// 飽和乗算 → u8の最大値255にはり付く
assert_eq!(n1.saturating_mul(n2), std::u8::MAX);
// ラッピング乗算 → 600を256で割った余りの88になる
assert_eq!(n1.wrapping_mul(n2), 88);
// 桁あふれ乗算 → 88と桁あふれを示すtrueのペアを返す
assert_eq!(n1.overflowing_mul(n2), (88, true));
}
================================================
FILE: ch04/ex04/examples/ch04_06_float.rs
================================================
#[allow(unused_variables)]
fn main() {
let f1 = 10.0; // f64型(小数リテラルのデフォルトの型)
let f2 = -1_234.56f32; // f32型(サフィックスで型を指定)
let f3 = 578.6E+77; // f64型(指数部も指定できる)
}
================================================
FILE: ch04/ex04/examples/ch04_07_char.rs
================================================
#[allow(unused_variables)]
fn main() {
let c1 = 'A'; // char型
let c2 = 'a';
assert!(c1 < c2); // 文字コード順で大小比較
assert!(c1.is_uppercase()); // 大文字か検査
let c3 = '0';
assert!(c3.is_digit(10)); // 10進数の数字か検査
let c4 = '\t'; // タブ文字
let c5 = '\n'; // 改行(LF)文字
let c6 = '\''; // シングルクオート(')
let c7 = '\\'; // バックスラッシュ(\)
let c8 = '\x7F'; // 制御文字delを8ビットコードで表現(16進数で2桁)
let c9 = '漢'; // ソースコードに直接漢字も書ける(ファイルはUTF-8形式で
// エンコードしておくこと)
let c10 = '\u{5b57}'; // '字'をユニコードのエスケープコードで表現(16進数で最大6桁)
let c11 = '\u{1f600}'; // 絵文字 😀
assert_eq!(std::mem::size_of::<char>(), 4); // サイズは4バイト
}
================================================
FILE: ch04/ex04/examples/ch04_08_reference1.rs
================================================
// 関数f1は呼び出し元の値のコピーを引数nに束縛し、1に変更する
#[allow(unused_assignments)]
fn f1(mut n: u32) {
n = 1;
println!("f1: n = {}", n);
}
// 関数f2は呼び出し元の値を指すポインタを受け取り、ポインタが指す
// 場所に2を格納する
fn f2(n_ptr: &mut u32) {
println!("f2: n_ptr = {:p}", n_ptr);
// *を付けると参照先にアクセスできる。これを参照外し(dereference)と呼ぶ
*n_ptr = 2;
println!("f2: *n_ptr = {}", *n_ptr);
}
fn main() {
let mut n = 0;
println!("main: n = {}", n);
f1(n);
println!("main: n = {}", n);
// `&mut n`でnの値を指す可変のポインタを作成する
f2(&mut n);
println!("main: n = {}", n);
}
================================================
FILE: ch04/ex04/examples/ch04_09_reference2.rs
================================================
fn main() {
let c1 = 'A'; // char型
let c1_ptr = &c1; // &char型。不変の参照(イミュータブルな参照)
assert_eq!(*c1_ptr, 'A');
let mut n1 = 0; // i32型
let n1_ptr = &mut n1; // &mut i32型。可変の参照(ミュータブルな参照)
assert_eq!(*n1_ptr, 0);
// 可変の参照では参照先の値を変更できる
*n1_ptr = 1_000;
assert_eq!(*n1_ptr, 1_000);
}
================================================
FILE: ch04/ex04/examples/ch04_10_raw_pointer.rs
================================================
fn main() {
let c1 = 'A'; // char型
// `&`で参照を作り、型強制で生ポインタに変換する
let c1_ptr: *const char = &c1; // *const char型。不変の生ポインタ
// 生ポインタの参照外しはunsafeな操作
assert_eq!(unsafe { *c1_ptr }, 'A');
let mut n1 = 0; // i32型
let n1_ptr: *mut i32 = &mut n1; // *mut i32型。可変の生ポインタ
assert_eq!(unsafe { *n1_ptr }, 0);
// 可変の生ポインタでは参照先の値を変更できる
unsafe {
*n1_ptr = 1_000;
assert_eq!(*n1_ptr, 1_000);
}
}
================================================
FILE: ch04/ex04/examples/ch04_11_fn_pointer.rs
================================================
// この関数は引数を2倍した値を返す
fn double(n: i32) -> i32 {
n + n
}
// この関数は引数の絶対値を返す
fn abs(n: i32) -> i32 {
if n >= 0 { n } else { -n }
}
fn main() {
// 変数に型注釈として関数ポインタ型を指定することで、関数名から関数ポインタを得られる。
let mut f: fn(i32) -> i32 = double;
assert_eq!(f(-42), -84); // double関数で2倍された
f = abs;
assert_eq!(f(-42), 42); // abs関数で絶対値を得た
// 関数ポインタのサイズはusizeと同じ(x86_64アーキテクチャなら8バイト)
assert_eq!(std::mem::size_of_val(&f), std::mem::size_of::<usize>());
// 変数に型注釈を付けないと関数ポインタ型(fn pointer)ではなく
// 関数定義型(fn item)だと推論される
#[allow(unused_mut)]
let mut f_bad = double;
// 関数定義型は関数ごとに異なる型になるので、変数f_badに別の関数定義型を
// 束縛できない
// f_bad = abs; // 型が合わず、コンパイルエラーになる
// error[E0308]: mismatched types(型の不一致)
// = note: expected type `fn(i32) -> i32 {double}`
// found type `fn(i32) -> i32 {abs}`
// 関数定義型の値のサイズは0バイト
assert_eq!(std::mem::size_of_val(&f_bad), 0);
}
================================================
FILE: ch04/ex04/examples/ch04_12_fn_pointer_vs_closure.rs
================================================
fn main() {
let x = 4; // 変数xを4に束縛する
// クロージャを定義する。するとxがクロージャの環境に捕捉される(キャプチャされる)
let adder = |n| n + x;
assert_eq!(adder(2), 4 + 2);
let mut state = false;
// 別のクロージャを定義する。このクロージャは引数を取らない
let mut flipflop = || {
// stateが補足される
state = !state; // 状態を反転する
state
};
// クロージャを呼ぶたびに返る値が反転する
assert!(flipflop()); // true
assert!(!flipflop()); // false
assert!(flipflop()); // true
// クロージャが返す値だけでなく、stateの値も変化している
assert!(state); // true
let b = 5;
// クロージャは1つ1つが独自の匿名の型を持つため、変数fの型はこのクロージャの匿名型になる
#[allow(unused_mut)]
let mut f = |a| a * 3 + b;
// 別のクロージャでは変数fと型が合わず、コンパイルエラーになる
// f = |a| a * 4 + b;
// → error[E0308]: mismatched types(型の不一致)
// = note: expected type `[closure@src/main.rs:5:17: 5:30 b:_]`
// found type `[closure@src/main.rs:6:9: 6:22 b:_]`
// = note: no two closures, even if identical, have the same type
// (2つのクロージャは、たとえ見た目が同じでも、同じ型を持つことはない)
f(6);
// 環境になにも捕捉しないクロージャは関数ポインタ型になれる
#[allow(unused_mut)]
let mut f: fn(i32) -> i32 = |n| n * 3;
assert_eq!(f(-42), -126);
// 環境になにかを捕捉するクロージャは関数ポインタ型になれない
#[allow(unused_variables)]
let x = 4;
// f = |n| n * x; // xを捕捉している
// → error[E0308]: mismatched types
// expected fn pointer, found closure
// (関数ポインタを期待しているのに、クロージャが見つかった)
let v = vec!["I", "love", "Rust!"]
.into_iter()
// .map(|s| s.len()) // &str型の引数sを取るクロージャ
// len()メソッドは&str型の引数を1つだけ取るので、len()メソッドへの
// 関数ポインタでも型が一致する
.map(str::len)
.collect::<Vec<_>>();
assert_eq!(v, vec![1, 4, 5]);
}
================================================
FILE: ch04/ex04/examples/ch04_13_tuple.rs
================================================
fn main() {
let t1 = (88, true);
// フィールド0の要素(左から数えて最初の要素)を取り出す
assert_eq!(t1.0, 88);
// フィールド1の要素(2番目の要素)を取り出す
assert_eq!(t1.1, true);
// フィールド名にはコンパイル時の定数のみ使える。変数は不可
#[allow(unused_variables)]
let i = 0;
// let t1a = t1.i;
// → コンパイルエラー
// no field `i` on type `({integer}, bool)`
// `({整数}, bool)`型には`i`という名のフィールドはありません
// 要素を書き換えるので、変数t1に`mut`を付けて可変にする
let mut t1 = (88, true);
// フィールド0の要素を書き換える
t1.0 += 100; // 現在の値に100を足す
assert_eq!(t1, (188, true));
let (n1, b1) = (88, true);
assert_eq!(n1, 88);
assert_eq!(b1, true);
let ((x1, y1), (x2, y2)) = ((0, 5), (10, -1));
assert_eq!(x1, 0);
assert_eq!(y1, 5);
assert_eq!(x2, 10);
assert_eq!(y2, -1);
// 不要な値はアンダースコアを使うと無視できる
#[allow(unused_variables)]
let ((x1, y1), _) = ((0, 5), (10, -1));
// 要素を書き換えるので、変数t1に`mut`を付けて可変にする
let mut t1 = ((0, 5), (10, -1));
// 要素を指す可変の参照を得るためにref mutを追加する
let ((ref mut x1_ptr, ref mut y1_ptr), _) = t1;
// *を付けることでポインタが指すアドレスにあるデータにアクセスできる
*x1_ptr += 3;
*y1_ptr *= -1;
assert_eq!(t1, ((3, -5), (10, -1)));
}
================================================
FILE: ch04/ex04/examples/ch04_14_array.rs
================================================
fn main() {
#[allow(unused_variables)]
let a1 = [false, true, false]; // [bool; 3]型
let a2 = [0.0, -1.0, 1.0, 0.5]; // [f64; 4]型
// `len()`で配列の長さを得られる
assert_eq!(a2.len(), 4);
// 長さ100の配列を作り、全要素を0i32で初期化する
// (要素の型はCopyトレイトを実装していなければならない)
let a3 = [0; 100]; // [i32; 100]型
assert_eq!(a3.len(), 100);
// 配列は入れ子にできる
#[allow(unused_variables)]
let a4 = [['a', 'b'], ['c', 'd']]; // [[char; 2]; 2]型。2次元配列
// 配列は同じ型の要素の並び。異なる型の要素は持てない
// let a5 = [false, 'a'];
// → コンパイルエラー E0308:型の不一致。boolでなくcharがある
// 配列の長さは実行時に指定できない
let size = 100;
// let a1 = [0; size];
// → コンパイルエラー E0435:
// コンパイル時定数が要求される場所に定数でない値がある
// ベクタなら実行時に長さを指定できる
let mut v1 = vec![0; size]; // Vec<i32>型
assert_eq!(v1.len(), 100); // 長さは100
// ベクタには要素を追加したり、削除したりできる。
v1.push(1); // ベクタの最後尾に要素を追加する
assert_eq!(v1.len(), 101); // 長さは101になる
assert_eq!(v1.pop(), Some(1)); // ベクタの最後尾から要素を取り除く
assert_eq!(v1.len(), 100); // 長さは100に戻る
let array1 = ['H', 'e', 'l', 'l', 'o'];
assert_eq!(array1[1], 'e');
let mut array2 = [0, 1, 2];
array2[1] = 10;
assert_eq!(array2, [0, 10, 2]);
// インデックスは定数でなくてもかまわない
let mut index = 0;
assert_eq!(array2[index], 0);
index += 1;
assert_eq!(array2[index], 10);
let array3 = [0, 1];
// array3[2];
// → コンパイルエラー:インデックスが範囲外。長さは2だがインデックスが2である
#[allow(unused_variables)]
let index = 2;
// array3[index];
// → コンパイルエラーにはならずにパニックする
assert_eq!(array3.get(1), Some(&1)); // get()はインデックスが範囲内の時はSome(&値)を返す
assert_eq!(array3.get(2), None); // さもなければNoneを返す
let array4 = ['a'; 50]; // 長さ50
// iter()で要素が不変のイテレータを作成
for ch in array4.iter() {
print!("{},", *ch);
}
let mut array5 = [1; 50]; // 長さ50
// iter_mut()で要素が可変のイテレータを作成
for n in array5.iter_mut() {
*n *= 2;
}
}
================================================
FILE: ch04/ex04/examples/ch04_15_slice1.rs
================================================
// この関数は&[char]型のスライスを引数に取り、その情報を表示する
fn print_info(name: &str, sl: &[char]) {
println!(
" {:9} - {}, {:?}, {:?}, {:?}",
name,
sl.len(), // 長さ(バイト数) usize型
sl.first(), // 最初の要素 Option<char>型
sl[1], // 2番目の要素 char型
sl.last() // 最後の要素 Option<char>型
);
}
fn main() {
// 配列
let a1 = ['a', 'b', 'c', 'd']; // 参照元のデータ。[char; 4]型
println!("a1: {:?}", a1);
print_info("&a1[..]", &a1[..]); // &[char]型。全要素のスライス
print_info("&a1", &a1); // 同上
print_info("&a1[1..3]", &a1[1..3]); // 'b'と'c'を要素とする長さ2のスライス
// ベクタ
let v1 = vec!['e', 'f', 'g', 'h']; // 参照元のデータ。Vec<char>型
println!("\nv1: {:?}", v1);
print_info("&v1[..]", &v1[..]); // &[char]型。全要素のスライス
print_info("&v1", &v1); // 同上
print_info("&v1[1..3]", &v1[1..3]); // &[char]型。'f'と'g'を要素とする長さ2のスライス
}
================================================
FILE: ch04/ex04/examples/ch04_16_slice2.rs
================================================
fn main() {
let mut a1 = [5, 4, 3, 2]; // 配列。[i32; 4]型
let s1 = &mut a1[1..3]; // 可変のスライス。&mut[i32]型
s1[0] = 6; // スライスの最初の要素を6にする
s1[1] *= 10; // 2番目の要素を10倍する
s1.swap(0, 1); // 要素を交換する
assert_eq!(s1, [30, 6]); // スライスの内容を確認
// 参照元の配列の内容を確認
assert_eq!(a1, [5, 30, 6, 2]); // スライスを通じて配列の内容が変更された
let a2: [i32; 0] = [];
let s2 = &a2; // 不変のスライスを作成
assert!(s2.is_empty()); // 空のスライス
assert_eq!(s2.len(), 0); // 長さは0
assert_eq!(s2.first(), None); // 最初の要素は存在しない
let a3 = ["zero", "one", "two", "three", "four"];
let s3 = &a3[1..4]; // 不変のスライスを作成
assert!(!s3.is_empty()); // 空ではない
assert_eq!(s3.len(), 3); // 長さは3
assert_eq!(s3.first(), Some(&"one")); // 最初の要素
assert_eq!(s3[1], "two"); // 2番目の要素
// assert_eq!(s3[3], "?"); // 4番目の要素。存在しないのでpanicする
assert_eq!(s3.get(1), Some(&"two")); // 2番目の要素を得る別の方法
assert_eq!(s3.get(3), None); // 4番目の要素。存在しないのでNone
assert!(s3.contains(&"two")); // "two"を要素に持つ
assert!(s3.starts_with(&["one", "two"])); // "one", "two"で始まる
assert!(s3.ends_with(&["two", "three"])); // "two", "three"で終わる
let mut a4 = [6, 4, 2, 8, 0, 9, 4, 3, 7, 5, 1, 7];
// 一部の要素を昇順にソートする
&mut a4[2..6].sort();
assert_eq!(&a4[2..6], &[0, 2, 8, 9]);
// スライスを2つの可変スライスへ分割する
#[allow(unused_variables)]
let (s4a, s4b) = &mut a4.split_at_mut(5);
// &mutを省略しても結果は同じ。型強制によって自動的にスライスが作られる
a4[2..6].sort();
let (s4a, s4b) = a4.split_at_mut(5);
// 前半を逆順にする
s4a.reverse();
assert_eq!(s4a, &[8, 2, 0, 4, 6]);
// 後半を昇順にソートする
s4b.sort_unstable();
assert_eq!(s4b, &[1, 3, 4, 5, 7, 7, 9]);
// sort()とsort_unstable()の違い
// sort()は安定ソートなので同順なデータのソート前の順序がソート後も保存される
// soft_unstable()は安定ソートではないが、一般的にsort()より高速
}
================================================
FILE: ch04/ex04/examples/ch04_17_str.rs
================================================
fn main() {
let s1 = "abc1"; // &'static str型
let s2 = "abc2";
assert!(s1 < s2);
assert!(s1 != s2);
let s3 = "文字列を複数行に渡って書くと
改行やスペースが入る";
let s4 = "行末にバックスラッシュを付けると\
改行などが入らない";
assert_eq!(s3, "文字列を複数行に渡って書くと\n 改行やスペースが入る");
assert_eq!(s4, "行末にバックスラッシュを付けると改行などが入らない");
let s5 = "文字列に\"と\\を含める"; // バックスラッシュでエスケープ
let s6 = r#"文字列に"と\を含める"#; // raw文字列リテラル。正規表現などに便利
assert_eq!(s5, s6);
let s7 = r###"このように#の数を増やすと"##"があっても大丈夫"###;
assert_eq!(s7, "このように#の数を増やすと\"##\"があっても大丈夫");
#[allow(unused_variables)]
let s8 = "もちろん絵文字\u{1f600}も使える"; // もちろん絵文字😀も使える
let fruits = "あかりんご, あおりんご\nラズベリー, ブラックベリー";
// lines()メソッドは改行コード(\n)を含む文字列から1行ずつ
// 取り出せるイテレータを作る
let mut lines = fruits.lines();
// イテレータのnext()メソッドで次の行を得る
let apple_line = lines.next();
assert_eq!(apple_line, Some("あかりんご, あおりんご"));
assert_eq!(lines.next(), Some("ラズベリー, ブラックベリー"));
// 次の行がないならNoneが返る。
assert_eq!(lines.next(), None);
// りんごの行(Some(..))の中身を取り出す
if let Some(apples) = apple_line {
// 「あか」で始まるかチェック
assert!(apples.starts_with("あか"));
// 「りんご」の文字を含むかチェック
assert!(apples.contains("りんご"));
// 「あお」が最初に出現する位置(UTF-8表現で何バイト目)を得る
assert_eq!(apples.find("あお"), Some(17)); // 0始まりなので18バイト目
// 文字列をカンマ(,)で分割するイテレータを作る
let mut apple_iter = apples.split(",");
assert_eq!(apple_iter.next(), Some("あかりんご"));
let green = apple_iter.next();
// 左側に余白がある。
assert_eq!(green, Some(" あおりんご"));
// Some(..)の内容にstrのtrim()メソッドを適用して余白を取り除く
assert_eq!(green.map(str::trim), Some("あおりんご"));
assert_eq!(apple_iter.next(), None);
} else {
unreachable!(); // もしここに到達したらパニックで強制終了する
}
// s1からs4はどれも画面上では1文字として表示される
// UTF-8表現
let s1 = "a"; // 61
let s2 = "あ"; // E3 81 82
let s3 = "😀"; // F0 9F 98 80
let s4 = "🇯🇵"; // F0 9F 87 AF F0 9F 87 B5
// len()メソッドはUTF-8のバイト数を返す
assert_eq!(s1.len(), 1);
assert_eq!(s2.len(), 3);
assert_eq!(s3.len(), 4);
assert_eq!(s4.len(), 8);
let s = "abcあいう";
assert_eq!(s.get(0..1), Some("a"));
assert_eq!(s.get(3..6), Some("あ"));
assert_eq!(s.get(3..4), None); // UTF-8として解釈できない場合
let s = "かか\u{3099}く"; // \u{3099}は濁点文字
println!("{}", s); // かがく
let mut iter = s.chars();
assert_eq!(iter.next(), Some('か'));
assert_eq!(iter.next(), Some('か'));
assert_eq!(iter.next(), Some('\u{3099}'));
assert_eq!(iter.next(), Some('く'));
assert_eq!(iter.next(), None);
let utf8: [u8; 4] = [0x61, 0xe3, 0x81, 0x82];
assert_eq!(std::str::from_utf8(&utf8), Ok("aあ"));
let bad_utf8: [u8; 2] = [0x81, 0x33]; // でたらめなバイト列
let result2 = std::str::from_utf8(&bad_utf8);
assert!(result2.is_err());
println!("{:?}", result2);
// → "Err(Utf8Error { valid_up_to: 0, error_len: Some(1) })"
// 文字列リテラル(&'static str)から&mut strは直接は得られない
// まず文字列リテラルをStringへ変換し、そこから&mut strを取り出す
let mut s1 = "abcあいう".to_string(); // String型
// &mut strを得る。これはStringが持つUTF-8バイト列を指す可変スライス
let s2 = s1.as_mut_str(); // &mut str型
// 英小文字を大文字に変更
s2.make_ascii_uppercase();
assert_eq!(s2, "ABCあいう");
// &mut strのUTF-8バイト列を直接操作して"あ"(3バイト)を"*a*"に変更する
let b = unsafe { s2.as_bytes_mut() };
b[3] = b'*';
b[4] = b'a';
b[5] = b'*';
// 大元のStringが変更されている
assert_eq!(s1, "ABC*a*いう");
}
================================================
FILE: ch04/ex04/tests/integration_tests.rs
================================================
use cli_test_dir::*;
// バイナリを実行して入出力を確認するテスト
// `cargo test` で実行できる
#[test]
fn run_ch04_01() {
let testdir = TestDir::new("./examples/ch04_01_unit", "Run ch04_01");
let output = testdir.cmd().expect_success();
assert_eq!(output.stdout_str(), "Hello\n");
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_02() {
let testdir = TestDir::new("./examples/ch04_02_bool", "Run ch04_02");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_03() {
let testdir = TestDir::new("./examples/ch04_03_integer", "Run ch04_03");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_04() {
let testdir = TestDir::new("./examples/ch04_04_overflowed1", "Run ch04_04");
let output = testdir
.cmd()
// .tee_output()
.expect_failure(); // debugビルドではpanicする
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().contains("thread 'main' panicked at 'attempt to add with overflow', examples/ch04_04_overflowed1.rs:"));
}
#[test]
fn run_ch04_05() {
let testdir = TestDir::new("./examples/ch04_05_overflowed2", "Run ch04_05");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_06() {
let testdir = TestDir::new("./examples/ch04_06_float", "Run ch04_06");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_07() {
let testdir = TestDir::new("./examples/ch04_07_char", "Run ch04_07");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_08() {
let testdir = TestDir::new("./examples/ch04_08_reference1", "Run ch04_08");
let output = testdir.cmd().expect_success();
use regex::Regex;
let re = Regex::new(
r#"(?m)\Amain: n = 0
f1: n = 1
main: n = 0
f2: n_ptr = 0x[0-9a-f]+
f2: \*n_ptr = 2
main: n = 2
\z"#,
)
.expect("Failed to compile a regex pattern");
assert!(re.is_match(output.stdout_str()));
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_09() {
let testdir = TestDir::new("./examples/ch04_09_reference2", "Run ch04_09");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_10() {
let testdir = TestDir::new("./examples/ch04_10_raw_pointer", "Run ch04_10");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_11() {
let testdir = TestDir::new("./examples/ch04_11_fn_pointer", "Run ch04_11");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_12() {
let testdir = TestDir::new("./examples/ch04_12_fn_pointer_vs_closure", "Run ch04_12");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_13() {
let testdir = TestDir::new("./examples/ch04_13_tuple", "Run ch04_13");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_14() {
let testdir = TestDir::new("./examples/ch04_14_array", "Run ch04_14");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
"a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,",
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_15() {
let testdir = TestDir::new("./examples/ch04_15_slice1", "Run ch04_15");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
r#"a1: ['a', 'b', 'c', 'd']
&a1[..] - 4, Some('a'), 'b', Some('d')
&a1 - 4, Some('a'), 'b', Some('d')
&a1[1..3] - 2, Some('b'), 'c', Some('c')
v1: ['e', 'f', 'g', 'h']
&v1[..] - 4, Some('e'), 'f', Some('h')
&v1 - 4, Some('e'), 'f', Some('h')
&v1[1..3] - 2, Some('f'), 'g', Some('g')
"#
);
assert!(output.stderr_str().is_empty());
}
/*
*/
#[test]
fn run_ch04_16() {
let testdir = TestDir::new("./examples/ch04_16_slice2", "Run ch04_16");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch04_17() {
let testdir = TestDir::new("./examples/ch04_17_str", "Run ch04_17");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
"かか\u{3099}く\nErr(Utf8Error { valid_up_to: 0, error_len: Some(1) })\n",
);
assert!(output.stderr_str().is_empty());
}
================================================
FILE: ch05/ex05/.gitignore
================================================
# Cargoが選択した依存ライブラリのバージョン郡
Cargo.lock
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch05/ex05/Cargo.toml
================================================
[package]
name = "ex05"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
[dev-dependencies]
cli_test_dir = "0.1"
regex = "1"
================================================
FILE: ch05/ex05/examples/ch05_01_box.rs
================================================
fn main() {
let t1 = (5, "birds".to_string()); // (i32, String)型のタプル。スタックに置かれる
let mut b1 = Box::new(t1); // Boxポインタを作る。タプルがヒープに移動する
(*b1).0 += 1; // *で参照外し
assert_eq!(*b1, (6, "birds".to_string()));
// Box::new()の実行後にt1にアクセスしようとするとコンパイルエラーになる
// println!("{:?}", t1);
// → error[E0382]: borrow of moved value: `t1`
}
================================================
FILE: ch05/ex05/examples/ch05_02_vec.rs
================================================
fn main() {
#[allow(unused_variables)]
let v1 = vec![false, true, false]; // Vec<bool>型
let v2 = vec![0.0, -1.0, 1.0, 0.5]; // Vec<f64>型
assert_eq!(v2.len(), 4); // v2ベクタの長さは4
// 長さ100のベクタを作り、全要素を0i32で初期化する
// (要素の型はCloneトレイトを実装していなければならない)
let v3 = vec![0; 100]; // Vec<i32>型
assert_eq!(v3.len(), 100);
// ベクタは入れ子にできる。子の要素数はそれぞれが異なってもかまわない
#[allow(unused_variables)]
let v4 = vec![vec!['a', 'b', 'c'], vec!['d']]; // Vec<Vec<char>>型
// ベクタは同じ型の要素の並び。異なる型の要素は持てない
// let v5 = vec![false, 'a'];
// → error[E0308]: mismatched types
let mut v6 = vec!['a', 'b', 'c']; // Vec<char>型
v6.push('d'); // 最後尾に値を追加
v6.push('e');
assert_eq!(v6, ['a', 'b', 'c', 'd', 'e']); // v6の現在の値
assert_eq!(v6.pop(), Some('e')); // 最後尾から値を取り出し
v6.insert(1, 'f'); // インデックス1の位置に要素を挿入
assert_eq!(v6.remove(2), 'b'); // インデックス2の要素を削除。返り値は削除した値
assert_eq!(v6, ['a', 'f', 'c', 'd']); // v6の現在の値
let mut v7 = vec!['g', 'h']; // 別のベクタv7を作成
v6.append(&mut v7); // v6の最後尾にv7の全要素を追加
assert_eq!(v6, ['a', 'f', 'c', 'd', 'g', 'h']);
assert_eq!(v7, []); // v7は空になった(全要素がv6へ移動した)
let a8 = ['i', 'j']; // 固定長配列a8を作成
v6.extend_from_slice(&a8); // v6の最後尾にa8の全要素を追加
assert_eq!(v6, ['a', 'f', 'c', 'd', 'g', 'h', 'i', 'j']);
assert_eq!(a8, ['i', 'j']); // a8は変更なし(a8の要素がコピーされた)
}
================================================
FILE: ch05/ex05/examples/ch05_03_boxed_slice.rs
================================================
fn main() {
// 4要素のベクタVec<i32>を作り、要素を1つ足して5要素に拡張する
let mut v1 = vec![0, 1, 2, 3];
v1.push(4);
println!("v1 len: {}, capacity: {}", v1.len(), v1.capacity());
// →「v1 len: 5, capacity: 8」と表示される。5要素だが8要素分のメモリを確保している
// Box<[i32]>に変換する。余分なメモリを持たなくするためにVecのshrink_to_fit()
// メソッドが実行されてからBox化される
let s1 = v1.into_boxed_slice();
// 余分なメモリを持ってないことを確認するためにVec<i32>に戻す
let v2 = s1.into_vec();
println!("v1 len: {}, capacity: {}", v2.len(), v2.capacity());
// →「v2 len: 5, capacity: 5」と表示される。5要素ぴったりのメモリを確保していることが分かる
}
================================================
FILE: ch05/ex05/examples/ch05_04_hash_map.rs
================================================
fn main() {
use std::collections::HashMap;
let mut m1 = HashMap::new(); // またはwith_capacity(要素数)
// 要素を2つ追加する
m1.insert("a", 1); // キー:"a"、バリュー:1
m1.insert("b", 3);
assert_eq!(m1.len(), 2); // 要素数は2
// キーに対応する値を取り出す
assert_eq!(m1.get("b"), Some(&3));
assert_eq!(m1.get("c"), None); // キーが存在しないのでNone
// "d"が存在するならその値への参照を得る。存在しないなら"d"に対して0を登録してから参照を返す
let d = m1.entry("d").or_insert(0);
*d += 7;
assert_eq!(m1.get("d"), Some(&7));
#[allow(unused_variables)]
let m2 = vec![("a", 1), ("b", 3)].into_iter().collect::<HashMap<_, _>>();
}
================================================
FILE: ch05/ex05/examples/ch05_05_string1.rs
================================================
fn main() {
// strリテラルからStringを作る。どちらの方法でも結果は同じ
let mut s1 = "ラズベリー".to_string();
let mut s2 = String::from("ブラックベリー");
// Rust 1.19より前のバージョンでは性能上の理由からto_string()よりも
// to_owned()が推奨されていた。現在のバージョンではそのような配慮は不要
let s3 = "ストロベリー".to_owned();
s1.push_str("タルト"); // String型の文字列にに&str型の文字列を追加
assert_eq!(s1, "ラズベリータルト");
s2.push('と'); // Stringにcharを追加する
// push_str()が受け付けるのは&str型のみ。以下はコンパイルエラーになる
// s2.push_str(s3); // s3はString型
// → error[E0308]: mismatched types
// expected &str, found struct `std::string::String`
// &を付けると型強制というしくみによって&Stringから&strへ変換される
s2.push_str(&s3);
assert_eq!(s2, "ブラックベリーとストロベリー");
}
================================================
FILE: ch05/ex05/examples/ch05_06_string2.rs
================================================
fn main() {
let i = 42; // i32型
assert_eq!(i.to_string(), "42");
let f = 4.3 + 0.1; // f64型
assert_eq!(f.to_string(), "4.3999999999999995");
assert_eq!(format!("{:.2}", f), "4.40"); // format!マクロが便利
let t = (1, "ABC");
// 2要素のタプル型はDebugトレイトを実装しているのでformat!マクロで変換できる
assert_eq!(format!("{:?}", t), r#"(1, "ABC")"#);
let s1 = "42";
assert_eq!(s1.parse::<i32>(), Ok(42)); // &str型からi32型へ変換
let s2 = "abc";
let r2: Result<f64, _> = s2.parse(); // 変数の型から型推論できるならparseの型パラメータは不要
assert!(r2.is_err()); // 数値として解釈できないときはエラーが返る
println!("{:?}", r2); // → Err(ParseFloatError { kind: Invalid })
let cs = ['t', 'r', 'u', 's', 't']; // [char; 5]型
assert_eq!(cs.iter().collect::<String>(), "trust");
assert_eq!(&cs[1..].iter().collect::<String>(), "rust" );
let bad_utf8: [u8; 7] = [
b'a', // a
0xf0, 0x90, 0x80, // でたらめなバイト列
0xe3, 0x81, 0x82, // あ
];
// 不正なバイト列はUnicodeのU+FFFD Replacement Characterに置き換わる
let s = String::from_utf8_lossy(&bad_utf8);
assert_eq!(s, "a\u{fffd}あ");
}
================================================
FILE: ch05/ex05/examples/ch05_07_string3.rs
================================================
// この関数は引数として&str型の名前を取り、&str型の"Hello, 名前!"を返す
// fn f1(name: &str) -> &str {
// let s = format!("Hello, {}!", name); // format!はStringを作る
// &s // Stringから&strを作成し、戻り値として返す
// // → コンパイルエラー:`s` does not live long enough.(sの生存期間が不十分)
// }
// この関数は引数として&str型の名前を取り、String型の"Hello, 名前!"を返す
fn f1(name: &str) -> String {
format!("Hello, {}!", name)
}
fn main() {
f1("ken");
}
================================================
FILE: ch05/ex05/examples/ch05_08_string4.rs
================================================
fn main() {
let utf16: Vec<u16> = vec![0x61, 0x62, 0x6f22, 0x5b57];
// Vec<u16>の値をUTF-16と解釈しStringを作成する(UTF-8へ変換される)
if let Ok(s) = String::from_utf16(&utf16) {
assert_eq!(s, "ab漢字");
} else {
unreachable!();
}
// バイト文字列リテラル。ASCII文字以外のバイトは「\x2桁の16進数」で記述する
let bs1 = b"abc\xe3\x81\x82"; // &[u8; 6]型。UTF-8表現で"abcあ"
assert_eq!(bs1, &[b'a', b'b', b'c', 0xe3, 0x81, 0x82]);
// rawバイト文字列リテラル。エスケープ文字(\)を特別扱いしないので、\nは
// 改行文字ではなく文字どおり\nと解釈される。
let bs2 = br#"ab\ncd"#; // &[u8; 6]型
assert_eq!(bs2, &[b'a', b'b', b'\\', b'n', b'c', b'd']);
}
================================================
FILE: ch05/ex05/examples/ch05_09_range.rs
================================================
fn main() {
let a = ['a', 'b', 'c', 'd', 'e'];
// 糖衣構文と実際の範囲の対応
assert_eq!(a[ .. ], ['a', 'b', 'c', 'd', 'e'] );
assert_eq!(a[ .. 3], ['a', 'b', 'c', ] );
assert_eq!(a[ ..=3], ['a', 'b', 'c', 'd' ] );
assert_eq!(a[1.. ], [ 'b', 'c', 'd', 'e'] );
assert_eq!(a[1.. 3], [ 'b', 'c' ] );
assert_eq!(a[1..=3], [ 'b', 'c', 'd' ] );
// 糖衣構文とRange*型の対応
assert_eq!( .. , std::ops::RangeFull );
assert_eq!( .. 3 , std::ops::RangeTo { end: 3 } );
assert_eq!( ..=3 , std::ops::RangeToInclusive { end: 3 } );
assert_eq!( 1.. , std::ops::RangeFrom { start: 1 } );
assert_eq!( 1.. 3 , std::ops::Range { start: 1, end: 3 } );
assert_eq!( 1..=3 , std::ops::RangeInclusive::new(1, 3) );
}
================================================
FILE: ch05/ex05/examples/ch05_10_option.rs
================================================
fn main() {
let a1 = ['a', 'b', 'c', 'd'];
assert_eq!(a1.get(0), Some(&'a')); // インデックス0は配列a1の範囲内なので`Some(&値)`が返る
assert_eq!(a1.get(4), None); // インデックス4は範囲外なので`None`が返る
let mut o1 = Some(10); // Option<i32>型
match o1 { // match式でバリアントが判別できる
Some(s) => assert_eq!(s, 10), // パターンマッチで中の値を取り出す
None => unreachable!(),
}
o1 = Some(20);
if let Some(s) = o1 { // if let式でもバリアントの判別と値の取り出しができる
assert_eq!(s, 20);
}
let mut o2 = Some(String::from("Hello")); // Option<String>型
assert_eq!(o2.unwrap(), "Hello"); // unwrap()でSomeの中の値が取り出せる
// しかしunwrap()はNoneのときにpanicするので、できるだけ使わない方がいい
o2 = None;
// o2.unwrap();
// → thread 'main' panicked at 'called `Option::unwrap()` on a `None` value'
// unwrap_or_else()ならNoneでもpanicしないので安心して使える
// Noneのときはクロージャを実行し、Noneの代わりになる値を得る
assert_eq!(o2.unwrap_or_else(|| String::from("o2 is none")), "o2 is none");
// Someで包まれた値を操作するならmap()やand_then()などのコンビネータが便利
// map()はSome(値)のときは値にクロージャを適用し、クロージャが返した値をSomeで包み直す
let mut o3 = Some(25);
assert_eq!(o3.map(|n| n * 10), Some(250));
// NoneならなにもせずNoneを返す
o3 = None;
assert_eq!(o3.map(|n| n * 10), None);
o3 = Some(10);
assert_eq!(
o3.map(|n| n * 10)
// and_then()はSome(値)のときは値にクロージャを適用し
// クロージャが返した値(Some(新しい値)、または、None)をそのまま返す
.and_then(|n| if n >= 200 { Some(n) } else { None }),
None
);
// インデックス0と3の両方に値があるので、それらの合計がSomeで包まれて返される
assert_eq!(add_elems(&[3, 7, 31, 127]), Some(3 + 127));
// インデックス3がないのでNoneが返される
assert_eq!(add_elems(&[7, 11]), None);
}
fn add_elems(s: &[i32]) -> Option<i32> {
// 複数のOption値を扱うときは?演算子が便利
// Some(値)なら値を取り出し、Noneならこの関数からすぐに戻る(Noneを返す)
let s0 = s.get(0)?;
let s3 = s.get(3)?;
Some(s0 + s3)
}
================================================
FILE: ch05/ex05/examples/ch05_11_result.rs
================================================
fn main() {
// str::parse()は文字列を指定した型(ここではi32型)に変換する
assert_eq!("10".parse::<i32>(), Ok(10)); // 変換できたらOK(値)が返される
let res0 = "a".parse::<i32>(); // 変換できなかったら`Err(エラーを表す値)`が返される
assert!(res0.is_err());
println!("{:?}", res0); // → Err(ParseIntError { kind: InvalidDigit })
// 複数のResult値を扱うときは?演算子が便利
// Ok(値)なら値を取り出し、Err(エラーを表す値)ならこの関数からリターンする
fn add0(s0: &str, s1: &str) -> Result<i32, std::num::ParseIntError> {
let s0 = s0.parse::<i32>()?;
let s1 = s1.parse::<i32>()?;
Ok(s0 + s1)
}
assert_eq!(add0("3", "127"), Ok(3 + 127));
assert!(add0("3", "abc").is_err());
// map_err()コンビネータを使うとErr(エラーを表す値)のときに別のエラーに変換できる
fn add1(s0: &str, s1: &str) -> Result<i32, String> {
let s0 = s0.parse::<i32>().map_err(|_e| "s0が整数ではありません")?;
let s1 = s1.parse::<i32>().map_err(|_e| "s1が整数ではありません")?;
Ok(s0 + s1)
}
assert_eq!(add1("3", "abc"), Err("s1が整数ではありません".to_string()));
}
================================================
FILE: ch05/ex05/examples/ch05_12_type_alias.rs
================================================
type UserName = String;
type Id = i64;
type Timestamp = i64;
type User = (Id, UserName, Timestamp);
fn new_user(name: UserName, id: Id, created: Timestamp) -> User {
(id, name, created)
}
fn main() {
let id = 400;
let now = 4567890123;
#[allow(unused_variables)]
let user = new_user(String::from("mika"), id, now);
// IdとTimestampは同じi64型なので、間違えてもエラーにならない
#[allow(unused_variables)]
let bad_user = new_user(String::from("kazuki"), now, id); // nowとidが逆
}
================================================
FILE: ch05/ex05/examples/ch05_13_struct1.rs
================================================
struct Polygon {
vertexes: Vec<(i32, i32)>, // 頂点の座標
#[allow(dead_code)]
stroke_width: u8, // 輪郭の太さ
fill: (u8, u8, u8), // 塗りつぶしのRGB色
}
// フィールド名と同じ名前の関数引数やローカル変数がある時は以下のような
// 省略形も使える(Rust 1.17以降)
fn new_polygon(vertexes: Vec<(i32, i32)>) -> Polygon {
let stroke_width = 1;
let fill = (0, 0, 0);
Polygon { vertexes, stroke_width, fill }
}
fn main() {
// Polygon型の値を作り、変数triangleを束縛する
let triangle = Polygon {
vertexes: vec![(0, 0), (3, 0), (2, 2)],
fill: (255, 255, 255),
stroke_width: 1,
};
let quadrangle = new_polygon(vec![(5, 2), (4, 7), (10, 6), (8, 1)]);
// フィールド名でアクセス
assert_eq!(triangle.vertexes[0], (0, 0));
assert_eq!(triangle.vertexes.len(), 3);
assert_eq!(triangle.fill, (255, 255, 255));
// パターンマッチでアクセス。不要なフィールドは..で省略できる
let Polygon { vertexes: quad_vx, .. } = quadrangle;
assert_eq!(4, quad_vx.len());
// :以降を省略すると、フィールドと同じ名前の変数が作られフィールド値に束縛される
let Polygon { fill, .. } = quadrangle;
assert_eq!((0, 0, 0), fill);
// 構造体の値を変更するにはmutが必要
let mut polygon = new_polygon(vec![(-1, -5), (-4, 0)]);
assert_eq!(polygon.vertexes.len(), 2);
polygon.vertexes.push((2, 8));
assert_eq!(polygon.vertexes.len(), 3);
let triangle1 = Polygon {
vertexes: vec![(0, 0), (3, 0), (2, 2)],
fill: (255, 255, 255),
stroke_width: 5,
};
// triangle1を元にvertexesだけ異なる新しい値を作る
#[allow(unused_variables)]
let triangle2 = Polygon {
vertexes: vec![(0, 0), (-3, 0), (-2, 2)],
.. triangle1
};
}
================================================
FILE: ch05/ex05/examples/ch05_14_struct2.rs
================================================
#[allow(dead_code)]
// #[derive(Default)]
struct Polygon {
vertexes: Vec<(i32, i32)>,
stroke_width: u8,
fill: (u8, u8, u8),
}
impl Default for Polygon {
fn default() -> Self {
Self {
stroke_width: 1, // デフォルト値を1にする
vertexes: Default::default(), // Vec<(i32, i32)>のDefault実装を使う
fill: Default::default(), // (u8, u8, u8)のDefault実装を使う
}
}
}
fn main() {
// すべてのフィールドがデフォルト値を持つPolygonを作成する
#[allow(unused_variables)]
let polygon1: Polygon = Default::default();
// vertexesフィールドだけ別の値に設定し、他はデフォルト値にする
#[allow(unused_variables)]
let polygon2 = Polygon {
vertexes: vec![(0, 0), (3, 0), (2, 2)],
.. Default::default()
};
}
================================================
FILE: ch05/ex05/examples/ch05_15_struct3.rs
================================================
struct Triangle(Vertex, Vertex, Vertex);
struct Vertex(i32, i32);
fn main() {
let vx0 = Vertex(0, 0);
let vx1 = Vertex(3, 0);
let triangle = Triangle(vx0, vx1, Vertex(2, 2));
assert_eq!((triangle.1).0, 3);
}
================================================
FILE: ch05/ex05/examples/ch05_16_struct4.rs
================================================
struct UserName(String);
struct Id(u64);
struct Timestamp(u64);
type User = (Id, UserName, Timestamp);
#[allow(dead_code)]
fn new_user(name: UserName, id: Id, created: Timestamp) -> User {
(id, name, created)
}
#[allow(unused_variables)]
fn main() {
let id = Id(400);
let now = Timestamp(4567890123);
// nowとidの順番を間違えるとコンパイルエラーになってくれる
// let bad_user = new_user(UserName(String::from("kazuki")), now, id);
// error[E0308]: mismatched types
// expected type `Id`, found type `Timestamp`
}
================================================
FILE: ch05/ex05/examples/ch05_17_struct5.rs
================================================
#[derive(Debug, PartialEq)]
struct UniqueValue;
// 以下の形式も可能
// struct UniqueValue {}
// struct UniqueValue();
fn main() {
// フィールドがないので作れる値は1つのみ
let uv1 = UniqueValue;
let uv2 = UniqueValue;
assert_eq!(uv1, uv2);
}
================================================
FILE: ch05/ex05/examples/ch05_18_enum1.rs
================================================
// 平日を表すWeekday型を定義する
// Debugトレイトを自動導出すると"{:?}"で表示できるようになる
// PartialEqトレイトを自動導出すると==演算子が使えるようになる
#[allow(dead_code)]
#[derive(Debug, PartialEq)]
enum Weekday {
// Weekday型には以下のバリアントがある。
Monday, Tuesday, Wednesday, Thursday, Friday,
}
// 月を表すMonth型を定義する
#[allow(dead_code)]
enum Month {
// バリアントにisize型の整数値を割り当てられる
January = 1, February = 2, March = 3, /* 中略 */ December = 12,
}
fn say_something(weekday: Weekday) {
if weekday == Weekday::Friday {
println!("TGIF!"); // Thank God, it’s Friday(やっと金曜日だ)
} else {
println!("まだ{:?}か", weekday);
}
}
fn main() {
say_something(Weekday::Friday);
assert_eq!(3, Month::March as isize); // isize型にキャストすると割り当てた値が得られる
}
================================================
FILE: ch05/ex05/examples/ch05_19_enum2.rs
================================================
type UserName = String;
#[allow(dead_code)]
#[derive(Debug)]
enum Task {
Open,
AssignedTo(UserName),
Working {
assignee: UserName,
remaining_hours: u16,
},
Done,
}
// use宣言でTaskが持つバリアントをインポートするとバリアント名が直接書けるようになる
use crate::Task::*;
fn main() {
// Task型の値を3つ作り、ベクタに格納する
let tasks = vec![
// もし上のuse宣言がなかったらTask::AssignedToと書かないといけない
AssignedTo(String::from("junko")),
Working {
assignee: String::from("hiro"),
remaining_hours: 18,
},
Done,
];
// 個々のタスクの状況をレポートする。
for (i, task) in tasks.iter().enumerate() {
// match式によるパターンマッチでバリアントを識別し、フィールド値を取り出す
match task {
AssignedTo(assignee) => {
println!("タスク{}は{}さんにアサインされています", i, assignee)
}
Working { assignee, remaining_hours } => {
println!("タスク{}は{}さんが作業中です。残り{}時間の見込み",
i, assignee, remaining_hours)
}
_ => println!("タスク{}はその他のステータス({:?})です", i, task)
}
}
}
================================================
FILE: ch05/ex05/examples/ch05_20_adv_types1.rs
================================================
#[allow(dead_code)]
#[derive(Default)]
pub struct Polygon<T> {
pub vertexes: Vec<T>,
pub stroke_width: u8,
pub fill: (u8, u8, u8),
internal_id: String,
}
// 座標
trait Coordinates {}
// デカルト座標
#[allow(dead_code)]
#[derive(Default)]
struct CartesianCoord {
x: f64,
y: f64,
}
impl Coordinates for CartesianCoord {}
// 極座標
#[allow(dead_code)]
#[derive(Default)]
struct PolarCoord {
r: f64,
theta: f64,
}
impl Coordinates for PolarCoord {}
fn main() {
let vertexes = vec![
CartesianCoord {x: 0.0, y: 0.0},
CartesianCoord {x: 50.0, y: 0.0},
CartesianCoord {x: 30.0, y: 20.0},
];
#[allow(unused_variables)]
// Polygon<CartesianCoord>型
let poly = Polygon { vertexes, .. Default::default() };
}
================================================
FILE: ch05/ex05/examples/ch05_21_adv_types2.rs
================================================
#[derive(Default)]
struct A {
f0: u8,
f1: u32,
f2: u8,
}
fn main() {
let a: A = Default::default();
println!(
"struct A ({} bytes)\n f0: {:p}\n f1: {:p}\n f2: {:p}\n",
std::mem::size_of::<A>(),
&a.f0,
&a.f1,
&a.f2
);
}
================================================
FILE: ch05/ex05/examples/ch05_22_type_cast1.rs
================================================
fn main() {
let i1 = 42; // i32型
#[allow(unused_variables)]
let f1 = i1 as f64 / 2.5; // i32型からf64型へキャスト
let c1 = 'a';
assert_eq!(97, c1 as u32); // char型からu32型へキャスト
let i2 = 300; // i32型
let u1 = i2 as u8; // u8型へキャスト
// 300はu8型の最大値を超えているので桁あふれして44になる(300を256で割った余りは44)
assert_eq!(44, u1);
}
================================================
FILE: ch05/ex05/examples/ch05_23_type_cast2.rs
================================================
fn main() {
let t1 = ('a', 42);
// let t2 = t1 as (u32, u8);
// → error[E0605]: non-primitive cast: `(char, i32)` as `(u32, u8)`
let v1 = vec![b'h', b'e', b'l', b'l', b'o']; // Vec<u8>型
// let v2 = v1 as Vec<u16>;
// → error[E0605]: non-primitive cast:
// `std::vec::Vec<u8>` as `std::vec::Vec<u16>`
#[allow(unused_variables)]
let t3 = (t1.0 as u32, t1.1 as u8);
#[allow(unused_variables)]
let v3 = v1.iter().map(|&n| n as u16).collect::<Vec<u16>>();
// &str型はVec<u8>型への変換を対象としたFromトレイトを実装している
let v4: Vec<u8> = From::from("hello");
assert_eq!(v1, v4);
}
================================================
FILE: ch05/ex05/examples/ch05_24_transmute.rs
================================================
fn main() {
let p1 = Box::new(10); // Box<i32>型
// boxポインタを生ポインタ*mut i32型に変換したいが型キャストできない
// let p2 = p1 as *mut i32;
// → error[E0605]: non-primitive cast: ...
// Boxポインタと*mutポインタはどちらも同じビット幅なのでtransmuteできる
#[allow(unused_variables)]
let p3: *mut i32 = unsafe { std::mem::transmute(p1) };
let f1 = 5.6789e+3_f32; // 5678.9
// f32型からi32型へ型キャストする。小数点以下は切り捨てられる
let i1 = f1 as i32;
println!("{}", i1); // 5678と表示される
// f32型からi32型へtransmuteする
let i2: i32 = unsafe { std::mem::transmute(f1) };
println!("{}", i2); // 浮動小数点数を整数として再解釈した値1169258291が表示される
}
================================================
FILE: ch05/ex05/examples/ch05_25_type_coercion1.rs
================================================
fn main() {
intro();
coercion_sites();
transitivity();
}
fn intro() {
// 整数リテラル3、4、5は通常はi32型と解釈されるが、
// 型アノテーション(型注釈)によってu8型へと型強制されている
let v1: Vec<u8> = vec![3, 4, 5];
// もし型強制がなかったらこう書かなければならない
// let v1 = vec![3u8, 4u8, 5u8];
// Vec<u8>型からスライス&[u8]型へ型強制されることによって
// スライスに備わったfirst(&self)メソッドが使用できる
assert_eq!(Some(&3u8), v1.first());
// もし型強制がなかったらこう書かなければならない
// assert_eq!(Some(&3u8), (&v1[..]).first());
let mut s1 = String::from("Type coercion ");
let s2 = String::from("is actually easy.");
// push_str()のシグネチャはpush_str(self: &mut String, s: &str)
// 型強制によってs1がString型から&mut String型へ変換され、
// &s2は&String型から&str型へ変換される
s1.push_str(&s2);
// もし型強制がなかったらこう書かなければならない
// (&mut s1).push_str(s2.as_str());
}
fn coercion_sites() {
let mut _i1 = 0u8; // i1はu8型だと型推論される
_i1 = 255; // よって255はu8型へ型強制される
}
fn transitivity() {
let p1 = &&&&[1, 2, 3, 4]; // &&&&[i32; 4]型
// 型強制が&&&&a1 → &&&a1 → &&a1 → &a1の順に推移的に作用する
#[allow(unused_variables)]
let p2: &[i32; 4] = p1;
let p3 = &&[1, 2, 3, 4]; // &&[i32; 4]型
// 配列への参照&&[i32; 4]型からスライス&[i32]型まで段階を踏むと型強制できる
let p4: &[i32; 4] = p3;
#[allow(unused_variables)]
let p5: &[i32] = p4;
// しかし一度にはできない(rustc 1.31.0)
// let p6: &[i32] = p3;
// → error[E0308]: mismatched types
// expected type `&[i32]`, found type `&&[i32; 4]`
}
================================================
FILE: ch05/ex05/examples/ch05_26_type_coercion2.rs
================================================
fn f1(n: &mut usize, str: &str, slice: &[i32]) {
*n = str.len() + slice.len()
}
fn main() {
let mut b1 = Box::new(0); // Box<usize>型
let s1 = String::from("deref");
let v1 = vec![1, 2, 3];
// Derefによる型強制が起こる:
// - &mut Box<usize> → &mut usize
// - &String → &str
// - &Vec<i32> → &[i32]
f1(&mut b1, &s1, &v1);
assert_eq!(8, *b1);
}
================================================
FILE: ch05/ex05/examples/ch05_27_type_coercion3.rs
================================================
fn f1(slice: &[usize]) -> usize {
slice.len()
}
fn f2(slice: &mut [usize]) {
// ポインタの弱体化により&mut [usize]型から&[usize]型へ型強制される
let len = f1(slice);
slice[0] = len;
}
fn main() {
let mut v = vec![0; 10];
f2(&mut v[..]);
assert_eq!(10, v[0]);
}
================================================
FILE: ch05/ex05/examples/ch05_28_type_coercion4.rs
================================================
fn f1(p: &[i32]) -> i32 { p[0] }
fn f2(p: Box<[i32]>) -> i32 { p[0] }
fn main() {
let a1 = [1, 2, 3, 4];
assert_eq!(1, f1(&a1)); // &[i32; 4] → &[i32]
assert_eq!(1, f2(Box::new(a1))); // Box<[i32; 4]> → Box<[i32]>
// dの型をDebugトレイトのトレイトオブジェクトに指定する
let mut _d: Box<std::fmt::Debug>;
// Debugトレイトを実装する型はトレイトオブジェクトへ型強制できる
_d = Box::new([1, 2]); // Box<[i32; 2]> → Box<dyn Debug>
_d = Box::new(Some(1)); // Box<Some<i32>> → Box<dyn Debug>
}
================================================
FILE: ch05/ex05/examples/ch05_29_type_coercion5.rs
================================================
fn main() {
let v1: Vec<u8> = vec![3, 4, 5];
assert_eq!(v1.first(), Some(&3u8)); // first()メソッドのレシーバはv1が束縛されたベクタ
}
================================================
FILE: ch05/ex05/tests/integration_tests.rs
================================================
use cli_test_dir::*;
// バイナリを実行して入出力を確認するテスト
// `cargo test` で実行できる
#[test]
fn run_ch05_01() {
let testdir = TestDir::new("./examples/ch05_01_box", "Run ch05_01");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_02() {
let testdir = TestDir::new("./examples/ch05_02_vec", "Run ch05_02");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_03() {
let testdir = TestDir::new("./examples/ch05_03_boxed_slice", "Run ch05_03");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
r#"v1 len: 5, capacity: 8
v1 len: 5, capacity: 5
"#
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_04() {
let testdir = TestDir::new("./examples/ch05_04_hash_map", "Run ch05_04");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_05() {
let testdir = TestDir::new("./examples/ch05_05_string1", "Run ch05_05");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_06() {
let testdir = TestDir::new("./examples/ch05_06_string2", "Run ch05_06");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
"Err(ParseFloatError { kind: Invalid })\n"
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_07() {
let testdir = TestDir::new("./examples/ch05_07_string3", "Run ch05_07");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_08() {
let testdir = TestDir::new("./examples/ch05_08_string4", "Run ch05_08");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_09() {
let testdir = TestDir::new("./examples/ch05_09_range", "Run ch05_09");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_10() {
let testdir = TestDir::new("./examples/ch05_10_option", "Run ch05_10");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_11() {
let testdir = TestDir::new("./examples/ch05_11_result", "Run ch05_11");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
"Err(ParseIntError { kind: InvalidDigit })\n"
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_12() {
let testdir = TestDir::new("./examples/ch05_12_type_alias", "Run ch05_12");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_13() {
let testdir = TestDir::new("./examples/ch05_13_struct1", "Run ch05_13");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_14() {
let testdir = TestDir::new("./examples/ch05_14_struct2", "Run ch05_14");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_15() {
let testdir = TestDir::new("./examples/ch05_15_struct3", "Run ch05_15");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_16() {
let testdir = TestDir::new("./examples/ch05_16_struct4", "Run ch16_??");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_17() {
let testdir = TestDir::new("./examples/ch05_17_struct5", "Run ch05_17");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_18() {
let testdir = TestDir::new("./examples/ch05_18_enum1", "Run ch05_18");
let output = testdir.cmd().expect_success();
assert_eq!(output.stdout_str(), "TGIF!\n");
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_19() {
let testdir = TestDir::new("./examples/ch05_19_enum2", "Run ch05_19");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
r#"タスク0はjunkoさんにアサインされています
タスク1はhiroさんが作業中です。残り18時間の見込み
タスク2はその他のステータス(Done)です
"#,
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_20() {
let testdir = TestDir::new("./examples/ch05_20_adv_types1", "Run ch05_20");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_21() {
let testdir = TestDir::new("./examples/ch05_21_adv_types2", "Run ch05_21");
let output = testdir.cmd().expect_success();
use regex::Regex;
let re = Regex::new(
r#"(?m)\Astruct A \(\d{1,2} bytes\)
f0: 0x[0-9a-f]+
f1: 0x[0-9a-f]+
f2: 0x[0-9a-f]+
\z"#,
)
.expect("Failed to compile a regex pattern");
assert!(re.is_match(output.stdout_str()));
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_22() {
let testdir = TestDir::new("./examples/ch05_22_type_cast1", "Run ch05_22");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_23() {
let testdir = TestDir::new("./examples/ch05_23_type_cast2", "Run ch05_23");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_24() {
let testdir = TestDir::new("./examples/ch05_24_transmute", "Run ch05_24");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
r#"5678
1169258291
"#
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_25() {
let testdir = TestDir::new("./examples/ch05_25_type_coercion1", "Run ch05_25");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_26() {
let testdir = TestDir::new("./examples/ch05_26_type_coercion2", "Run ch05_26");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_27() {
let testdir = TestDir::new("./examples/ch05_27_type_coercion3", "Run ch05_27");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_28() {
let testdir = TestDir::new("./examples/ch05_28_type_coercion4", "Run ch05_28");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch05_29() {
let testdir = TestDir::new("./examples/ch05_29_type_coercion5", "Run ch05_29");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
================================================
FILE: ch06/leap-year/.gitignore
================================================
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch06/leap-year/Cargo.toml
================================================
[package]
name = "leap-year"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
[dev-dependencies]
cli_test_dir = "0.1"
================================================
FILE: ch06/leap-year/src/main.rs
================================================
// 第6章 基本構文
// 入力された年がうるう年かどうかを判断するプログラム
// `std::io` 名前空間を `io` としてインポート
use std::io;
// `std::io::Write` トレイトを使う
use std::io::Write;
// エントリポイントとなる関数
fn main() {
let mut year = String::new();
print!("Please input a year to check if it is a leap year: ");
io::stdout().flush().unwrap();
io::stdin().read_line(&mut year).unwrap();
let year = year.trim().parse::<u32>().unwrap();
if is_leap_year(year) {
println!("{} is a leap year!", year);
} else {
println!("{} is not a leap year.", year);
}
}
// うるう年の場合は `true` 、平年の場合は `false` を返す関数
fn is_leap_year(year: u32) -> bool {
year % 4 == 0 && !(year % 100 == 0 && year % 400 != 0)
}
================================================
FILE: ch06/leap-year/tests/integration_tests.rs
================================================
use cli_test_dir::*;
// バイナリ(leap-year)を実行して入出力を確認するテスト
// `cargo test` で実行できる
const CMD: &'static str = "./leap-year";
#[test]
fn year_2000_is_leap_year() {
let testdir = TestDir::new(CMD, "Test year 2000");
let output = testdir
.cmd()
.output_with_stdin("2000\n")
// .tee_output() // stdoutとstderrorを順に表示する。デバッグに便利
.expect_success();
assert!(output.stdout_str().contains("2000 is a leap year!"));
}
#[test]
fn year_2019_is_not_leap_year() {
let testdir = TestDir::new(CMD, "Test year 2019");
let output = testdir.cmd().output_with_stdin("2019\n").expect_success();
assert!(output.stdout_str().contains("2019 is not a leap year."));
}
#[test]
fn year_2020_is_leap_year() {
let testdir = TestDir::new(CMD, "Test year 2020");
let output = testdir.cmd().output_with_stdin("2020\n").expect_success();
assert!(output.stdout_str().contains("2020 is a leap year!"));
}
#[test]
fn year_2100_is_not_leap_year() {
let testdir = TestDir::new(CMD, "Test year 2100");
let output = testdir.cmd().output_with_stdin("2100\n").expect_success();
assert!(output.stdout_str().contains("2100 is not a leap year."));
}
================================================
FILE: ch07/ex07/.gitignore
================================================
# Cargoが選択した依存ライブラリのバージョン郡
Cargo.lock
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch07/ex07/Cargo.toml
================================================
[package]
name = "ex07"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
lazy_static = "1.2.0"
[dev-dependencies]
cli_test_dir = "0.1"
================================================
FILE: ch07/ex07/examples/ch07_01_value_scope.rs
================================================
// 構造体を定義する
// println!の"{:?}"で表示できるようにDebugトレイトを自動導出しておく
#[derive(Debug)]
struct Parent(usize, Child, Child); // Parentはusizeに加えてChildを2つ持つ
use std::ops::Drop;
// Parent構造体にデストラクタを実装する
impl Drop for Parent {
fn drop(&mut self) {
println!("Dropping {:?}", self);
}
}
#[derive(Debug)]
struct Child(usize);
// Child構造体にデストラクタを実装する
impl Drop for Child {
fn drop(&mut self) {
println!("Dropping {:?}", self);
}
}
fn main() {
let p1 = Parent(1, Child(11), Child(12));
{ // ブロックを作りp2はその中で導入する
let p2 = Parent(2, Child(21), Child(22));
println!("(a) p1: {:?}, p2: {:?}", p1, p2); // (a)の時点
}
println!("(b) p1: {:?}", &p1); // (b)の時点
let p3 = Parent(3, Child(31), Child(32));
println!("(c) p1: {:?}, p3: {:?}", p1, p3); // (c)の時点
}
================================================
FILE: ch07/ex07/examples/ch07_02_move_semantics.rs
================================================
#[derive(Debug)]
struct Parent(usize, Child, Child);
use std::ops::Drop;
impl Drop for Parent {
fn drop(&mut self) {
println!("Dropping {:?}", self);
}
}
#[derive(Debug)]
struct Child(usize);
impl Drop for Child {
fn drop(&mut self) {
println!("Dropping {:?}", self);
}
}
fn main() {
println!("Move Semantics:");
move_semantics();
println!("\nBorrow:");
borrow();
}
fn move_semantics() {
let mut p1 = Parent(1, Child(11), Child(12));
let p2 = p1; // 値の所有権をp1からp2にムーブする
println!("p2: {:?}", p2);
// println!("p1: {:?}", &p1); // p1は値の所有権を失ったためアクセス不可
// → error[E0382]: borrow of moved value: `p1`
p1 = Parent(2, Child(21), Child(22)); // p1を別の値に束縛する
println!("p1: {:?}", p1); // p1は別の値の所有権を持つためアクセスできる
}
// Parentへの不変の参照を引数にとる
fn f1(p: &Parent) {
println!("p: {:?}", p);
}
// Parentへの可変の参照を引数にとる
fn f2(p: &mut Parent) {
p.0 *= 1;
}
fn borrow() {
let mut p1 = Parent(1, Child(11), Child(12));
f1(&p1); // f1には所有権をムーブせず、不変の参照を渡す
f2(&mut p1); // f2には所有権をムーブせず、可変の参照を渡す
println!("p1: {:?}", p1); // p1は値の所有権を失っていないのでアクセスできる
}
================================================
FILE: ch07/ex07/examples/ch07_03_nll.rs
================================================
use std::collections::HashMap;
// この関数はHashMapにキーに対応する値がある場合はそれを変更し
// ない場合はデフォルト値を挿入する
fn process_or_default(key: char, map: &mut HashMap<char, String>) {
// get_mutが返す可変の参照が生存している間はmapの可変の借用が有効
match map.get_mut(&key) {
// valueが可変の参照に束縛される
// つまりvalueが生存している間はmapの可変の借用が有効となる
Some(value) => value.push_str(", world!"),
None => { // このブロック内ではselfの可変の借用は終了している
// insertはselfの可変の借用をとる
map.insert(key, Default::default());
}
}
}
fn main() {
let mut map = HashMap::new();
map.insert('h', "Hello".to_string());
process_or_default('h', &mut map);
}
================================================
FILE: ch07/ex07/examples/ch07_04_static_lifetime.rs
================================================
#[allow(dead_code)]
static I0: i32 = 42; // static変数。'staticスコープを持つ
// この関数は'staticライフタイムを持つ任意の型を引数にとる
fn take_static<T: 'static>(_x: T) { }
fn main() {
#[allow(unused_mut)]
let mut s0: &'static str;
let s1 = "42"; // &str型。文字列リテラル(データは静的領域にある)
let s2 = 42.to_string(); // String型(データはヒープ領域にある)
s0 = s1; // 文字列リテラルへの参照は'staticライフタイムを持つ
// s0 = &s2; // コンパイルエラー。String型から&'static strは作れない
// → error[E0597]: `s2` does not live long enough
println!("{}", s0);
take_static(s1); // &'static str型。OK
// take_static(&s2); // &String型。コンパイルエラー('static要求を満たせない)
take_static(s2); // String型。OK
}
================================================
FILE: ch07/ex07/examples/ch07_05_rc.rs
================================================
#[derive(Debug)]
struct Child(usize);
impl Drop for Child {
fn drop(&mut self) {
println!("Dropping {:?}", self);
}
}
use std::rc::Rc;
fn main() {
let mut rc1 = Rc::new(Child(1)); // Rcポインタ経由でChild値をヒープ領域に格納する
// strong_countでこのChild値の参照カウント(共同所有者の数)が得られる
println!("(a) count: {}, rc1: {:?}", Rc::strong_count(&rc1), rc1);
{
let rc2 = Rc::clone(&rc1); // cloneで共同所有者を作る。参照カウントが増える
println!(
"(b) count: {}, rc1: {:?}, rc2: {:?}",
Rc::strong_count(&rc1), rc1, rc2
);
} // rc2がスコープを抜け、参照カウントが減る
println!("(c) count: {}, rc1: {:?}", Rc::strong_count(&rc1), rc1);
// 参照カウントが1のときは可変の参照が得られる。そうでないときはNoneが返る
if let Some(child) = Rc::get_mut(&mut rc1) {
child.0 += 1;
}
println!("(d) count: {}, rc1: {:?}", Rc::strong_count(&rc1), rc1);
let weak = Rc::downgrade(&rc1); // Rc::downgradeでWeakポインタが得られる
println!(
"(e) count: {}, rc1: {:?}, weak: {:?}",
Rc::strong_count(&rc1), // 参照カウントは1。Weakポインタはカウントされない
rc1,
weak,
);
// WeakをRcにアップグレードするとChild値にアクセスできる
if let Some(rc3) = weak.upgrade() {
println!(
"(f) count: {}, rc1: {:?}, rc3: {:?}",
Rc::strong_count(&rc1),
rc1,
rc3,
);
}
// rc1をドロップする(スコープを抜けたのと同じ) 参照カウントが0になりChildは破棄される
std::mem::drop(rc1);
println!("(g) count: 0, weak.upgrade(): {:?}", weak.upgrade());
}
================================================
FILE: ch07/ex07/examples/ch07_06_simple_refcell.rs
================================================
use std::cell::RefCell;
struct A {
#[allow(dead_code)]
c: char,
#[allow(dead_code)]
s: String,
}
struct B {
#[allow(dead_code)]
c: char,
s: RefCell<String>, // StringをRefCellで包む
}
fn main() {
//
// RefCellを使わない場合
//
let a = A { c: 'a', s: "alex".to_string() };
#[allow(unused_variables)]
let r = &a; // 不変の参照を作る
// r.s.push('a'); // 不変の参照経由でフィールドを変更しようとするとコンパイルエラーになる
// → error[E0596]: cannot borrow `r.s` as mutable, as it is behind a `&` reference
//
// RefCellを使った場合
//
let b = B { c: 'a', s: RefCell::new("alex".to_string()) };
let rb = &b;
rb.s.borrow_mut().push('a'); // フィールドsのデータに対する可変の参照をとる
{
let rbs = b.s.borrow(); // 不変の参照をとる
assert_eq!(&*rbs, "alexa");
// RefCellでは他の参照が有効な間に可変の参照をとろうとすると実行時にパニックする
// b.s.borrow_mut(); // この時点で不変の参照rbsがまだ有効
// → thread 'main' panicked at 'already borrowed: BorrowMutError'
// try_borrow_mutならパニックせずErrを返してくれる
assert!(b.s.try_borrow_mut().is_err()); // Errが返る
} // rbsはここでスコープを抜ける
assert!(b.s.try_borrow_mut().is_ok()); // Okが返る
}
================================================
FILE: ch07/ex07/examples/ch07_07_tls_refcell.rs
================================================
use std::cell::RefCell;
use std::collections::HashSet;
thread_local!(
// TLSに変数RABBITSを作成する。thread_localマクロはmutキーワードをサポートしない
static RABBITS: RefCell<HashSet<&'static str>> = {
// 初期化のコードはそのスレッドでRABBITSが初めてアクセスされたときに実行される
// ここでは2要素のHashSetを作成し、可変にするためにRefCellで包んでいる
let rb = ["ロップイヤー", "ダッチ"].iter().cloned().collect();
RefCell::new(rb)
}
);
fn main() {
// TLSに置いた値にアクセスするにはwithを使う。mainスレッドのRABBITSが得られる
RABBITS.with(|rb| { // &RefCell<HashSet<'static str>>型
assert!(rb.borrow().contains("ロップイヤー"));
rb.borrow_mut().insert("ネザーランド・ドワーフ"); // 要素を追加
});
std::thread::spawn(|| // 別スレッドを起動し、そこでも要素を追加する
RABBITS.with(|rb| rb.borrow_mut().insert("ドワーフホト"))
).join().expect("Thread error"); // スレッドの終了を待つ
// mainスレッドのRABBITSにアクセスする
RABBITS.with(|rb| {
assert!(rb.borrow().contains("ネザーランド・ドワーフ"));
// RABBITSはスレッドごとに持つので、別スレッドで追加した要素はここにはない
assert!(!rb.borrow().contains("ドワーフホト"));
});
}
================================================
FILE: ch07/ex07/examples/ch07_08_arc_rwlock.rs
================================================
use std::collections::HashSet;
use std::error::Error;
use std::sync::{Arc, RwLock};
// ?演算子を使うためmain関数からResult型を返すようにする
fn main() -> Result<(), Box<dyn Error>> {
let dogs: HashSet<_> = ["柴", "トイプードル"].iter().cloned().collect();
// HashSetを可変にするためにRwLockで包み、スレッド間で共有するためにArcで包む
let dogs = Arc::new(RwLock::new(dogs)); // Arc<RwLock<HashSet<&'static str>>>型
// PoisonErrorをStringに変換するのに便利な関数を定義しておく
fn stringify(x: impl ToString) -> String { x.to_string() }
{
let ds = dogs.read().map_err(stringify)?; // readロックを取得する
assert!(ds.contains("柴"));
assert!(ds.contains("トイプードル"));
} // dsがスコープを外れロックが解除される
// writeロックを取得しHashSetに要素を追加する
dogs.write().map_err(stringify)?.insert("ブル・テリア");
let dogs1 = Arc::clone(&dogs);
std::thread::spawn(move ||
// 別のスレッドでwriteロックを取得しHashSetに要素を追加する
dogs1.write().map(|mut ds| ds.insert("コーギー")).map_err(stringify)
).join().expect("Thread error")?; // スレッドの終了を待つ
// このスレッドと別スレッドの両方で追加した要素が見える
assert!(dogs.read().map_err(stringify)?.contains("ブル・テリア"));
assert!(dogs.read().map_err(stringify)?.contains("コーギー"));
Ok(())
}
================================================
FILE: ch07/ex07/examples/ch07_09_static_rwlock.rs
================================================
use lazy_static::lazy_static;
use std::collections::HashSet;
use std::error::Error;
use std::sync::RwLock;
lazy_static! {
// staticな変数DOGSを導入する。この変数はプログラム全体で共有される
// refは束縛モードと呼ばれ、不変の参照を意味する
// lazy_staticではDOGSが初めて参照外しされたときに以下の初期化コードが実行される
pub static ref DOGS: RwLock<HashSet<&'static str>> = {
// HashSetを可変にするためにRwLockで包む
let dogs = ["柴", "トイプードル"].iter().cloned().collect();
RwLock::new(dogs)
};
}
fn main() -> Result<(), Box<dyn Error>> {
{
let dogs = DOGS.read()?; // readロックを取得する
assert!(dogs.contains("柴"));
assert!(dogs.contains("トイプードル"));
} // dogsがスコープを外れreadロックが解除される
fn stringify(x: impl ToString) -> String { x.to_string() }
// writeロックを取得しHashSetに要素を追加する
DOGS.write()?.insert("ブル・テリア");
std::thread::spawn(||
// 別のスレッドでwriteロックを取得しHashSetに要素を追加する
DOGS.write().map(|mut ds| ds.insert("コーギー")).map_err(stringify)
).join().expect("Thread error")?; // スレッドの終了を待つ
// このスレッドと別スレッドの両方で追加した要素が見える
assert!(DOGS.read()?.contains("ブル・テリア"));
assert!(DOGS.read()?.contains("コーギー"));
Ok(())
}
================================================
FILE: ch07/ex07/examples/ch07_10_closure.rs
================================================
// この関数はクロージャFとcharを引数にとる
// FはFnを実装し、引数にcharをとり、boolを返す
fn apply_fn<F>(f: &F, ch: char) where F: Fn(char) -> bool {
assert!(f(ch)); // chにクロージャを適用する。trueが返されればOK
}
// この関数はFnMutを実装したクロージャFとcharを引数にとる(Fの関数シグネチャは上と同じ)
fn apply_fn_mut<F>(f: &mut F, ch: char) where F: FnMut(char) -> bool {
assert!(f(ch));
}
// この関数はFnOnceを実装したクロージャFとcharを引数にとる(Fの関数シグネチャは上と同じ)
fn apply_fn_once<F>(f: F, ch: char) where F: FnOnce(char) -> bool {
assert!(f(ch));
}
fn main() {
let s1 = "read-only";
let mut lookup = |ch| s1.find(ch).is_some(); // find(&self)は&strを読むだけ
apply_fn(&lookup, 'r');
apply_fn_mut(&mut lookup, 'o'); // Fnを実装するクロージャはFnMutも実装する
apply_fn_once(lookup, 'y'); // FnMutを実装するクロージャはFnOnceも実装する
assert_eq!(s1, "read-only"); // 環境に取り込まれた文字列(&str型)は変更されてない
let mut s2 = "append".to_string();
let mut modify = |ch| {
s2.push(ch); // push(&mut self, char)はStringを可変の参照経由で変更する
true
};
// apply_fn(&modify, 'e'); // Fnをとる関数はコンパイルエラーになる
// → error[E0525]: expected a closure that implements the `Fn` trait,
// but this closure only implements `FnMut`
apply_fn_mut(&mut modify, 'e'); // FnMutをとる関数はOK
apply_fn_once(modify, 'd'); // FnOnceをとる関数はOK
assert_eq!(s2, "appended"); // 環境に取り込まれた文字列(String型)が変更された
let s3 = "be converted".to_string();
#[allow(unused_mut)]
let mut consume = |ch| {
let bytes = s3.into_bytes(); // into_bytes(self)はStringを消費する(所有権をとる)
bytes.contains(&(ch as u8))
};
// apply_fn(&consume, 'b'); // Fnをとる関数はコンパイルエラー
// → error[E0525]: expected a closure that implements the `Fn` trait,
// but this closure only implements `FnOnce`
// apply_fn_mut(&mut consume, 'c'); // FnMutをとる関数もコンパイルエラー
// → error[E0525]: expected a closure that implements the `FnMut` trait,
// but this closure only implements `FnOnce`
apply_fn_once(consume, 'd'); // FnOnceをとる関数ならOK
// assert_eq!(s3, "error"); // s3はムーブ済み。コンパイルエラー
// → error[E0382]: borrow of moved value: `s3`
// Fnトレイトを実装するクロージャを定義する
// let lookup = || assert!(s1.find('d').is_some());
// クロージャをspawnに渡す。クロージャにmoveがないのでコンパイルエラーになる
// let handle = std::thread::spawn(lookup);
// → error[E0373]: closure may outlive the current function, but it
// borrows `s1`, which is owned by the current function
// moveを付けるとs1が環境へムーブする
// クロージャがs1を所有するのでライフタイム要件を満たせる
let lookup = move || assert!(s1.find('d').is_some());
let handle = std::thread::spawn(lookup);
handle.join().expect("Failed to run thread.");
}
================================================
FILE: ch07/ex07/tests/integration_tests.rs
================================================
use cli_test_dir::*;
// examplesディレクトリ配下にあるバイナリを実行して入出力を確認するテスト
// `cargo test` で実行できる
#[test]
fn run_ch07_01() {
let testdir = TestDir::new("./examples/ch07_01_value_scope", "Run ch07_01");
let output = testdir
.cmd()
//.tee_output() // stdoutとstderrorを順に表示する。デバッグに便利
.expect_success();
assert_eq!(
output.stdout_str(),
r#"(a) p1: Parent(1, Child(11), Child(12)), p2: Parent(2, Child(21), Child(22))
Dropping Parent(2, Child(21), Child(22))
Dropping Child(21)
Dropping Child(22)
(b) p1: Parent(1, Child(11), Child(12))
(c) p1: Parent(1, Child(11), Child(12)), p3: Parent(3, Child(31), Child(32))
Dropping Parent(3, Child(31), Child(32))
Dropping Child(31)
Dropping Child(32)
Dropping Parent(1, Child(11), Child(12))
Dropping Child(11)
Dropping Child(12)
"#
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch07_02() {
let testdir = TestDir::new("./examples/ch07_02_move_semantics", "Run ch07_02");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
r#"Move Semantics:
p2: Parent(1, Child(11), Child(12))
p1: Parent(2, Child(21), Child(22))
Dropping Parent(1, Child(11), Child(12))
Dropping Child(11)
Dropping Child(12)
Dropping Parent(2, Child(21), Child(22))
Dropping Child(21)
Dropping Child(22)
Borrow:
p: Parent(1, Child(11), Child(12))
p1: Parent(1, Child(11), Child(12))
Dropping Parent(1, Child(11), Child(12))
Dropping Child(11)
Dropping Child(12)
"#
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch07_03() {
let testdir = TestDir::new("./examples/ch07_03_nll", "Run ch07_03");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch07_04() {
let testdir = TestDir::new("./examples/ch07_04_static_lifetime", "Run ch07_04");
let output = testdir.cmd().expect_success();
assert_eq!(output.stdout_str(), "42\n");
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch07_05() {
let testdir = TestDir::new("./examples/ch07_05_rc", "Run ch07_05");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
r#"(a) count: 1, rc1: Child(1)
(b) count: 2, rc1: Child(1), rc2: Child(1)
(c) count: 1, rc1: Child(1)
(d) count: 1, rc1: Child(2)
(e) count: 1, rc1: Child(2), weak: (Weak)
(f) count: 2, rc1: Child(2), rc3: Child(2)
Dropping Child(2)
(g) count: 0, weak.upgrade(): None
"#
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch07_06() {
let testdir = TestDir::new("./examples/ch07_06_simple_refcell", "Run ch07_06");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch07_07() {
let testdir = TestDir::new("./examples/ch07_07_tls_refcell", "Run ch07_07");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch07_08() {
let testdir = TestDir::new("./examples/ch07_08_arc_rwlock", "Run ch07_08");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch07_09() {
let testdir = TestDir::new("./examples/ch07_09_static_rwlock", "Run ch07_09");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch07_10() {
let testdir = TestDir::new("./examples/ch07_10_closure", "Run ch07_10");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
================================================
FILE: ch07/toy-vec/.gitignore
================================================
# Cargoが選択した依存ライブラリのバージョン郡
Cargo.lock
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch07/toy-vec/Cargo.toml
================================================
[package]
name = "toy-vec"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
[dev-dependencies]
cli_test_dir = "0.1"
================================================
FILE: ch07/toy-vec/examples/toy_vec_01.rs
================================================
use toy_vec::ToyVec;
fn main() {
let mut v = ToyVec::new();
v.push("Java Finch".to_string()); // 桜文鳥
v.push("Budgerigar".to_string()); // セキセイインコ
let e = v.get(1);
assert_eq!(e, Some(&"Budgerigar".to_string()));
}
================================================
FILE: ch07/toy-vec/examples/toy_vec_02.rs
================================================
use toy_vec::ToyVec;
fn main() {
let _e: Option<&String>;
{
let mut v = ToyVec::new();
v.push("Java Finch".to_string());
v.push("Budgerigar".to_string());
_e = v.get(1); // コンパイルエラーになる
// → error[E0597]: `v` does not live long enough
} // ここでvがスコープから抜け、ToyVec構造体が破棄される
// eは解放後のメモリを参照している
// assert_eq!(_e, Some(&"Budgerigar".to_string()));
}
================================================
FILE: ch07/toy-vec/examples/toy_vec_03.rs
================================================
use toy_vec::ToyVec;
fn main() {
let mut v = ToyVec::new();
v.push("Java Finch".to_string()); // 桜文鳥
v.push("Budgerigar".to_string()); // セキセイインコ
let mut iter = v.iter();
// v.push("Hill Mynah".to_string()); // 九官鳥。コンパイルエラーになる
// → error[E0502]: cannot borrow `v` as mutable because it is
// also borrowed as immutable
// pushは可変の参照を得ようとするが、iterが生存しているので不変の参照が有効
assert_eq!(iter.next(), Some(&"Java Finch".to_string()));
v.push("Canary".to_string()); // カナリア。iterはもう生存していないので変更できる
}
================================================
FILE: ch07/toy-vec/src/lib.rs
================================================
use std::fmt;
pub struct ToyVec<T> {
elements: Box<[T]>, // T型の要素を格納する領域。各要素はヒープ領域に置かれる
len: usize, // ベクタの長さ(現在の要素数)
}
// implブロック内に関連関数やメソッドを定義していく。トレイト境界としてDefaultを設定する
impl<T: Default> ToyVec<T> {
// newはキャパシティ(容量)が0のToyVecを作る
pub fn new() -> Self {
Self::with_capacity(0)
}
// with_capacityは指定されたキャパシティを持つToyVecを作る
pub fn with_capacity(capacity: usize) -> Self {
Self {
elements: Self::allocate_in_heap(capacity),
len: 0,
}
}
// T型の値がsize個格納できるBox<[T]>を返す
fn allocate_in_heap(size: usize) -> Box<[T]> {
std::iter::repeat_with(Default::default)
.take(size) // T型のデフォルト値をsize個作り
.collect::<Vec<_>>() // Vec<T>に収集してから
.into_boxed_slice() // Box<[T]>に変換する
}
// ベクタの長さを返す
pub fn len(&self) -> usize {
self.len
}
// ベクタの現在のキャパシティを返す
pub fn capacity(&self) -> usize {
self.elements.len() // elementsの要素数(len)がToyVecのキャパシティになる
}
pub fn push(&mut self, element: T) {
if self.len == self.capacity() { // 要素を追加するスペースがないなら
self.grow(); // もっと大きいelementsを確保して既存の要素を引っ越す
}
self.elements[self.len] = element; // 要素を格納する(所有権がムーブする)
self.len += 1;
}
// elementsを拡張する(より大きなサイズで作り直す)
fn grow(&mut self) {
if self.capacity() == 0 { // 現在のelementsが空なら
// 1要素分の領域を確保する
self.elements = Self::allocate_in_heap(1);
} else {
// 現在の2倍の領域を確保する
let new_elements = Self::allocate_in_heap(self.capacity() * 2);
// self.elementsを置き換える
let old_elements = std::mem::replace(&mut self.elements, new_elements);
// 既存の全要素を新しい領域へムーブする
// Vec<T>のinto_iter(self)なら要素の所有権が得られる
for (i, elem) in old_elements.into_vec().into_iter().enumerate() {
self.elements[i] = elem;
}
}
}
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.len { // インデックスが範囲内なら
Some(&self.elements[index]) // Some(不変の参照)を返す
} else {
None // 範囲外ならNoneを返す
}
}
// インデックスが範囲内なら要素への参照を返し、さもなければdefaultで与えた別の値への参照を返す
pub fn get_or<'a>(&'a self, index: usize, default: &'a T) -> &'a T {
self.get(index).unwrap_or(default)
}
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
self.len -= 1;
// 最後の要素の所有権を得たいが、借用(&mut self)経由では所有権を奪えない
// let elem = self.elements[self.len];
// → error[E0507]: cannot move out of borrowed content
// 代わりの値と交換するならできる(ここではデフォルト値を使用)
let elem = std::mem::replace(&mut self.elements[self.len], Default::default());
Some(elem)
}
}
// 要素へのイミュータブルな参照(Option<&T>)を返すイテレータを作る
// 説明のためにライフタイムを明示しているが、実際には省略できる
pub fn iter<'vec>(&'vec self) -> Iter<'vec, T> {
Iter {
elements: &self.elements, // Iter構造体の定義より、ライフタイムは'vecになる
len: self.len,
pos: 0,
}
}
// 要素へのイミュータブルな参照(Option<&mut T>)を返すイテレータを作る
pub fn iter_mut<'vec>(&'vec mut self) -> IterMut<'vec, T> {
IterMut {
elements: &mut self.elements,
len: self.len,
pos: 0,
}
}
// 要素の所有権をとる(Option<T>)イテレータを作る
pub fn into_iter<'vec>(self) -> IntoIter<T> {
IntoIter {
elements: self.elements,
len: self.len,
pos: 0,
}
}
}
impl<T: Default> Default for ToyVec<T> {
fn default() -> Self {
// newはキャパシティ(容量)が0のToyVecを作る
Self::new()
}
}
impl<T: Clone + Default> Clone for ToyVec<T> {
fn clone(&self) -> Self {
let mut cloned = Self::with_capacity(self.len());
// 各要素のcloneを呼ぶことでdeepコピーを実現する
for elem in self.iter() {
cloned.push(elem.clone());
}
cloned
}
}
impl<T: PartialEq> PartialEq for ToyVec<T> {
fn eq(&self, other: &Self) -> bool {
// スライス[T]同士を比較。各要素(T)がPartialEqを実装しているので可能になる
self.elements[..self.len] == other.elements[..other.len]
}
}
impl<T: fmt::Debug> fmt::Debug for ToyVec<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.elements[..self.len].fmt(f)
}
}
// IntoIteratorトレイトを実装するとfor式での繰り返しができるようになる
impl<'vec, T: Default> IntoIterator for &'vec ToyVec<T> {
type Item = &'vec T; // イテレータがイテレートする値の型
type IntoIter = Iter<'vec, T>; // into_iterメソッドの戻り値の型
// &ToyVec<T>に対するトレイト実装なので、selfの型はToyVec<T>ではなく&ToyVec<T>
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'vec, T: Default> IntoIterator for &'vec mut ToyVec<T> {
type Item = &'vec mut T;
type IntoIter = IterMut<'vec, T>;
// selfの型はToyVec<T>ではなく&mut ToyVec<T>
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<T: Default> IntoIterator for ToyVec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
// selfの型はToyVec<T>
fn into_iter(self) -> Self::IntoIter {
self.into_iter()
}
}
//
// 要素へのイミュータブルな参照(Option<&T>)を返すイテレータ
//
// ライフタイムの指定により、このイテレータ自身またはnext()で得た&'vec T型の値が
// 生存してる間は、ToyVec<T>は変更できない
pub struct Iter<'vec, T> {
elements: &'vec Box<[T]>,
len: usize,
pos: usize,
}
impl<'vec, T> Iterator for Iter<'vec, T> {
// 関連型(トレイトに関連付いた型)で、このイテレータがイテレートする要素の型を指定する
// 関連型は8章で説明
type Item = &'vec T;
// nextメソッドは次の要素を返す
// 要素があるなら不変の参照(&T)をSomeで包んで返し、ないときはNoneを返す
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.len {
None
} else {
let res = Some(&self.elements[self.pos]);
self.pos += 1;
res
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
//
// 要素へのミュータブルな参照(Option<&mut T>)を返すイテレータ
//
pub struct IterMut<'vec, T> {
elements: &'vec mut Box<[T]>, // ミュータブルな参照
len: usize,
pos: usize,
}
impl<'vec, T> Iterator for IterMut<'vec, T> {
type Item = &'vec mut T;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.len {
None
} else {
// 要素を&'vec mut Tとして返したいが、&'a mut selfから要素を取り出すと
// 要素が&'a mut Tになってしまい、ライフタイム要件が満たせない
// そこで以下のように対応した
// 1. &'a mut Tを生ポインタ*mut Tに変換してライフタイムをなくす
// 2. *mut Tの参照外しをして要素Tにアクセス
// 3. 要素Tから&'vec mut Tを得る
let elem = unsafe { &mut *(&mut self.elements[self.pos] as *mut T) };
self.pos += 1;
Some(elem)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
//
// 要素の所有権をとるイテレータ。Option<T>を返す
//
pub struct IntoIter<T> {
elements: Box<[T]>, // ミュータブルな参照
len: usize,
pos: usize,
}
impl<T: Default> Iterator for IntoIter<T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.len {
None
} else {
// &mut selfから要素Tをムーブアウトできないのでreplaceでデフォルト値と交換している
let elem = std::mem::replace(&mut self.elements[self.pos], Default::default());
self.pos += 1;
Some(elem)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
#[cfg(test)]
mod tests {
use super::ToyVec;
#[test]
fn test_char_vec() {
let mut v = ToyVec::new();
assert_eq!(v.len(), 0);
assert_eq!(v.capacity(), 0);
v.push('a');
assert_eq!(v.capacity(), 1);
v.push('b');
assert_eq!(v.capacity(), 2);
v.push('c');
assert_eq!(v.capacity(), 4);
assert_eq!(v.len(), 3);
assert_eq!(v.get(2), Some(&'c'));
assert_eq!(v.get(3), None);
let mut iter = v.iter();
assert_eq!(iter.size_hint(), (3, Some(3)));
assert_eq!(iter.next(), Some(&'a'));
assert_eq!(iter.next(), Some(&'b'));
assert_eq!(iter.next(), Some(&'c'));
assert_eq!(iter.next(), None);
assert_eq!(v.pop(), Some('c'));
assert_eq!(v.pop(), Some('b'));
assert_eq!(v.pop(), Some('a'));
assert_eq!(v.pop(), None);
assert_eq!(v.len(), 0);
assert_eq!(v.capacity(), 4);
}
#[test]
fn test_string_vec() {
let mut v = ToyVec::new();
assert_eq!(v.len(), 0);
assert_eq!(v.capacity(), 0);
v.push("alfalfa");
v.push("broccoli");
v.push("carrot");
assert_eq!(v.capacity(), 4);
assert_eq!(v.len(), 3);
assert_eq!(v.get(2), Some(&"carrot"));
assert_eq!(v.get(3), None);
let mut iter = v.iter();
assert_eq!(iter.size_hint(), (3, Some(3)));
assert_eq!(iter.next(), Some(&"alfalfa"));
assert_eq!(iter.next(), Some(&"broccoli"));
assert_eq!(iter.next(), Some(&"carrot"));
assert_eq!(iter.next(), None);
assert_eq!(v.pop(), Some("carrot"));
assert_eq!(v.pop(), Some("broccoli"));
assert_eq!(v.pop(), Some("alfalfa"));
assert_eq!(v.pop(), None);
assert_eq!(v.len(), 0);
assert_eq!(v.capacity(), 4);
}
#[test]
fn test_nested_vec() {
let mut v = ToyVec::new();
assert_eq!(v.len(), 0);
assert_eq!(v.capacity(), 0);
let mut v1 = ToyVec::new();
v1.push("alfalfa");
let mut v2 = ToyVec::new();
v2.push("broccoli");
let mut v3 = ToyVec::new();
v3.push("carrot");
v.push(v1);
v.push(v2);
v.push(v3);
assert_eq!(v.capacity(), 4);
assert_eq!(v.len(), 3);
assert!(v.get(2).is_some());
assert!(v.get(3).is_none());
let mut iter = v.iter();
assert_eq!(iter.size_hint(), (3, Some(3)));
assert!(iter.next().is_some());
assert!(iter.next().is_some());
assert!(iter.next().is_some());
assert!(iter.next().is_none());
let mut v1 = ToyVec::new();
v1.push("alfalfa");
let mut v2 = ToyVec::new();
v2.push("broccoli");
let mut v3 = ToyVec::new();
v3.push("carrot");
assert_eq!(v.pop(), Some(v3));
assert_eq!(v.pop(), Some(v2));
assert_eq!(v.pop(), Some(v1));
assert_eq!(v.pop(), None);
assert_eq!(v.len(), 0);
assert_eq!(v.capacity(), 4);
}
#[test]
fn test_iter() {
let mut v = ToyVec::new();
v.push(1);
v.push(1);
v.push(2);
v.push(3);
v.push(5);
// このイテレータはイミュータブルな要素(Option<&i32>)を返す
let mut iter = v.iter();
// イテレータが有効な間でもvからの直接のget(読み出し)はできる
assert_eq!(v.get(4), Some(&5));
// イテレータが有効な間はvへの直接のpush(変更)はできない
// v.push(8);
// → error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
let mut sum = 0;
// &ToyVec<T>にIntoIteratorを実装し、Iter<T>を返すようにしたので以下のように使える
for i in &v {
sum += *i;
}
assert_eq!(sum, [1, 1, 2, 3, 5].iter().sum());
assert_eq!(iter.next(), Some(&1));
assert_eq!(v.len(), 5);
}
#[test]
fn test_iter_mut() {
let mut v = ToyVec::new(); // ToyVec<i32>
v.push(1);
v.push(1);
v.push(2);
v.push(3);
v.push(5);
// このイテレータはミュータブルな要素(Option<&mut i32>)を返す
let mut iter = v.iter_mut();
// イテレータが有効な間はvからの直接のget(読み出し)やpush(変更)はできない
// v.get(0);
// → error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
iter.next().map(|i| *i *= 8); // 最初の要素を8倍する
// &mut ToyVec<T>にIntoIteratorを実装し、IterMut<T>を返すようにしたので以下のように使える
for i in &mut v {
*i += 10;
}
assert_eq!(v.get(0), Some(&18));
assert_eq!(v.get(1), Some(&11));
assert_eq!(v.get(2), Some(&12));
assert_eq!(v.get(3), Some(&13));
assert_eq!(v.get(4), Some(&15));
assert_eq!(v.len(), 5);
}
#[test]
fn test_into_iter() {
let mut v = ToyVec::new();
v.push(1);
v.push(1);
v.push(2);
v.push(3);
v.push(5);
let mut sum = 0;
// ToyVec<T>にIntoIteratorを実装し、IntoIter<T>を返すようにしたので以下のように使える
for i in v {
sum += i;
// IntoIter<T>はToyVec<T>消費するので、作成後はvにアクセスできなくなる
// v.get(0);
// → error[E0382]: borrow of moved value: `v`
}
assert_eq!(sum, [1, 1, 2, 3, 5].iter().sum());
}
}
================================================
FILE: ch07/toy-vec/tests/integration_tests.rs
================================================
use cli_test_dir::*;
// バイナリを実行して入出力を確認するテスト
// `cargo test` で実行できる
#[test]
fn run_toy_vec_01() {
let testdir = TestDir::new("./examples/toy_vec_01", "Run ToyVec example 01");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_toy_vec_02() {
let testdir = TestDir::new("./examples/toy_vec_02", "Run ToyVec example 02");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_toy_vec_03() {
let testdir = TestDir::new("./examples/toy_vec_03", "Run ToyVec example 03");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
================================================
FILE: ch08/ex08/.gitignore
================================================
# Cargoが選択した依存ライブラリのバージョン郡
Cargo.lock
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
# Javaのコンパイラが生成する成果物
*.class
================================================
FILE: ch08/ex08/Cargo.toml
================================================
[package]
name = "ex08"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
[dev-dependencies]
cli_test_dir = "0.1"
================================================
FILE: ch08/ex08/examples/ch08_01_trait_basics.rs
================================================
// デカルト座標
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct CartesianCoord {
pub x: f64,
pub y: f64,
}
// 極座標
// y
// ^
// |
// r sin θ + +
// | / r
// | /
// |/)θ
// +---+--------> x
// O r cos θ
pub struct PolarCoord {
pub r: f64,
pub theta: f64,
}
// 座標
pub trait Coordinates {
// 関数の本体は書かない
fn to_cartesian(self) -> CartesianCoord;
fn from_cartesian(cart: CartesianCoord) -> Self;
}
// デカルト座標系はそのまま
impl Coordinates for CartesianCoord {
fn to_cartesian(self) -> CartesianCoord {
self
}
fn from_cartesian(cart: CartesianCoord) -> Self {
cart
}
}
// 極座標系は変換が必要
impl Coordinates for PolarCoord {
fn to_cartesian(self) -> CartesianCoord {
CartesianCoord {
x: self.r * self.theta.cos(),
y: self.r * self.theta.sin(),
}
}
fn from_cartesian(cart: CartesianCoord) -> Self {
PolarCoord {
r: (cart.x * cart.x + cart.y * cart.y).sqrt(),
theta: (cart.y / cart.x).atan(),
}
}
}
// タプルにもトレイトを実装できる
impl Coordinates for (f64, f64) {
fn to_cartesian(self) -> CartesianCoord {
CartesianCoord {
x: self.0,
y: self.1,
}
}
fn from_cartesian(cart: CartesianCoord) -> Self {
(cart.x, cart.y)
}
}
fn main() {
// 値を用意する
let point = (1.0, 1.0);
// トレイトのメソッドを呼ぶ
let c = point.to_cartesian();
println!("x = {}, y = {}", c.x, c.y);
// 同じくトレイトの関連関数を呼ぶ(後述
let p: PolarCoord = Coordinates::from_cartesian(c);
println!("r = {}, θ = {}", p.r, p.theta);
}
================================================
FILE: ch08/ex08/examples/ch08_02_trait_basics.rs
================================================
// デカルト座標
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct CartesianCoord {
pub x: f64,
pub y: f64,
}
// 極座標
pub struct PolarCoord {
r: f64,
theta: f64,
}
// 座標
pub trait Coordinates {
// 関数の本体は書かない
fn to_cartesian(self) -> CartesianCoord;
fn from_cartesian(cart: CartesianCoord) -> Self;
}
// デカルト座標系はそのまま
impl Coordinates for CartesianCoord {
fn to_cartesian(self) -> CartesianCoord {
self
}
fn from_cartesian(cart: CartesianCoord) -> Self {
cart
}
}
// 極座標系は変換が必要
impl Coordinates for PolarCoord {
fn to_cartesian(self) -> CartesianCoord {
CartesianCoord {
x: self.r * self.theta.cos(),
y: self.r * self.theta.sin(),
}
}
fn from_cartesian(cart: CartesianCoord) -> Self {
PolarCoord {
r: (cart.x * cart.x + cart.y * cart.y).sqrt(),
theta: (cart.y / cart.x).atan(),
}
}
}
// タプルにもトレイトを実装できる
impl Coordinates for (f64, f64) {
fn to_cartesian(self) -> CartesianCoord {
CartesianCoord {
x: self.0,
y: self.1,
}
}
fn from_cartesian(cart: CartesianCoord) -> Self {
(cart.x, cart.y)
}
}
// fn print_point<P: Coordinates>(point: P) {
// let p = point.to_cartesian();
// println!("({}, {})", p.x, p.y)
// }
// fn print_point<P>(point: P)
// where
// P: Coordinates,
// {
// let p = point.to_cartesian();
// println!("({}, {})", p.x, p.y)
// }
fn print_point(point: impl Coordinates) {
let p = point.to_cartesian();
println!("({}, {})", p.x, p.y)
}
// #[allow(dead_code)]
// fn as_cartesian<P: Coordinates + Clone>(point: &P) -> CartesianCoord {
// point.clone().to_cartesian()
// }
#[allow(dead_code)]
fn as_cartesian(point: &(impl Coordinates + Clone)) -> CartesianCoord {
point.clone().to_cartesian()
}
// `P` を2回書くにはジェネリクスが必要
#[allow(dead_code)]
fn double_point<P: Coordinates>(point: P) -> P {
let mut cart = point.to_cartesian();
cart.x *= 2.0;
cart.y *= 2.0;
P::from_cartesian(cart)
}
// `(T, T)` のように `T` そのものでない型への制約はジェネリクスが必要
#[allow(dead_code)]
fn make_point<T>(x: T, y: T) -> CartesianCoord
where
(T, T): Coordinates,
{
(x, y).to_cartesian()
}
// ジェネリックトレイトを用意しておく
// 後に説明するがトレイトもジェネリクスにできる
trait ConvertTo<Output> {
fn convert(&self) -> Output;
}
#[allow(dead_code)]
fn to<T>(i: i32) -> T
where
// `ConvertTo<T>` と型パラメータがトレイト側にある
// where 記法だと`i32`など具体的な型に制約がかける
i32: ConvertTo<T>,
{
i.convert()
}
fn main() {
// `Coordinates`を実装している型の値に対して呼べる
print_point((0.0, 1.0)); // (0, 1)
print_point(PolarCoord {
r: 1.0,
theta: std::f64::consts::PI / 2.0,
}); // (0.00000000000000006123233995736766, 1)
// しかし `Coordinates` を実装していない型の値を引数に渡そうとするとコンパイルエラーになる
// print_point("string"); // error[E0277]: the trait bound `&str: Coordinates` is not satisfied
}
================================================
FILE: ch08/ex08/examples/ch08_03_trait_basics.rs
================================================
// デカルト座標
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct CartesianCoord {
pub x: f64,
pub y: f64,
}
// 極座標
pub struct PolarCoord {
r: f64,
theta: f64,
}
// 座標
pub trait Coordinates {
// 関数の本体は書かない
fn to_cartesian(self) -> CartesianCoord;
fn from_cartesian(cart: CartesianCoord) -> Self;
}
// デカルト座標系はそのまま
impl Coordinates for CartesianCoord {
fn to_cartesian(self) -> CartesianCoord {
self
}
fn from_cartesian(cart: CartesianCoord) -> Self {
cart
}
}
// 極座標系は変換が必要
impl Coordinates for PolarCoord {
fn to_cartesian(self) -> CartesianCoord {
CartesianCoord {
x: self.r * self.theta.cos(),
y: self.r * self.theta.sin(),
}
}
fn from_cartesian(cart: CartesianCoord) -> Self {
PolarCoord {
r: (cart.x * cart.x + cart.y * cart.y).sqrt(),
theta: (cart.y / cart.x).atan(),
}
}
}
// タプルにもトレイトを実装できる
impl Coordinates for (f64, f64) {
fn to_cartesian(self) -> CartesianCoord {
CartesianCoord {
x: self.0,
y: self.1,
}
}
fn from_cartesian(cart: CartesianCoord) -> Self {
(cart.x, cart.y)
}
}
fn print_point(point: impl Coordinates) {
let p = point.to_cartesian();
println!("({}, {})", p.x, p.y)
}
struct Matrix([[f64; 2]; 2]);
trait LinearTransform: Coordinates {
fn transform(self, matrix: &Matrix) -> Self
where
Self: Sized,
{
let mut cart = self.to_cartesian();
let x = cart.x;
let y = cart.y;
let m = matrix.0;
cart.x = m[0][0] * x + m[0][1] * y;
cart.y = m[1][0] * x + m[1][1] * y;
Self::from_cartesian(cart)
}
fn rotate(self, theta: f64) -> Self
where
Self: Sized,
{
self.transform(&Matrix([
[theta.cos(), -theta.sin()],
[theta.sin(), theta.cos()],
]))
}
}
// 継承するトレイトを全て実装しているので `LinearTransform` を `CartesianCoord` に実装できる
impl LinearTransform for CartesianCoord {}
impl LinearTransform for PolarCoord {
fn rotate(mut self, theta: f64) -> Self {
self.theta += theta;
self
}
}
fn main() {
let p = (1.0, 0.0).to_cartesian();
print_point(p.rotate(std::f64::consts::PI)); // (-1, 0.00000000000000012246467991473532)
}
================================================
FILE: ch08/ex08/examples/ch08_04_trait_generics.rs
================================================
trait Init<T> {
fn init(t: T) -> Self;
}
impl<T> Init<T> for Box<T> {
// 内部では`T`でパラメータの型を参照する
fn init(t: T) -> Self {
Box::new(t)
}
}
fn main() {
// ジェネリクスが推論可能なら省略できる
let _data = Box::init("foo");
// トレイトのジェネリク型を明示するには`型名::<型>`と書く
let _data = Box::<f32>::init(0.1);
let _data: Box<f32> = Init::init(0.1);
let _data: Box<_> = Init::<f32>::init(0.1);
}
================================================
FILE: ch08/ex08/examples/ch08_05_trait_generics.rs
================================================
trait As<T> {
fn cast(self) -> T;
}
// 実装をジェネリックにせずに個別の型に対して実装する
impl As<u64> for u8 {
fn cast(self) -> u64 {
self as u64
}
}
// 同じ`As`を`u8`に実装しているが、パラメータが異なるので問題ない
impl As<u32> for u8 {
fn cast(self) -> u32 {
self as u32
}
}
fn main() {
// トレイト実装で指定した型はcastに指定できる
let _one_u32: u32 = 1.cast();
let _one_u32: u64 = 1.cast();
// `i8` は指定していないのでこれはエラー
// error[E0277]: the trait bound `{integer}: As<i8>` is not satisfied
// let _one_u32: i8 = 1.cast();
}
================================================
FILE: ch08/ex08/examples/ch08_06_overload.rs
================================================
// trait Overload {
// fn call(&self) -> &'static str;
// }
// impl Overload for i32 {
// fn call(&self) -> &'static str {
// "i32"
// }
// }
// impl Overload for str {
// fn call(&self) -> &'static str {
// "str"
// }
// }
// fn main() {
// assert_eq!(Overload::name(&1i32), "i32");
// assert_eq!(Overload::name("str"), "str");
// }
// fn main() {
// assert_eq!(1i32.name(), "i32");
// assert_eq!("str".name(), "str");
// }
trait Overload1<T> {
fn call(&self, t: T) -> &'static str;
}
impl Overload1<i32> for i32 {
fn call(&self, _: i32) -> &'static str {
"(i32, i32)"
}
}
impl Overload1<char> for i32 {
fn call(&self, _: char) -> &'static str {
"(i32, char)"
}
}
fn main() {
assert_eq!(1i32.call(2i32), "(i32, i32)");
assert_eq!(1i32.call('c'), "(i32, char)");
}
================================================
FILE: ch08/ex08/examples/ch08_07_trait_object.rs
================================================
use std::fmt::Display;
fn main() {
let mut v: Vec<&dyn Display> = vec![];
v.push(&true);
v.push(&1i32);
}
use std::string::ToString;
// #[allow(dead_code)]
// fn stringify(t: Box<dyn ToString>) -> String {
// t.to_string()
// }
// #[allow(dead_code)]
// fn stringify<T: ToString>(t: T) -> String {
// t.to_string()
// }
#[allow(dead_code)]
fn stringify(t: impl ToString) -> String {
t.to_string()
}
// 上記コードは以下のようなコードに展開される
// ※ コードはイメージです。実際のものと異なることがありま
// fn stringify(t: Box<dyn ToString>) -> String {
// let data = t.data;
// let to_string = t.to_string;
// to_string(&data)
// }
================================================
FILE: ch08/ex08/examples/ch08_08_existential_impl_trait.rs
================================================
#[allow(dead_code)]
fn to_n(n: i32) -> impl Iterator {
0..n
}
// use std::ops::Range;
// #[allow(dead_code)]
// fn to_n(n: i32) -> Range<i32> {
// 0..n
// }
#[allow(dead_code)]
fn to_n_even(n: i32) -> impl Iterator {
(0..n).filter(|i| i % 2 == 0)
}
use std::fmt;
fn one() -> impl fmt::Display {
1i32
}
// fn one(is_float: bool) -> impl fmt::Display {
// // error[E0308]: if and else have incompatible types
// if is_float {
// 1.0f32
// } else {
// 1i32
// }
// }
fn main() {
let mut _n = one();
_n = one();
}
#[allow(dead_code)]
fn gen_counter(init: i32) -> impl FnMut() -> i32 {
let mut n = init;
move || {
let ret = n;
n += 1;
ret
}
}
================================================
FILE: ch08/ex08/examples/ch08_09_associated_const.rs
================================================
// デカルト座標
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
pub struct CartesianCoord {
pub x: f64,
pub y: f64,
}
// `Dimension` を `CartesianCoord` に実装する
trait Dimension {
const DIMENSION: u32;
}
fn main() {
// 実装された型から定数を取り出す
#[allow(unused_variables)]
let dim = CartesianCoord::DIMENSION;
#[allow(dead_code)]
const DIM: u32 = CartesianCoord::DIMENSION;
}
// 09
impl Dimension for CartesianCoord {
const DIMENSION: u32 = 2;
}
================================================
FILE: ch08/ex08/examples/ch08_10_associated_type.rs
================================================
use std::str::FromStr;
trait Server {
// `type 型名`で関連型を宣言できる
type Response;
// あるいは`type 型名: トレイト境界` で境界を設定することもできる
type Request: FromStr;
// 関連型を参照するには`Self::型名`でアクセスする
fn handle(&self, req: Self::Request) -> Self::Response;
}
struct EchoServer;
// `Server` トレイトを実装する
impl Server for EchoServer {
// トップレベルと同じように`type 型名 = 型名`で定義できる
type Response = String;
// トレイト境界のついた型も同じように定義できる
// トレイト境界を満たさない型を書くとコンパイルエラーになる
type Request = String;
// 関連型を参照するには`Self::型名`でアクセスする
fn handle(&self, req: Self::Request) -> Self::Response {
req
}
}
// // `S::Response`のようにServerの関連型を参照できる
// // 関連型については特別指定しなければ任意の関連型を受け付ける
// fn handle<S: Server>(server: S, req: &str) -> S::Response {
// // 関連型にトレイト境界がついているのでトレイトの関数を呼び出すこともできる
// let req = S::Request::from_str(&req).unwrap();
// server.handle(req)
// }
// あるいは、関連型が特定の型を持っていることを指定したければ、`トレイト名<関連型名 = 型>`のように指定できる
// この場合RequestにStringを持つServerの実装しか受け付けない
fn handle<S: Server<Request = String>>(server: S, req: &str) -> S::Response {
server.handle(req.to_string())
}
fn main() {
let server = EchoServer;
assert_eq!(handle(server, "Hello"), "Hello");
}
// どこかに定義されたToJsonトレイトを実装して欲しいとする
trait ToJson {}
trait Server2 {
type Request: FromStr;
// 返り値にトレイトを書くことはできない
fn handle(&self, req: Self::Request) -> ToJson;
}
trait Foo<T> {}
trait Bar {
type T;
}
#[allow(dead_code)]
struct Baz;
impl Foo<i32> for Baz {}
impl Foo<char> for Baz {}
// impl Bar for Baz {
// type T = i32;
// }
// impl Bar for Baz {
// type T = char;
// }
================================================
FILE: ch08/ex08/examples/ch08_11_sized.rs
================================================
fn main() {
use std::mem::size_of;
println!("{}", size_of::<&i32>()); // -> 8
println!("{}", size_of::<&str>()); // -> 16
}
================================================
FILE: ch08/ex08/examples/ch08_12_trait_techniques.rs
================================================
#[derive(Debug)]
enum Either<A, B> {
A(A),
B(B),
}
use std::fmt;
impl<A, B> fmt::Display for Either<A, B>
where
A: fmt::Display,
B: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Either::A(a) => a.fmt(f),
Either::B(b) => b.fmt(f),
}
}
}
fn main() {
// `Vec<Either<bool, i32>>` として宣言しておく
let mut v: Vec<Either<bool, i32>> = vec![];
// Eitherの値を入れる
v.push(Either::A(true));
v.push(Either::B(1i32));
// すると `{}` で表示できる
for e in v {
println!("{}", e);
}
}
================================================
FILE: ch08/ex08/examples-java/Overload.java
================================================
import java.util.List;
import java.util.ArrayList;
public class Overload {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
// オーバーロードだとListとして扱われる
assert "List".equals(Overload.call(list));
}
// Listに対して定義する
static String call(List<String> list) {
return "List";
}
// ArrayListに対して定義する
static String call(ArrayList<String> list) {
return "ArrayList";
}
}
================================================
FILE: ch08/ex08/tests/integration_tests.rs
================================================
use cli_test_dir::{ExpectStatus, OutputExt, TestDir};
// examplesディレクトリ配下にあるバイナリを実行して入出力を確認するテスト
// `cargo test` で実行できる
#[test]
fn run_ch08_01() {
// use cli_test_dir::TeeOutputExt;
let testdir = TestDir::new("./examples/ch08_01_trait_basics", "Run ch08_01");
let output = testdir
.cmd()
// .tee_output() // stdoutとstderrorを順に表示する。デバッグに便利
.expect_success();
assert_eq!(
output.stdout_str(),
r#"x = 1, y = 1
r = 1.4142135623730951, θ = 0.7853981633974483
"#,
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_02() {
let testdir = TestDir::new("./examples/ch08_02_trait_basics", "Run ch08_02");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
r#"(0, 1)
(0.00000000000000006123233995736766, 1)
"#,
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_03() {
let testdir = TestDir::new("./examples/ch08_03_trait_basics", "Run ch08_03");
let output = testdir.cmd().expect_success();
assert_eq!(output.stdout_str(), "(-1, 0.00000000000000012246467991473532)\n");
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_04() {
let testdir = TestDir::new("./examples/ch08_04_trait_generics", "Run ch08_04");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_05() {
let testdir = TestDir::new("./examples/ch08_05_trait_generics", "Run ch08_05");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_06() {
let testdir = TestDir::new("./examples/ch08_06_overload", "Run ch08_06");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_07() {
let testdir = TestDir::new("./examples/ch08_07_trait_object", "Run ch08_07");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_08() {
let testdir = TestDir::new("./examples/ch08_08_existential_impl_trait", "Run ch08_08");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_09() {
let testdir = TestDir::new("./examples/ch08_09_associated_const", "Run ch08_09");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_10() {
let testdir = TestDir::new("./examples/ch08_10_associated_type", "Run ch08_10");
let output = testdir.cmd().expect_success();
assert!(output.stdout_str().is_empty());
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_11() {
let testdir = TestDir::new("./examples/ch08_11_sized", "Run ch08_11");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
r#"8
16
"#,
);
assert!(output.stderr_str().is_empty());
}
#[test]
fn run_ch08_12() {
let testdir = TestDir::new("./examples/ch08_12_trait_techniques", "Run ch08_12");
let output = testdir.cmd().expect_success();
assert_eq!(
output.stdout_str(),
r#"true
1
"#,
);
assert!(output.stderr_str().is_empty());
}
================================================
FILE: ch09/parser/.gitignore
================================================
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch09/parser/Cargo.toml
================================================
[package]
name = "parser"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
================================================
FILE: ch09/parser/src/main.rs
================================================
use std::error::Error as StdError;
use std::fmt;
use std::str::FromStr;
/// 位置情報。.0から.1までの区間を表す。
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Loc(usize, usize);
// loc に便利メソッドを実装しておく。
impl Loc {
fn merge(&self, other: &Loc) -> Loc {
use std::cmp::{max, min};
Loc(min(self.0, other.0), max(self.1, other.1))
}
}
/// アノテーション。値に様々なデータをもたせたもの。ここでは`Loc`をもたせている。
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Annot<T> {
value: T,
loc: Loc,
}
impl<T> Annot<T> {
fn new(value: T, loc: Loc) -> Self {
Self { value, loc }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TokenKind {
/// [0-9][0-9]*
Number(u64),
/// +
Plus,
/// -
Minus,
/// *
Asterisk,
/// /
Slash,
/// (
LParen,
/// )
RParen,
}
// `TokenKind` にアノテーションをつけたものを `Token` として定義しておく
type Token = Annot<TokenKind>;
// ヘルパーメソッドを定義しておく
impl Token {
fn number(n: u64, loc: Loc) -> Self {
Self::new(TokenKind::Number(n), loc)
}
fn plus(loc: Loc) -> Self {
Self::new(TokenKind::Plus, loc)
}
fn minus(loc: Loc) -> Self {
Self::new(TokenKind::Minus, loc)
}
fn asterisk(loc: Loc) -> Self {
Self::new(TokenKind::Asterisk, loc)
}
fn slash(loc: Loc) -> Self {
Self::new(TokenKind::Slash, loc)
}
fn lparen(loc: Loc) -> Self {
Self::new(TokenKind::LParen, loc)
}
fn rparen(loc: Loc) -> Self {
Self::new(TokenKind::RParen, loc)
}
}
// `TokenKind` と同様の実装をする
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum LexErrorKind {
InvalidChar(char),
Eof,
}
type LexError = Annot<LexErrorKind>;
impl LexError {
fn invalid_char(c: char, loc: Loc) -> Self {
LexError::new(LexErrorKind::InvalidChar(c), loc)
}
fn eof(loc: Loc) -> Self {
LexError::new(LexErrorKind::Eof, loc)
}
}
/// `pos` のバイトが期待するものであれば1バイト消費して `pos`を1進める
fn consume_byte(input: &[u8], pos: usize, b: u8) -> Result<(u8, usize), LexError> {
// posが入力サイズ以上なら入力が終わっている。
// 1バイト期待しているのに終わっているのでエラー
if input.len() <= pos {
return Err(LexError::eof(Loc(pos, pos)));
}
// 入力が期待するものでなければエラー
if input[pos] != b {
return Err(LexError::invalid_char(
input[pos] as char,
Loc(pos, pos + 1),
));
}
Ok((b, pos + 1))
}
fn recognize_many(input: &[u8], mut pos: usize, mut f: impl FnMut(u8) -> bool) -> usize {
while pos < input.len() && f(input[pos]) {
pos += 1;
}
pos
}
fn lex_number(input: &[u8], pos: usize) -> Result<(Token, usize), LexError> {
use std::str::from_utf8;
let start = pos;
let end = recognize_many(input, start, |b| b"1234567890".contains(&b));
let n = from_utf8(&input[start..end])
// start..posの構成から `from_utf8` は常に成功するため`unwrap`しても安全
.unwrap()
.parse()
// 同じく構成から `parse` は常に成功する
.unwrap();
Ok((Token::number(n, Loc(start, end)), end))
}
fn lex_plus(input: &[u8], start: usize) -> Result<(Token, usize), LexError> {
// `Result::map` を使うことで結果が正常だった場合の処理を簡潔に書ける。
// これはこのコードと等価
// ```
// match consume_byte(input, start, b'+') {
// Ok((_, end)) => (Token::plus(Loc(start, end)), end),
// Err(err) => Err(err),
// }
consume_byte(input, start, b'+').map(|(_, end)| (Token::plus(Loc(start, end)), end))
}
fn lex_minus(input: &[u8], start: usize) -> Result<(Token, usize), LexError> {
consume_byte(input, start, b'-').map(|(_, end)| (Token::minus(Loc(start, end)), end))
}
fn lex_asterisk(input: &[u8], start: usize) -> Result<(Token, usize), LexError> {
consume_byte(input, start, b'*').map(|(_, end)| (Token::asterisk(Loc(start, end)), end))
}
fn lex_slash(input: &[u8], start: usize) -> Result<(Token, usize), LexError> {
consume_byte(input, start, b'/').map(|(_, end)| (Token::slash(Loc(start, end)), end))
}
fn lex_lparen(input: &[u8], start: usize) -> Result<(Token, usize), LexError> {
consume_byte(input, start, b'(').map(|(_, end)| (Token::lparen(Loc(start, end)), end))
}
fn lex_rparen(input: &[u8], start: usize) -> Result<(Token, usize), LexError> {
consume_byte(input, start, b')').map(|(_, end)| (Token::rparen(Loc(start, end)), end))
}
fn skip_spaces(input: &[u8], pos: usize) -> Result<((), usize), LexError> {
let pos = recognize_many(input, pos, |b| b" \n\t".contains(&b));
Ok(((), pos))
}
/// 字句解析器
fn lex(input: &str) -> Result<Vec<Token>, LexError> {
// 解析結果を保存するベクタ
let mut tokens = Vec::new();
// 入力
let input = input.as_bytes();
// 位置を管理する値
let mut pos = 0;
// サブレキサを呼んだ後`pos`を更新するマクロ
macro_rules! lex_a_token {
($lexer:expr) => {{
let (tok, p) = $lexer?;
tokens.push(tok);
pos = p;
}};
}
while pos < input.len() {
// ここでそれぞれの関数に`input`と`pos`を渡す
match input[pos] {
// 遷移図通りの実装
b'0'...b'9' => lex_a_token!(lex_number(input, pos)),
b'+' => lex_a_token!(lex_plus(input, pos)),
b'-' => lex_a_token!(lex_minus(input, pos)),
b'*' => lex_a_token!(lex_asterisk(input, pos)),
b'/' => lex_a_token!(lex_slash(input, pos)),
b'(' => lex_a_token!(lex_lparen(input, pos)),
b')' => lex_a_token!(lex_rparen(input, pos)),
// 空白を扱う
b' ' | b'\n' | b'\t' => {
let ((), p) = skip_spaces(input, pos)?;
pos = p;
}
// それ以外がくるとエラー
b => return Err(LexError::invalid_char(b as char, Loc(pos, pos + 1))),
}
}
Ok(tokens)
}
#[test]
fn test_lexer() {
assert_eq!(
lex("1 + 2 * 3 - -10"),
Ok(vec![
Token::number(1, Loc(0, 1)),
Token::plus(Loc(2, 3)),
Token::number(2, Loc(4, 5)),
Token::asterisk(Loc(6, 7)),
Token::number(3, Loc(8, 9)),
Token::minus(Loc(10, 11)),
Token::minus(Loc(12, 13)),
Token::number(10, Loc(13, 15)),
])
)
}
/// 単項演算子を表すデータ型
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum UniOpKind {
/// 正号
Plus,
/// 負号
Minus,
}
type UniOp = Annot<UniOpKind>;
impl UniOp {
fn plus(loc: Loc) -> Self {
Self::new(UniOpKind::Plus, loc)
}
fn minus(loc: Loc) -> Self {
Self::new(UniOpKind::Minus, loc)
}
}
/// 二項演算子を表すデータ型
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum BinOpKind {
/// 加算
Add,
/// 減算
Sub,
/// 乗算
Mult,
/// 除算
Div,
}
type BinOp = Annot<BinOpKind>;
impl BinOp {
fn add(loc: Loc) -> Self {
Self::new(BinOpKind::Add, loc)
}
fn sub(loc: Loc) -> Self {
Self::new(BinOpKind::Sub, loc)
}
fn mult(loc: Loc) -> Self {
Self::new(BinOpKind::Mult, loc)
}
fn div(loc: Loc) -> Self {
Self::new(BinOpKind::Div, loc)
}
}
/// ASTを表すデータ型
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum AstKind {
/// 数値
Num(u64),
/// 単項演算
UniOp { op: UniOp, e: Box<Ast> },
/// 二項演算
BinOp { op: BinOp, l: Box<Ast>, r: Box<Ast> },
}
type Ast = Annot<AstKind>;
// ヘルパメソッドを定義しておく
impl Ast {
fn num(n: u64, loc: Loc) -> Self {
// impl<T> Annot<T>で実装したnewを呼ぶ
Self::new(AstKind::Num(n), loc)
}
fn uniop(op: UniOp, e: Ast, loc: Loc) -> Self {
Self::new(AstKind::UniOp { op, e: Box::new(e) }, loc)
}
fn binop(op: BinOp, l: Ast, r: Ast, loc: Loc) -> Self {
Self::new(
AstKind::BinOp {
op,
l: Box::new(l),
r: Box::new(r),
},
loc,
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum ParseError {
/// 予期しないトークンがきた
UnexpectedToken(Token),
/// 式を期待していたのに式でないものがきた
NotExpression(Token),
/// 演算子を期待していたのに演算子でないものがきた
NotOperator(Token),
/// 括弧が閉じられていない
UnclosedOpenParen(Token),
/// 式の解析が終わったのにまだトークンが残っている
RedundantExpression(Token),
/// パース途中で入力が終わった
Eof,
}
use std::iter::Peekable;
fn parse_left_binop<Tokens>(
tokens: &mut Peekable<Tokens>,
subexpr_parser: fn(&mut Peekable<Tokens>) -> Result<Ast, ParseError>,
op_parser: fn(&mut Peekable<Tokens>) -> Result<BinOp, ParseError>,
) -> Result<Ast, ParseError>
where
Tokens: Iterator<Item = Token>,
{
let mut e = subexpr_parser(tokens)?;
loop {
match tokens.peek() {
Some(_) => {
let op = match op_parser(tokens) {
Ok(op) => op,
// ここでパースに失敗したのはこれ以上中置演算子がないという意味
Err(_) => break,
};
let r = subexpr_parser(tokens)?;
let loc = e.loc.merge(&r.loc);
e = Ast::binop(op, e, r, loc)
}
_ => break,
}
}
Ok(e)
}
// atom
fn parse_atom<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, ParseError>
where
Tokens: Iterator<Item = Token>,
{
tokens
.next()
.ok_or(ParseError::Eof)
.and_then(|tok| match tok.value {
// UNUMBER
TokenKind::Number(n) => Ok(Ast::new(AstKind::Num(n), tok.loc)),
// | "(", EXPR3, ")" ;
TokenKind::LParen => {
let e = parse_expr(tokens)?;
match tokens.next() {
Some(Token {
value: TokenKind::RParen,
..
}) => Ok(e),
Some(t) => Err(ParseError::RedundantExpression(t)),
_ => Err(ParseError::UnclosedOpenParen(tok)),
}
}
_ => Err(ParseError::NotExpression(tok)),
})
}
// expr1
fn parse_expr1<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, ParseError>
where
Tokens: Iterator<Item = Token>,
{
match tokens.peek().map(|tok| tok.value) {
Some(TokenKind::Plus) | Some(TokenKind::Minus) => {
// ("+" | "-")
let op = match tokens.next() {
Some(Token {
value: TokenKind::Plus,
loc,
}) => UniOp::plus(loc),
Some(Token {
value: TokenKind::Minus,
loc,
}) => UniOp::minus(loc),
_ => unreachable!(),
};
// , ATOM
let e = parse_atom(tokens)?;
let loc = op.loc.merge(&e.loc);
Ok(Ast::uniop(op, e, loc))
}
// | ATOM
_ => parse_atom(tokens),
}
}
fn parse_expr2<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, ParseError>
where
Tokens: Iterator<Item = Token>,
{
// `parse_left_binop` に渡す関数を定義する
fn parse_expr2_op<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<BinOp, ParseError>
where
Tokens: Iterator<Item = Token>,
{
let op = tokens
.peek()
.ok_or(ParseError::Eof)
.and_then(|tok| match tok.value {
TokenKind::Asterisk => Ok(BinOp::mult(tok.loc.clone())),
TokenKind::Slash => Ok(BinOp::div(tok.loc.clone())),
_ => Err(ParseError::NotOperator(tok.clone())),
})?;
tokens.next();
Ok(op)
}
parse_left_binop(tokens, parse_expr1, parse_expr2_op)
}
fn parse_expr3<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, ParseError>
where
Tokens: Iterator<Item = Token>,
{
// `parse_left_binop` に渡す関数を定義する
fn parse_expr3_op<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<BinOp, ParseError>
where
Tokens: Iterator<Item = Token>,
{
let op = tokens
.peek()
// イテレータの終わりは入力の終端なのでエラーを出す。
.ok_or(ParseError::Eof)
// エラーを返すかもしれない値を繋げる
.and_then(|tok| match tok.value {
TokenKind::Plus => Ok(BinOp::add(tok.loc.clone())),
TokenKind::Minus => Ok(BinOp::sub(tok.loc.clone())),
_ => Err(ParseError::NotOperator(tok.clone())),
})?;
tokens.next();
Ok(op)
}
parse_left_binop(tokens, parse_expr2, parse_expr3_op)
}
fn parse_expr<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, ParseError>
where
Tokens: Iterator<Item = Token>,
{
// `parse_expr`は `parse_expr3` を呼ぶだけ
parse_expr3(tokens)
}
fn parse(tokens: Vec<Token>) -> Result<Ast, ParseError> {
// 入力をイテレータにし、 `Peekable` にする
let mut tokens = tokens.into_iter().peekable();
// その後 `parse_expr` を呼んでエラー処理をする
let ret = parse_expr(&mut tokens)?;
match tokens.next() {
Some(tok) => Err(ParseError::RedundantExpression(tok)),
None => Ok(ret),
}
}
#[test]
fn test_parser() {
// 1 + 2 * 3 - -10
let ast = parse(vec![
Token::number(1, Loc(0, 1)),
Token::plus(Loc(2, 3)),
Token::number(2, Loc(4, 5)),
Token::asterisk(Loc(6, 7)),
Token::number(3, Loc(8, 9)),
Token::minus(Loc(10, 11)),
Token::minus(Loc(12, 13)),
Token::number(10, Loc(13, 15)),
]);
assert_eq!(
ast,
Ok(Ast::binop(
BinOp::sub(Loc(10, 11)),
Ast::binop(
BinOp::add(Loc(2, 3)),
Ast::num(1, Loc(0, 1)),
Ast::binop(
BinOp::new(BinOpKind::Mult, Loc(6, 7)),
Ast::num(2, Loc(4, 5)),
Ast::num(3, Loc(8, 9)),
Loc(4, 9)
),
Loc(0, 9),
),
Ast::uniop(
UniOp::minus(Loc(12, 13)),
Ast::num(10, Loc(13, 15)),
Loc(12, 15)
),
Loc(0, 15)
))
)
}
impl FromStr for Ast {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// 内部では字句解析、構文解析の順に実行する
let tokens = lex(s)?;
let ast = parse(tokens)?;
Ok(ast)
}
}
/// 字句解析エラーと構文解析エラーを統合するエラー型
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum Error {
Lexer(LexError),
Parser(ParseError),
}
impl From<LexError> for Error {
fn from(e: LexError) -> Self {
Error::Lexer(e)
}
}
impl From<ParseError> for Error {
fn from(e: ParseError) -> Self {
Error::Parser(e)
}
}
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::TokenKind::*;
match self {
Number(n) => n.fmt(f),
Plus => write!(f, "+"),
Minus => write!(f, "-"),
Asterisk => write!(f, "*"),
Slash => write!(f, "/"),
LParen => write!(f, "("),
RParen => write!(f, ")"),
}
}
}
impl fmt::Display for Loc {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}-{}", self.0, self.1)
}
}
impl fmt::Display for LexError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::LexErrorKind::*;
let loc = &self.loc;
match self.value {
InvalidChar(c) => write!(f, "{}: invalid char '{}'", loc, c),
Eof => write!(f, "End of file"),
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::ParseError::*;
match self {
UnexpectedToken(tok) => write!(f, "{}: {} is not expected", tok.loc, tok.value),
NotExpression(tok) => write!(
f,
"{}: '{}' is not a start of expression",
tok.loc, tok.value
),
NotOperator(tok) => write!(f, "{}: '{}' is not an operator", tok.loc, tok.value),
UnclosedOpenParen(tok) => write!(f, "{}: '{}' is not closed", tok.loc, tok.value),
RedundantExpression(tok) => write!(
f,
"{}: expression after '{}' is redundant",
tok.loc, tok.value
),
Eof => write!(f, "End of file"),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "parser error")
}
}
impl StdError for LexError {}
impl StdError for ParseError {}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
use self::Error::*;
match self {
Lexer(lex) => Some(lex),
Parser(parse) => Some(parse),
}
}
}
/// `input` に対して `loc` の位置を強調表示する
fn print_annot(input: &str, loc: Loc) {
// 入力に対して
eprintln!("{}", input);
// 位置情報をわかりやすく示す
eprintln!("{}{}", " ".repeat(loc.0), "^".repeat(loc.1 - loc.0));
}
impl Error {
/// 診断メッセージを表示する
fn show_diagnostic(&self, input: &str) {
use self::Error::*;
use self::ParseError as P;
// エラー情報とその位置情報を取り出す。エラーの種類によって位置情報を調整する。
let (e, loc): (&StdError, Loc) = match self {
Lexer(e) => (e, e.loc.clone()),
Parser(e) => {
let loc = match e {
P::UnexpectedToken(Token { loc, .. })
| P::NotExpression(Token { loc, .. })
| P::NotOperator(Token { loc, .. })
| P::UnclosedOpenParen(Token { loc, .. }) => loc.clone(),
// redundant expressionはトークン以降行末までが余りなのでlocの終了位置を調整する
P::RedundantExpression(Token { loc, .. }) => Loc(loc.0, input.len()),
// EoFはloc情報を持っていないのでその場で作る
P::Eof => Loc(input.len(), input.len() + 1),
};
(e, loc)
}
};
// エラー情報を簡単に表示し
eprintln!("{}", e);
// エラー位置を指示する
print_annot(input, loc);
}
}
fn show_trace<E: StdError>(e: E) {
// エラーがあった場合そのエラーとcauseを全部出力する
eprintln!("{}", e);
let mut source = e.source();
// cause を全て辿って表示する
while let Some(e) = source {
eprintln!("caused by {}", e);
source = e.source()
}
// エラー表示のあとは次の入力を受け付ける
}
/// 評価器を表すデータ型
struct Interpreter;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum InterpreterErrorKind {
DivisionByZero,
}
type InterpreterError = Annot<InterpreterErrorKind>;
impl Interpreter {
pub fn new() -> Self {
Interpreter
}
pub fn eval(&mut self, expr: &Ast) -> Result<i64, InterpreterError> {
use self::AstKind::*;
match expr.value {
Num(n) => Ok(n as i64),
UniOp { ref op, ref e } => {
let e = self.eval(e)?;
Ok(self.eval_uniop(op, e))
}
BinOp {
ref op,
ref l,
ref r,
} => {
let l = self.eval(l)?;
let r = self.eval(r)?;
self.eval_binop(op, l, r)
.map_err(|e| InterpreterError::new(e, expr.loc.clone()))
}
}
}
fn eval_uniop(&mut self, op: &UniOp, n: i64) -> i64 {
use self::UniOpKind::*;
match op.value {
Plus => n,
Minus => -n,
}
}
fn eval_binop(&mut self, op: &BinOp, l: i64, r: i64) -> Result<i64, InterpreterErrorKind> {
use self::BinOpKind::*;
match op.value {
Add => Ok(l + r),
Sub => Ok(l - r),
Mult => Ok(l * r),
Div => {
if r == 0 {
Err(InterpreterErrorKind::DivisionByZero)
} else {
Ok(l / r)
}
}
}
}
}
impl fmt::Display for InterpreterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::InterpreterErrorKind::*;
match self.value {
DivisionByZero => write!(f, "division by zero"),
}
}
}
impl StdError for InterpreterError {
fn description(&self) -> &str {
use self::InterpreterErrorKind::*;
match self.value {
DivisionByZero => "the right hand expression of the division evaluates to zero",
}
}
}
impl InterpreterError {
fn show_diagnostic(&self, input: &str) {
// エラー情報を簡単に表示し
eprintln!("{}", self);
// エラー位置を指示する
print_annot(input, self.loc.clone());
}
}
/// 逆ポーランド記法へのコンパイラを表すデータ型
struct RpnCompiler;
impl RpnCompiler {
pub fn new() -> Self {
RpnCompiler
}
pub fn compile(&mut self, expr: &Ast) -> String {
let mut buf = String::new();
self.compile_inner(expr, &mut buf);
buf
}
pub fn compile_inner(&mut self, expr: &Ast, buf: &mut String) {
use self::AstKind::*;
match expr.value {
Num(n) => buf.push_str(&n.to_string()),
UniOp { ref op, ref e } => {
self.compile_uniop(op, buf);
self.compile_inner(e, buf)
}
BinOp {
ref op,
ref l,
ref r,
} => {
self.compile_inner(l, buf);
buf.push_str(" ");
self.compile_inner(r, buf);
buf.push_str(" ");
self.compile_binop(op, buf)
}
}
}
fn compile_uniop(&mut self, op: &UniOp, buf: &mut String) {
use self::UniOpKind::*;
match op.value {
Plus => buf.push_str("+"),
Minus => buf.push_str("-"),
}
}
fn compile_binop(&mut self, op: &BinOp, buf: &mut String) {
use self::BinOpKind::*;
match op.value {
Add => buf.push_str("+"),
Sub => buf.push_str("-"),
Mult => buf.push_str("*"),
Div => buf.push_str("/"),
}
}
}
use std::io;
/// プロンプトを表示しユーザの入力を促す
fn prompt(s: &str) -> io::Result<()> {
use std::io::{stdout, Write};
let stdout = stdout();
let mut stdout = stdout.lock();
stdout.write(s.as_bytes())?;
stdout.flush()
}
fn main() {
use std::io::{stdin, BufRead, BufReader};
let mut interp = Interpreter::new();
let mut compiler = RpnCompiler::new();
let stdin = stdin();
let stdin = stdin.lock();
let stdin = BufReader::new(stdin);
let mut lines = stdin.lines();
loop {
prompt("> ").unwrap();
// ユーザの入力を取得する
if let Some(Ok(line)) = lines.next() {
// `from_str` を実装したので`parse`が呼べる
let ast = match line.parse::<Ast>() {
Ok(ast) => ast,
Err(e) => {
e.show_diagnostic(&line);
show_trace(e);
continue;
}
};
println!("{:?}", ast);
let n = match interp.eval(&ast) {
Ok(n) => n,
Err(e) => {
e.show_diagnostic(&line);
show_trace(e);
continue;
}
};
println!("{}", n);
let rpn = compiler.compile(&ast);
println!("{}", rpn);
} else {
break;
}
}
}
================================================
FILE: ch10/wordcount/.gitignore
================================================
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch10/wordcount/Cargo.toml
================================================
[package]
name = "bicycle-book-wordcount"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
# ここから追記
license = "MIT OR Apache-2.0"
description = "シンプルな文字、単語、行の出現頻度の計数機能を提供します。"
readme = "README.md"
repository = "https://github.com/your_account/wordcount"
categories = ["command-line-utilities"]
keywords = ["example", "frequency", "text"]
exclude = ["text.txt", "tests/*"]
[badges]
appveyor = { repository = "your_account/wordcount" }
travis-ci = { repository = "your_account/wordcount" }
# 追記ここまで
[dependencies]
regex = "1.0"
================================================
FILE: ch10/wordcount/README.md
================================================
`bicycle_book_wordcount` はシンプルな文字、単語、行の出現頻度の計数機能を提供します。
CLIからは単語数の出現頻度が使えます。
```console
$ cargo run text.txt
{"bb": 1, "aa": 2, "cc": 1}
```
````
================================================
FILE: ch10/wordcount/src/lib.rs
================================================
//! `bicycle_book_wordcount` はシンプルな文字、単語、行の出現頻度の計数機能を提供します。
//! 詳しくは[`count`](fn.count.html)関数のドキュメントを見て下さい。
use regex::Regex;
use std::collections::HashMap;
use std::io::BufRead;
/// [`count`](fn.count.html)で使うオプション
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CountOption {
/// 文字毎に頻度を数える
Char,
/// 単語毎に頻度を数える
Word,
/// 行毎に頻度を数える
Line,
}
/// オプションのデフォルトは [`Word`](enum.CountOption.html#variant.Word)
impl Default for CountOption {
fn default() -> Self {
CountOption::Word
}
}
/// `input` から1行ずつUTF-8文字列を読み込み、頻度を数える。
///
/// 頻度を数える対象はオプションによって制御される。
/// * [`CountOption::Char`](enum.CountOption.html#variant.Char): Unicodeの1文字毎に頻度を数える
/// * [`CountOption::Word`](enum.CountOption.html#variant.Word): 正規表現 `\w+` にマッチする単語毎に頻度を数える
/// * [`CountOption::Line`](enum.CountOption.html#variant.Line): `\n`または`\r\n` で区切られた1行毎に頻度を数える
///
/// # Examples
/// 入力中の単語の出現頻度を数える例
///
/// ```
/// use std::io::Cursor;
/// use bicycle_book_wordcount::{count, CountOption};
///
/// let mut input = Cursor::new("aa bb cc bb");
/// let freq = count(input, CountOption::Word);
///
/// assert_eq!(freq["aa"], 1);
/// assert_eq!(freq["bb"], 2);
/// assert_eq!(freq["cc"], 1);
/// ```
///
/// # Panics
///
/// 入力がUTF-8でフォーマットされていない場合にパニックする。
pub fn count(input: impl BufRead, option: CountOption) -> HashMap<String, usize> {
let re = Regex::new(r"\w+").unwrap();
let mut freqs = HashMap::new(); // HashMap<String, usize>型
for line in input.lines() {
let line = line.unwrap();
use crate::CountOption::*;
match option {
Char => {
for c in line.chars() {
*freqs.entry(c.to_string()).or_insert(0) += 1;
}
}
Word =>
// 4. その行を単語で分割する
{
for m in re.find_iter(&line) {
let word = m.as_str().to_string();
// 5. 出現した単語の出現頻度を数える
*freqs.entry(word).or_insert(0) += 1;
}
}
Line => *freqs.entry(line.to_string()).or_insert(0) += 1,
}
}
freqs
}
#[test]
fn word_count_works() {
use std::io::Cursor;
let mut exp = HashMap::new();
exp.insert("aa".to_string(), 1);
exp.insert("bb".to_string(), 2);
exp.insert("cc".to_string(), 1);
assert_eq!(count(Cursor::new("aa bb cc bb"), CountOption::Word), exp);
}
#[test]
fn word_count_works2() {
use std::io::Cursor;
let mut exp = HashMap::new();
exp.insert("aa".to_string(), 1);
exp.insert("cc".to_string(), 1);
exp.insert("dd".to_string(), 1);
assert_eq!(count(Cursor::new("aa cc dd"), CountOption::Word), exp);
}
#[cfg(test)]
mod test {
use super::*;
use std::io::Cursor;
macro_rules! assert_map {
($expr: expr, {$($key: expr => $value:expr),*}) => {
$(assert_eq!($expr[$key], $value));*
};
}
#[test]
fn word_count_works3() {
let freqs = count(Cursor::new("aa cc dd"), CountOption::Word);
assert_eq!(freqs.len(), 3);
assert_map!(freqs, {"aa" => 1, "cc" => 1, "dd" => 1});
}
}
#[test]
#[should_panic]
fn word_count_do_not_contain_unknown_words() {
use std::io::Cursor;
count(
Cursor::new([
b'a', // a
0xf0, 0x90, 0x80, // でたらめなバイト列
0xe3, 0x81, 0x82, // あ
]),
CountOption::Word,
);
}
================================================
FILE: ch10/wordcount/src/main.rs
================================================
use std::env;
use std::fs::File;
use std::io::BufReader;
// libクレートに分離したものを使う
use bicycle_book_wordcount::count;
fn main() {
// 1. コマンドラインで指定された引数を読み込む
let filename = env::args().nth(1).expect("1 argument FILENAME required");
// 2. 指定されたファイルを開く
let file = File::open(filename).unwrap();
let reader = BufReader::new(&file);
// 3. ファイルから1行ずつ読み込む
// 第2引数 `Default::default` を加える
let freqs = count(reader, Default::default());
println!("{:?}", freqs);
}
================================================
FILE: ch10/wordcount/tests/char.rs
================================================
// アイテムのインポートも、もちろん必要
use std::io::Cursor;
use bicycle_book_wordcount::{count, CountOption};
#[macro_use]
mod utils;
// 以下にテストを書く
#[test]
fn char_count_works() {
let input = Cursor::new(b"abadracadabra");
let freq = count(input, CountOption::Char);
assert_map!(freq,
{
"a" => 6,
"b" => 2,
"c" => 1,
"d" => 2,
"r" => 2
}
);
}
#[test]
fn char_count_utf8() {
let input = Cursor::new(
r#"
天地玄黃
宇宙洪荒
日月盈昃
辰宿列張
"#,
);
let freq = count(input, CountOption::Char);
assert_eq!(freq.len(), 16);
for (_, count) in freq {
assert_eq!(count, 1);
}
}
================================================
FILE: ch10/wordcount/tests/line.rs
================================================
use std::io::Cursor;
use bicycle_book_wordcount::{count, CountOption};
#[macro_use]
mod utils;
// 以下にテストを書く
#[test]
fn line_count_works() {
let input = Cursor::new(
r#"Tokyo, Japan
Kyoto, Japan
Tokyo, Japan
Shanghai, China
"#,
);
let freq = count(input, CountOption::Line);
assert_map!(freq, {
"Tokyo, Japan" => 2,
"Kyoto, Japan" => 1,
"Shanghai, China" => 1
});
}
#[test]
fn line_count_lfcr() {
let input = Cursor::new("aa\r\nbb\r\ncc\r\nbb");
let freq = count(input, CountOption::Line);
assert_map!(freq, {
"aa" => 1,
"bb" => 2,
"cc" => 1
});
}
================================================
FILE: ch10/wordcount/tests/utils/mod.rs
================================================
macro_rules! assert_map {
($expr: expr, {$($key: expr => $value:expr),*}) => {
$(assert_eq!($expr[$key], $value));*
};
}
================================================
FILE: ch10/wordcount/text.txt
================================================
aa bb cc aa
================================================
FILE: ch11/log-collector/.gitignore
================================================
# コンパイラが生成する成果物や中間ファイルが置かれる
target/
================================================
FILE: ch11/log-collector/Cargo.toml
================================================
[workspace]
members = ["server", "api", "cli"]
================================================
FILE: ch11/log-collector/api/Cargo.toml
================================================
[package]
name = "api"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
serde = "1.0.8"
serde_derive = "1.0.8"
[dependencies.chrono]
features = ["serde"]
version = "0.4.0"
================================================
FILE: ch11/log-collector/api/src/lib.rs
================================================
use chrono::{DateTime, Utc};
use serde_derive::*;
// JSONの {"user_agent": "xxx", "response_time": 0, "timestamp": "yyyy-MM-dd+HH:mm:ss"}に対応
// 返り値で使うログはtimestampが`Option`ではない
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub struct Log {
pub user_agent: String,
pub response_time: i32,
pub timestamp: DateTime<Utc>,
}
// クエリパラメータの `?from=yyyy-MM-dd+HH:mm:ss&until=yyyy-MM-dd+HH:mm:ss` に対応
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub struct DateTimeRange {
pub from: Option<DateTime<Utc>>,
pub until: Option<DateTime<Utc>>,
}
pub mod csv {
pub mod get {
use crate::DateTimeRange;
pub type Query = DateTimeRange;
// getははファイルを返すのでResponse型の定義がない
}
pub mod post {
use serde_derive::*;
// CSVファイルを受け付けるのでリクエストデータはない
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Deserialize, Serialize)]
// 受領したログの数を返す
pub struct Response(pub usize);
}
}
pub mod logs {
pub mod get {
use crate::{DateTimeRange, Log};
use serde_derive::*;
pub type Query = DateTimeRange;
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Deserialize, Serialize)]
// 保存しているログをすべて返す
pub struct Response(pub Vec<Log>);
}
pub mod post {
use chrono::{DateTime, Utc};
use serde_derive::*;
// 説明した通りのデータを受け付ける
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, Deserialize, Serialize)]
pub struct Request {
pub user_agent: String,
pub response_time: i32,
pub timestamp: Option<DateTime<Utc>>,
}
// Acceptedを返すのでResponseデータ型の定義はない
}
}
================================================
FILE: ch11/log-collector/cli/Cargo.toml
================================================
[package]
name = "cli"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
clap = "2"
reqwest = "0.9"
csv = "1"
serde = "1"
serde_json = "1"
api = {path = "../api"}
[dependencies.chrono]
features = ["serde"]
version = "0.4"
================================================
FILE: ch11/log-collector/cli/src/main.rs
================================================
use clap::{App, AppSettings, Arg, SubCommand};
use clap::{_clap_count_exprs, arg_enum};
use reqwest::Client;
use std::io;
arg_enum! {
#[derive(Debug)]
enum Format {
Csv,
Json,
}
}
struct ApiClient {
server: String,
client: Client,
}
impl ApiClient {
fn post_logs(&self, req: &api::logs::post::Request) -> reqwest::Result<()> {
self.client
.post(&format!("http://{}/logs", &self.server))
.json(req)
.send()
.map(|_| ())
}
fn get_logs(&self) -> reqwest::Result<api::logs::get::Response> {
self.client
.get(&format!("http://{}/logs", &self.server))
.send()?
.json()
}
fn get_csv<W: io::Write>(&self, w: &mut W) -> reqwest::Result<u64> {
self.client
.get(&format!("http://{}/csv", &self.server))
.send()?
.copy_to(w)
}
}
fn do_post_csv(api_client: &ApiClient) {
let reader = csv::Reader::from_reader(io::stdin());
for log in reader.into_deserialize::<api::logs::post::Request>() {
let log = match log {
Ok(log) => log,
Err(e) => {
eprintln!("[WARN] failed to parse a line, skipping: {}", e);
continue;
}
};
api_client.post_logs(&log).expect("api request failed");
}
}
fn do_get_json(api_client: &ApiClient) {
let res = api_client.get_logs().expect("api request failed");
let json_str = serde_json::to_string(&res).unwrap();
println!("{}", json_str);
}
fn do_get_csv(api_client: &ApiClient) {
let out = io::stdout();
let mut out = out.lock();
api_client.get_csv(&mut out).expect("api request failed");
}
fn main() {
let opts = App::new(env!("CARGO_PKG_NAME"))
.about(env!("CARGO_PKG_DESCRIPTION"))
.version(env!("CARGO_PKG_VERSION"))
.author(env!("CARGO_PKG_AUTHORS"))
// 以上がほぼテンプレート
.setting(AppSettings::SubcommandRequiredElseHelp)
// -s URL | --server URL のオプションを受け付ける
.arg(
Arg::with_name("SERVER")
.short("s")
.long("server")
.value_name("URL")
.help("server url")
.takes_value(true),
)
// サブコマンドとして `post` を受け付ける
.subcommand(SubCommand::with_name("post").about("post logs, taking input from stdin"))
// サブコマンドとして `get` を受け付ける
.subcommand(
SubCommand::with_name("get").about("get logs").arg(
Arg::with_name("FORMAT")
.help("log format")
.short("f")
.long("format")
.takes_value(true)
// "csv", "json" のみを受け付ける
.possible_values(&Format::variants())
.case_insensitive(true),
),
);
let matches = opts.get_matches();
let server = matches
.value_of("SERVER")
.unwrap_or("localhost:3000")
// .into()が増えた
.into();
let client = Client::new();
let api_client = ApiClient { server, client };
match matches.subcommand() {
("get", sub_match) => {
let format = sub_match
.and_then(|m| m.value_of("FORMAT"))
.map(|m| m.parse().unwrap())
.unwrap();
match format {
Format::Csv => do_get_csv(&api_client),
Format::Json => do_get_json(&api_client),
}
}
("post", _) => do_post_csv(&api_client),
_ => unreachable!(),
}
}
================================================
FILE: ch11/log-collector/docker-compose.yml
================================================
# いろいろ書かれていますが、ローカルホストの5432番ポートにユーザ名postgres、パスワードpasswordのデータベースサーバを立てる設定です
postgres-data:
image: busybox
volumes:
- /var/lib/postgresql/log-collector-data
container_name: log-collector-postgres-datastore
postgresql:
image: postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
ports:
- "5432:5432"
volumes_from:
- postgres-data
================================================
FILE: ch11/log-collector/server/Cargo.toml
================================================
[package]
name = "server"
version = "0.1.0"
authors = ["Rust Bicycle Book <bicycle-book@example.com>"]
edition = "2018"
[dependencies]
env_logger = "0.6"
log = "0.4"
actix-web = "0.7"
failure = "0.1"
api = {path = "../api"}
dotenv = "0.13"
chrono = "0.4"
csv = "1"
actix-web-multipart-file = "0.1"
futures = "0.1"
itertools = "0.8"
[dependencies.diesel]
features = ["postgres", "chrono", "r2d2"]
version = "1.4"
================================================
FILE: ch11/log-collector/server/diesel.toml
================================================
# For documentation on how to configure this file,
# see diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "src/schema.rs"
================================================
FILE: ch11/log-collector/server/migrations/.gitkeep
================================================
================================================
FILE: ch11/log-collector/server/migrations/00000000000000_diesel_initial_setup/down.sql
================================================
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
DROP FUNCTION IF EXISTS diesel_set_updated_at();
================================================
FILE: ch11/log-collector/server/migrations/00000000000000_diesel_initial_setup/up.sql
================================================
-- This file was automatically created by Diesel to setup helper functions
-- and other internal bookkeeping. This file is safe to edit, any future
-- changes will be added to existing projects as new migrations.
-- Sets up a trigger for the given table to automatically set a column called
-- `updated_at` whenever the row is modified (unless `updated_at` was included
-- in the modified columns)
--
-- # Example
--
-- ```sql
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
--
-- SELECT diesel_manage_updated_at('users');
-- ```
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
BEGIN
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
BEGIN
IF (
NEW IS DISTINCT FROM OLD AND
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
) THEN
NEW.updated_at := current_timestamp;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
================================================
FILE: ch11/log-collector/server/migrations/2018-12-28-161332_create_logs/down.sql
================================================
-- This file should undo anything in `up.sql`
DROP TABLE IF EXISTS logs;
================================================
FILE: ch11/log-collector/server/migrations/2018-12-28-161332_create_logs/up.sql
================================================
-- Your SQL goes here
CREATE TABLE logs (
id BIGSERIAL NOT NULL,
user_agent VARCHAR NOT NULL,
response_time INT NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (id)
);
================================================
FILE: ch11/log-collector/server/src/db.rs
================================================
use crate::model::*;
use chrono::{DateTime, Utc};
use diesel::insert_into;
use diesel::prelude::*;
use diesel::result::QueryResult;
pub fn insert_log(cn: &PgConnection, log: &NewLog) -> QueryResult<i64> {
use crate::schema::logs::dsl;
insert_into(dsl::logs)
.values(log)
.returning(dsl::id)
.get_result(cn)
}
pub fn insert_logs(cn: &PgConnection, logs: &[NewLog]) -> QueryResult<Vec<i64>> {
use crate::schema::logs::dsl;
insert_into(dsl::logs)
.values(logs)
.returning(dsl::id)
.load(cn)
}
pub fn logs(
cn: &PgConnection,
from: Option<DateTime<Utc>>,
until: Option<DateTime<Utc>>,
) -> QueryResult<Vec<Log>> {
use crate::schema::logs::dsl;
// 型エラーを防ぐためにinto_boxedを呼んでおく
let mut query = dsl::logs.into_boxed();
if let Some(f
gitextract_qeqog3z0/
├── .circleci/
│ └── config.yml
├── .gitignore
├── LICENSE
├── README.md
├── RELEASES.md
├── ch02/
│ ├── ex02/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── examples/
│ │ │ └── println.rs
│ │ └── tests/
│ │ └── integration_tests.rs
│ ├── examples/
│ │ └── README.md
│ ├── hello/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── tests/
│ │ └── integration_tests.rs
│ └── rpn/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── src/
│ │ └── main.rs
│ └── tests/
│ └── integration_tests.rs
├── ch03/
│ └── bitonic-sorter/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ └── benchmark.rs
│ ├── py-src/
│ │ ├── .gitignore
│ │ └── bitonic_sorter.py
│ ├── src/
│ │ ├── first.rs
│ │ ├── fourth.rs
│ │ ├── lib.rs
│ │ ├── second.rs
│ │ ├── third.rs
│ │ └── utils.rs
│ └── tests/
│ └── integration_tests.rs
├── ch04/
│ └── ex04/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ ├── ch04_01_unit.rs
│ │ ├── ch04_02_bool.rs
│ │ ├── ch04_03_integer.rs
│ │ ├── ch04_04_overflowed1.rs
│ │ ├── ch04_05_overflowed2.rs
│ │ ├── ch04_06_float.rs
│ │ ├── ch04_07_char.rs
│ │ ├── ch04_08_reference1.rs
│ │ ├── ch04_09_reference2.rs
│ │ ├── ch04_10_raw_pointer.rs
│ │ ├── ch04_11_fn_pointer.rs
│ │ ├── ch04_12_fn_pointer_vs_closure.rs
│ │ ├── ch04_13_tuple.rs
│ │ ├── ch04_14_array.rs
│ │ ├── ch04_15_slice1.rs
│ │ ├── ch04_16_slice2.rs
│ │ └── ch04_17_str.rs
│ └── tests/
│ └── integration_tests.rs
├── ch05/
│ └── ex05/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ ├── ch05_01_box.rs
│ │ ├── ch05_02_vec.rs
│ │ ├── ch05_03_boxed_slice.rs
│ │ ├── ch05_04_hash_map.rs
│ │ ├── ch05_05_string1.rs
│ │ ├── ch05_06_string2.rs
│ │ ├── ch05_07_string3.rs
│ │ ├── ch05_08_string4.rs
│ │ ├── ch05_09_range.rs
│ │ ├── ch05_10_option.rs
│ │ ├── ch05_11_result.rs
│ │ ├── ch05_12_type_alias.rs
│ │ ├── ch05_13_struct1.rs
│ │ ├── ch05_14_struct2.rs
│ │ ├── ch05_15_struct3.rs
│ │ ├── ch05_16_struct4.rs
│ │ ├── ch05_17_struct5.rs
│ │ ├── ch05_18_enum1.rs
│ │ ├── ch05_19_enum2.rs
│ │ ├── ch05_20_adv_types1.rs
│ │ ├── ch05_21_adv_types2.rs
│ │ ├── ch05_22_type_cast1.rs
│ │ ├── ch05_23_type_cast2.rs
│ │ ├── ch05_24_transmute.rs
│ │ ├── ch05_25_type_coercion1.rs
│ │ ├── ch05_26_type_coercion2.rs
│ │ ├── ch05_27_type_coercion3.rs
│ │ ├── ch05_28_type_coercion4.rs
│ │ └── ch05_29_type_coercion5.rs
│ └── tests/
│ └── integration_tests.rs
├── ch06/
│ └── leap-year/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── src/
│ │ └── main.rs
│ └── tests/
│ └── integration_tests.rs
├── ch07/
│ ├── ex07/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── examples/
│ │ │ ├── ch07_01_value_scope.rs
│ │ │ ├── ch07_02_move_semantics.rs
│ │ │ ├── ch07_03_nll.rs
│ │ │ ├── ch07_04_static_lifetime.rs
│ │ │ ├── ch07_05_rc.rs
│ │ │ ├── ch07_06_simple_refcell.rs
│ │ │ ├── ch07_07_tls_refcell.rs
│ │ │ ├── ch07_08_arc_rwlock.rs
│ │ │ ├── ch07_09_static_rwlock.rs
│ │ │ └── ch07_10_closure.rs
│ │ └── tests/
│ │ └── integration_tests.rs
│ └── toy-vec/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ ├── toy_vec_01.rs
│ │ ├── toy_vec_02.rs
│ │ └── toy_vec_03.rs
│ ├── src/
│ │ └── lib.rs
│ └── tests/
│ └── integration_tests.rs
├── ch08/
│ └── ex08/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── examples/
│ │ ├── ch08_01_trait_basics.rs
│ │ ├── ch08_02_trait_basics.rs
│ │ ├── ch08_03_trait_basics.rs
│ │ ├── ch08_04_trait_generics.rs
│ │ ├── ch08_05_trait_generics.rs
│ │ ├── ch08_06_overload.rs
│ │ ├── ch08_07_trait_object.rs
│ │ ├── ch08_08_existential_impl_trait.rs
│ │ ├── ch08_09_associated_const.rs
│ │ ├── ch08_10_associated_type.rs
│ │ ├── ch08_11_sized.rs
│ │ └── ch08_12_trait_techniques.rs
│ ├── examples-java/
│ │ └── Overload.java
│ └── tests/
│ └── integration_tests.rs
├── ch09/
│ └── parser/
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src/
│ └── main.rs
├── ch10/
│ └── wordcount/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── README.md
│ ├── src/
│ │ ├── lib.rs
│ │ └── main.rs
│ ├── tests/
│ │ ├── char.rs
│ │ ├── line.rs
│ │ └── utils/
│ │ └── mod.rs
│ └── text.txt
├── ch11/
│ ├── log-collector/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── api/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ ├── cli/
│ │ │ ├── Cargo.toml
│ │ │ └── src/
│ │ │ └── main.rs
│ │ ├── docker-compose.yml
│ │ ├── server/
│ │ │ ├── Cargo.toml
│ │ │ ├── diesel.toml
│ │ │ ├── migrations/
│ │ │ │ ├── .gitkeep
│ │ │ │ ├── 00000000000000_diesel_initial_setup/
│ │ │ │ │ ├── down.sql
│ │ │ │ │ └── up.sql
│ │ │ │ └── 2018-12-28-161332_create_logs/
│ │ │ │ ├── down.sql
│ │ │ │ └── up.sql
│ │ │ └── src/
│ │ │ ├── db.rs
│ │ │ ├── handlers.rs
│ │ │ ├── main.rs
│ │ │ ├── model.rs
│ │ │ └── schema.rs
│ │ └── test.csv
│ ├── start-aw/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── static/
│ │ └── test.txt
│ ├── static-files/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ └── templates/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── src/
│ │ └── main.rs
│ └── templates/
│ └── index.html.tera
├── ch12/
│ ├── c-api/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── main.c
│ │ └── src/
│ │ └── lib.rs
│ ├── cffi-ownership/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ ├── c_src/
│ │ │ └── ownership.c
│ │ └── src/
│ │ └── main.rs
│ ├── cffi_readline.rs
│ ├── ffi-global/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ ├── onigmo-rs/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── onigmo/
│ │ │ ├── Cargo.toml
│ │ │ ├── examples/
│ │ │ │ └── simple.rs
│ │ │ └── src/
│ │ │ └── lib.rs
│ │ └── onigmo-sys/
│ │ ├── Cargo.toml
│ │ ├── build.rs
│ │ ├── examples/
│ │ │ └── simple.rs
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── wrapper.h
│ ├── opaque/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ ├── ptr.rs
│ ├── ptr_ownership.rs
│ ├── repr-c/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── main.rs
│ ├── small_cffi.rs
│ └── static-link/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── build.rs
│ ├── c_src/
│ │ └── fib.c
│ └── src/
│ └── main.rs
├── howto/
│ └── running-msvc-compiler.md
└── install/
├── docker.md
└── windows10-vcpkg.md
SYMBOL INDEX (596 symbols across 130 files)
FILE: ch02/ex02/examples/println.rs
function main (line 1) | fn main() {
FILE: ch02/ex02/tests/integration_tests.rs
constant CMD (line 6) | const CMD: &'static str = "./examples/println";
function verify_output (line 9) | fn verify_output() {
FILE: ch02/hello/src/main.rs
function main (line 2) | fn main() {
FILE: ch02/hello/tests/integration_tests.rs
function run_hello (line 7) | fn run_hello() {
FILE: ch02/rpn/src/main.rs
function main (line 1) | fn main() {
function rpn (line 18) | fn rpn(exp: &str) -> f64 {
function apply2 (line 50) | fn apply2<F>(stack: &mut Vec<f64>, fun: F)
FILE: ch02/rpn/tests/integration_tests.rs
function run_rpn (line 7) | fn run_rpn() {
FILE: ch03/bitonic-sorter/examples/benchmark.rs
function main (line 14) | fn main() {
function run_sorts (line 33) | fn run_sorts(bits: u32) {
function timed_sort (line 64) | fn timed_sort<F>(sorter: &F, len: usize, name: &str) -> f64
FILE: ch03/bitonic-sorter/py-src/bitonic_sorter.py
function sort (line 5) | def sort(x, up):
function _sub_sort (line 28) | def _sub_sort(x, up):
function _compare_and_swap (line 52) | def _compare_and_swap(x, up):
FILE: ch03/bitonic-sorter/src/first.rs
function sort (line 7) | pub fn sort(x: &mut [u32], up: bool) {
function sub_sort (line 16) | fn sub_sort(x: &mut [u32], up: bool) {
function compare_and_swap (line 25) | fn compare_and_swap(x: &mut [u32], up: bool) {
function sort_u32_ascending (line 43) | fn sort_u32_ascending() {
function sort_u32_descending (line 57) | fn sort_u32_descending() {
FILE: ch03/bitonic-sorter/src/fourth.rs
constant PARALLEL_THRESHOLD (line 6) | const PARALLEL_THRESHOLD: usize = 4096;
function sort (line 8) | pub fn sort<T: Ord + Send>(x: &mut [T], order: &SortOrder) -> Result<(),...
function sort_by (line 15) | pub fn sort_by<T, F>(x: &mut [T], comparator: &F) -> Result<(), String>
function do_sort (line 27) | fn do_sort<T, F>(x: &mut [T], forward: bool, comparator: &F)
function sub_sort (line 50) | fn sub_sort<T, F>(x: &mut [T], forward: bool, comparator: &F)
function compare_and_swap (line 68) | fn compare_and_swap<T, F>(x: &mut [T], forward: bool, comparator: &F)
type Student (line 91) | struct Student {
method new (line 98) | fn new(first_name: &str, last_name: &str, age: u8) -> Self {
function sort_to_fail (line 108) | fn sort_to_fail() {
function sort_u32_ascending (line 114) | fn sort_u32_ascending() {
function sort_u32_descending (line 121) | fn sort_u32_descending() {
function sort_u32_large (line 128) | fn sort_u32_large() {
function sort_str_ascending (line 142) | fn sort_str_ascending() {
function sort_str_descending (line 149) | fn sort_str_descending() {
function sort_students_by_age_ascending (line 156) | fn sort_students_by_age_ascending() {
function sort_students_by_name_ascending (line 175) | fn sort_students_by_name_ascending() {
FILE: ch03/bitonic-sorter/src/lib.rs
type SortOrder (line 15) | pub enum SortOrder {
FILE: ch03/bitonic-sorter/src/second.rs
function sort (line 4) | pub fn sort<T: Ord>(x: &mut [T], order: &SortOrder) -> Result<(), String> {
function do_sort (line 16) | fn do_sort<T: Ord>(x: &mut [T], up: bool) {
function sub_sort (line 25) | fn sub_sort<T: Ord>(x: &mut [T], up: bool) {
function compare_and_swap (line 34) | fn compare_and_swap<T: Ord>(x: &mut [T], up: bool) {
function sort_to_fail (line 49) | fn sort_to_fail() {
function sort_u32_ascending (line 55) | fn sort_u32_ascending() {
function sort_u32_descending (line 62) | fn sort_u32_descending() {
function sort_str_ascending (line 69) | fn sort_str_ascending() {
function sort_str_descending (line 77) | fn sort_str_descending() {
FILE: ch03/bitonic-sorter/src/third.rs
function sort (line 4) | pub fn sort<T: Ord>(x: &mut [T], order: &SortOrder) -> Result<(), String> {
function sort_by (line 14) | pub fn sort_by<T, F>(x: &mut [T], comparator: &F) -> Result<(), String>
function do_sort (line 25) | fn do_sort<T, F>(x: &mut [T], forward: bool, comparator: &F)
function sub_sort (line 40) | fn sub_sort<T, F>(x: &mut [T], forward: bool, comparator: &F)
function compare_and_swap (line 51) | fn compare_and_swap<T, F>(x: &mut [T], forward: bool, comparator: &F)
type Student (line 81) | struct Student {
method new (line 91) | fn new(first_name: &str, last_name: &str, age: u8) -> Self {
function sort_to_fail (line 104) | fn sort_to_fail() {
function sort_u32_ascending (line 110) | fn sort_u32_ascending() {
function sort_u32_descending (line 117) | fn sort_u32_descending() {
function sort_u32_large (line 124) | fn sort_u32_large() {
function sort_str_ascending (line 141) | fn sort_str_ascending() {
function sort_str_descending (line 148) | fn sort_str_descending() {
function sort_students_by_age_ascending (line 156) | fn sort_students_by_age_ascending() {
function sort_students_by_name_ascending (line 183) | fn sort_students_by_name_ascending() {
FILE: ch03/bitonic-sorter/src/utils.rs
function new_u32_vec (line 5) | pub fn new_u32_vec(n: usize) -> Vec<u32> {
function is_sorted_ascending (line 16) | pub fn is_sorted_ascending<T: Ord>(x: &[T]) -> bool {
function is_sorted_descending (line 29) | pub fn is_sorted_descending<T: Ord>(x: &[T]) -> bool {
FILE: ch03/bitonic-sorter/tests/integration_tests.rs
function run_benchmark (line 7) | fn run_benchmark() {
FILE: ch04/ex04/examples/ch04_01_unit.rs
function hello (line 2) | fn hello() {
function main (line 6) | fn main() {
FILE: ch04/ex04/examples/ch04_02_bool.rs
function main (line 2) | fn main() {
FILE: ch04/ex04/examples/ch04_03_integer.rs
function main (line 2) | fn main() {
FILE: ch04/ex04/examples/ch04_04_overflowed1.rs
function main (line 1) | fn main() {
FILE: ch04/ex04/examples/ch04_05_overflowed2.rs
function main (line 1) | fn main() {
FILE: ch04/ex04/examples/ch04_06_float.rs
function main (line 2) | fn main() {
FILE: ch04/ex04/examples/ch04_07_char.rs
function main (line 2) | fn main() {
FILE: ch04/ex04/examples/ch04_08_reference1.rs
function f1 (line 3) | fn f1(mut n: u32) {
function f2 (line 10) | fn f2(n_ptr: &mut u32) {
function main (line 18) | fn main() {
FILE: ch04/ex04/examples/ch04_09_reference2.rs
function main (line 1) | fn main() {
FILE: ch04/ex04/examples/ch04_10_raw_pointer.rs
function main (line 1) | fn main() {
FILE: ch04/ex04/examples/ch04_11_fn_pointer.rs
function double (line 2) | fn double(n: i32) -> i32 {
function abs (line 7) | fn abs(n: i32) -> i32 {
function main (line 11) | fn main() {
FILE: ch04/ex04/examples/ch04_12_fn_pointer_vs_closure.rs
function main (line 1) | fn main() {
FILE: ch04/ex04/examples/ch04_13_tuple.rs
function main (line 1) | fn main() {
FILE: ch04/ex04/examples/ch04_14_array.rs
function main (line 1) | fn main() {
FILE: ch04/ex04/examples/ch04_15_slice1.rs
function print_info (line 2) | fn print_info(name: &str, sl: &[char]) {
function main (line 13) | fn main() {
FILE: ch04/ex04/examples/ch04_16_slice2.rs
function main (line 1) | fn main() {
FILE: ch04/ex04/examples/ch04_17_str.rs
function main (line 1) | fn main() {
FILE: ch04/ex04/tests/integration_tests.rs
function run_ch04_01 (line 7) | fn run_ch04_01() {
function run_ch04_02 (line 15) | fn run_ch04_02() {
function run_ch04_03 (line 23) | fn run_ch04_03() {
function run_ch04_04 (line 31) | fn run_ch04_04() {
function run_ch04_05 (line 42) | fn run_ch04_05() {
function run_ch04_06 (line 50) | fn run_ch04_06() {
function run_ch04_07 (line 58) | fn run_ch04_07() {
function run_ch04_08 (line 66) | fn run_ch04_08() {
function run_ch04_09 (line 87) | fn run_ch04_09() {
function run_ch04_10 (line 95) | fn run_ch04_10() {
function run_ch04_11 (line 103) | fn run_ch04_11() {
function run_ch04_12 (line 111) | fn run_ch04_12() {
function run_ch04_13 (line 119) | fn run_ch04_13() {
function run_ch04_14 (line 127) | fn run_ch04_14() {
function run_ch04_15 (line 138) | fn run_ch04_15() {
function run_ch04_16 (line 163) | fn run_ch04_16() {
function run_ch04_17 (line 171) | fn run_ch04_17() {
FILE: ch05/ex05/examples/ch05_01_box.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_02_vec.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_03_boxed_slice.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_04_hash_map.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_05_string1.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_06_string2.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_07_string3.rs
function f1 (line 9) | fn f1(name: &str) -> String {
function main (line 13) | fn main() {
FILE: ch05/ex05/examples/ch05_08_string4.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_09_range.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_10_option.rs
function main (line 1) | fn main() {
function add_elems (line 55) | fn add_elems(s: &[i32]) -> Option<i32> {
FILE: ch05/ex05/examples/ch05_11_result.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_12_type_alias.rs
type UserName (line 1) | type UserName = String;
type Id (line 2) | type Id = i64;
type Timestamp (line 3) | type Timestamp = i64;
type User (line 4) | type User = (Id, UserName, Timestamp);
function new_user (line 6) | fn new_user(name: UserName, id: Id, created: Timestamp) -> User {
function main (line 10) | fn main() {
FILE: ch05/ex05/examples/ch05_13_struct1.rs
type Polygon (line 1) | struct Polygon {
function new_polygon (line 10) | fn new_polygon(vertexes: Vec<(i32, i32)>) -> Polygon {
function main (line 16) | fn main() {
FILE: ch05/ex05/examples/ch05_14_struct2.rs
type Polygon (line 3) | struct Polygon {
method default (line 10) | fn default() -> Self {
function main (line 19) | fn main() {
FILE: ch05/ex05/examples/ch05_15_struct3.rs
type Triangle (line 1) | struct Triangle(Vertex, Vertex, Vertex);
type Vertex (line 2) | struct Vertex(i32, i32);
function main (line 4) | fn main() {
FILE: ch05/ex05/examples/ch05_16_struct4.rs
type UserName (line 1) | struct UserName(String);
type Id (line 2) | struct Id(u64);
type Timestamp (line 3) | struct Timestamp(u64);
type User (line 4) | type User = (Id, UserName, Timestamp);
function new_user (line 7) | fn new_user(name: UserName, id: Id, created: Timestamp) -> User {
function main (line 12) | fn main() {
FILE: ch05/ex05/examples/ch05_17_struct5.rs
type UniqueValue (line 2) | struct UniqueValue;
function main (line 7) | fn main() {
FILE: ch05/ex05/examples/ch05_18_enum1.rs
type Weekday (line 6) | enum Weekday {
type Month (line 13) | enum Month {
function say_something (line 18) | fn say_something(weekday: Weekday) {
function main (line 26) | fn main() {
FILE: ch05/ex05/examples/ch05_19_enum2.rs
type UserName (line 1) | type UserName = String;
type Task (line 5) | enum Task {
function main (line 18) | fn main() {
FILE: ch05/ex05/examples/ch05_20_adv_types1.rs
type Polygon (line 3) | pub struct Polygon<T> {
type Coordinates (line 11) | trait Coordinates {}
type CartesianCoord (line 16) | struct CartesianCoord {
type PolarCoord (line 25) | struct PolarCoord {
function main (line 31) | fn main() {
FILE: ch05/ex05/examples/ch05_21_adv_types2.rs
type A (line 2) | struct A {
function main (line 8) | fn main() {
FILE: ch05/ex05/examples/ch05_22_type_cast1.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_23_type_cast2.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_24_transmute.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/examples/ch05_25_type_coercion1.rs
function main (line 1) | fn main() {
function intro (line 7) | fn intro() {
function coercion_sites (line 34) | fn coercion_sites() {
function transitivity (line 39) | fn transitivity() {
FILE: ch05/ex05/examples/ch05_26_type_coercion2.rs
function f1 (line 1) | fn f1(n: &mut usize, str: &str, slice: &[i32]) {
function main (line 5) | fn main() {
FILE: ch05/ex05/examples/ch05_27_type_coercion3.rs
function f1 (line 1) | fn f1(slice: &[usize]) -> usize {
function f2 (line 5) | fn f2(slice: &mut [usize]) {
function main (line 11) | fn main() {
FILE: ch05/ex05/examples/ch05_28_type_coercion4.rs
function f1 (line 1) | fn f1(p: &[i32]) -> i32 { p[0] }
function f2 (line 2) | fn f2(p: Box<[i32]>) -> i32 { p[0] }
function main (line 4) | fn main() {
FILE: ch05/ex05/examples/ch05_29_type_coercion5.rs
function main (line 1) | fn main() {
FILE: ch05/ex05/tests/integration_tests.rs
function run_ch05_01 (line 7) | fn run_ch05_01() {
function run_ch05_02 (line 15) | fn run_ch05_02() {
function run_ch05_03 (line 23) | fn run_ch05_03() {
function run_ch05_04 (line 36) | fn run_ch05_04() {
function run_ch05_05 (line 44) | fn run_ch05_05() {
function run_ch05_06 (line 52) | fn run_ch05_06() {
function run_ch05_07 (line 63) | fn run_ch05_07() {
function run_ch05_08 (line 71) | fn run_ch05_08() {
function run_ch05_09 (line 79) | fn run_ch05_09() {
function run_ch05_10 (line 87) | fn run_ch05_10() {
function run_ch05_11 (line 95) | fn run_ch05_11() {
function run_ch05_12 (line 106) | fn run_ch05_12() {
function run_ch05_13 (line 114) | fn run_ch05_13() {
function run_ch05_14 (line 122) | fn run_ch05_14() {
function run_ch05_15 (line 130) | fn run_ch05_15() {
function run_ch05_16 (line 138) | fn run_ch05_16() {
function run_ch05_17 (line 146) | fn run_ch05_17() {
function run_ch05_18 (line 154) | fn run_ch05_18() {
function run_ch05_19 (line 162) | fn run_ch05_19() {
function run_ch05_20 (line 176) | fn run_ch05_20() {
function run_ch05_21 (line 184) | fn run_ch05_21() {
function run_ch05_22 (line 204) | fn run_ch05_22() {
function run_ch05_23 (line 212) | fn run_ch05_23() {
function run_ch05_24 (line 220) | fn run_ch05_24() {
function run_ch05_25 (line 233) | fn run_ch05_25() {
function run_ch05_26 (line 241) | fn run_ch05_26() {
function run_ch05_27 (line 249) | fn run_ch05_27() {
function run_ch05_28 (line 257) | fn run_ch05_28() {
function run_ch05_29 (line 265) | fn run_ch05_29() {
FILE: ch06/leap-year/src/main.rs
function main (line 10) | fn main() {
function is_leap_year (line 25) | fn is_leap_year(year: u32) -> bool {
FILE: ch06/leap-year/tests/integration_tests.rs
constant CMD (line 6) | const CMD: &'static str = "./leap-year";
function year_2000_is_leap_year (line 9) | fn year_2000_is_leap_year() {
function year_2019_is_not_leap_year (line 20) | fn year_2019_is_not_leap_year() {
function year_2020_is_leap_year (line 27) | fn year_2020_is_leap_year() {
function year_2100_is_not_leap_year (line 34) | fn year_2100_is_not_leap_year() {
FILE: ch07/ex07/examples/ch07_01_value_scope.rs
type Parent (line 4) | struct Parent(usize, Child, Child);
method drop (line 10) | fn drop(&mut self) {
type Child (line 16) | struct Child(usize);
method drop (line 20) | fn drop(&mut self) {
function main (line 25) | fn main() {
FILE: ch07/ex07/examples/ch07_02_move_semantics.rs
type Parent (line 2) | struct Parent(usize, Child, Child);
method drop (line 7) | fn drop(&mut self) {
type Child (line 13) | struct Child(usize);
method drop (line 16) | fn drop(&mut self) {
function main (line 21) | fn main() {
function move_semantics (line 28) | fn move_semantics() {
function f1 (line 40) | fn f1(p: &Parent) {
function f2 (line 45) | fn f2(p: &mut Parent) {
function borrow (line 49) | fn borrow() {
FILE: ch07/ex07/examples/ch07_03_nll.rs
function process_or_default (line 5) | fn process_or_default(key: char, map: &mut HashMap<char, String>) {
function main (line 18) | fn main() {
FILE: ch07/ex07/examples/ch07_04_static_lifetime.rs
function take_static (line 5) | fn take_static<T: 'static>(_x: T) { }
function main (line 7) | fn main() {
FILE: ch07/ex07/examples/ch07_05_rc.rs
type Child (line 2) | struct Child(usize);
method drop (line 5) | fn drop(&mut self) {
function main (line 12) | fn main() {
FILE: ch07/ex07/examples/ch07_06_simple_refcell.rs
type A (line 3) | struct A {
type B (line 10) | struct B {
function main (line 16) | fn main() {
FILE: ch07/ex07/examples/ch07_07_tls_refcell.rs
function main (line 14) | fn main() {
FILE: ch07/ex07/examples/ch07_08_arc_rwlock.rs
function main (line 6) | fn main() -> Result<(), Box<dyn Error>> {
FILE: ch07/ex07/examples/ch07_09_static_rwlock.rs
function main (line 17) | fn main() -> Result<(), Box<dyn Error>> {
FILE: ch07/ex07/examples/ch07_10_closure.rs
function apply_fn (line 3) | fn apply_fn<F>(f: &F, ch: char) where F: Fn(char) -> bool {
function apply_fn_mut (line 8) | fn apply_fn_mut<F>(f: &mut F, ch: char) where F: FnMut(char) -> bool {
function apply_fn_once (line 13) | fn apply_fn_once<F>(f: F, ch: char) where F: FnOnce(char) -> bool {
function main (line 17) | fn main() {
FILE: ch07/ex07/tests/integration_tests.rs
function run_ch07_01 (line 7) | fn run_ch07_01() {
function run_ch07_02 (line 33) | fn run_ch07_02() {
function run_ch07_03 (line 60) | fn run_ch07_03() {
function run_ch07_04 (line 68) | fn run_ch07_04() {
function run_ch07_05 (line 76) | fn run_ch07_05() {
function run_ch07_06 (line 95) | fn run_ch07_06() {
function run_ch07_07 (line 103) | fn run_ch07_07() {
function run_ch07_08 (line 111) | fn run_ch07_08() {
function run_ch07_09 (line 119) | fn run_ch07_09() {
function run_ch07_10 (line 127) | fn run_ch07_10() {
FILE: ch07/toy-vec/examples/toy_vec_01.rs
function main (line 3) | fn main() {
FILE: ch07/toy-vec/examples/toy_vec_02.rs
function main (line 3) | fn main() {
FILE: ch07/toy-vec/examples/toy_vec_03.rs
function main (line 3) | fn main() {
FILE: ch07/toy-vec/src/lib.rs
type ToyVec (line 3) | pub struct ToyVec<T> {
function new (line 12) | pub fn new() -> Self {
function with_capacity (line 17) | pub fn with_capacity(capacity: usize) -> Self {
function allocate_in_heap (line 25) | fn allocate_in_heap(size: usize) -> Box<[T]> {
function len (line 33) | pub fn len(&self) -> usize {
function capacity (line 38) | pub fn capacity(&self) -> usize {
function push (line 42) | pub fn push(&mut self, element: T) {
function grow (line 51) | fn grow(&mut self) {
function get (line 68) | pub fn get(&self, index: usize) -> Option<&T> {
function get_or (line 77) | pub fn get_or<'a>(&'a self, index: usize, default: &'a T) -> &'a T {
function pop (line 81) | pub fn pop(&mut self) -> Option<T> {
function iter (line 99) | pub fn iter<'vec>(&'vec self) -> Iter<'vec, T> {
function iter_mut (line 108) | pub fn iter_mut<'vec>(&'vec mut self) -> IterMut<'vec, T> {
function into_iter (line 117) | pub fn into_iter<'vec>(self) -> IntoIter<T> {
method default (line 128) | fn default() -> Self {
method clone (line 135) | fn clone(&self) -> Self {
method eq (line 146) | fn eq(&self, other: &Self) -> bool {
function fmt (line 153) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
type Item (line 160) | type Item = &'vec T;
type IntoIter (line 161) | type IntoIter = Iter<'vec, T>;
method into_iter (line 164) | fn into_iter(self) -> Self::IntoIter {
type Item (line 170) | type Item = &'vec mut T;
type IntoIter (line 171) | type IntoIter = IterMut<'vec, T>;
method into_iter (line 174) | fn into_iter(self) -> Self::IntoIter {
type Item (line 180) | type Item = T;
type IntoIter (line 181) | type IntoIter = IntoIter<T>;
method into_iter (line 184) | fn into_iter(self) -> Self::IntoIter {
type Iter (line 195) | pub struct Iter<'vec, T> {
type Item (line 204) | type Item = &'vec T;
method next (line 208) | fn next(&mut self) -> Option<Self::Item> {
method size_hint (line 218) | fn size_hint(&self) -> (usize, Option<usize>) {
type IterMut (line 227) | pub struct IterMut<'vec, T> {
type Item (line 234) | type Item = &'vec mut T;
method next (line 236) | fn next(&mut self) -> Option<Self::Item> {
method size_hint (line 252) | fn size_hint(&self) -> (usize, Option<usize>) {
type IntoIter (line 261) | pub struct IntoIter<T> {
type Item (line 268) | type Item = T;
method next (line 270) | fn next(&mut self) -> Option<Self::Item> {
method size_hint (line 281) | fn size_hint(&self) -> (usize, Option<usize>) {
function test_char_vec (line 292) | fn test_char_vec() {
function test_string_vec (line 325) | fn test_string_vec() {
function test_nested_vec (line 356) | fn test_nested_vec() {
function test_iter (line 402) | fn test_iter() {
function test_iter_mut (line 434) | fn test_iter_mut() {
function test_into_iter (line 465) | fn test_into_iter() {
FILE: ch07/toy-vec/tests/integration_tests.rs
function run_toy_vec_01 (line 7) | fn run_toy_vec_01() {
function run_toy_vec_02 (line 15) | fn run_toy_vec_02() {
function run_toy_vec_03 (line 23) | fn run_toy_vec_03() {
FILE: ch08/ex08/examples-java/Overload.java
class Overload (line 4) | public class Overload {
method main (line 5) | public static void main(String[] args) {
method call (line 12) | static String call(List<String> list) {
method call (line 17) | static String call(ArrayList<String> list) {
FILE: ch08/ex08/examples/ch08_01_trait_basics.rs
type CartesianCoord (line 3) | pub struct CartesianCoord {
type PolarCoord (line 18) | pub struct PolarCoord {
type Coordinates (line 24) | pub trait Coordinates {
method to_cartesian (line 26) | fn to_cartesian(self) -> CartesianCoord;
method from_cartesian (line 27) | fn from_cartesian(cart: CartesianCoord) -> Self;
method to_cartesian (line 32) | fn to_cartesian(self) -> CartesianCoord {
method from_cartesian (line 35) | fn from_cartesian(cart: CartesianCoord) -> Self {
method to_cartesian (line 42) | fn to_cartesian(self) -> CartesianCoord {
method from_cartesian (line 48) | fn from_cartesian(cart: CartesianCoord) -> Self {
method to_cartesian (line 58) | fn to_cartesian(self) -> CartesianCoord {
method from_cartesian (line 64) | fn from_cartesian(cart: CartesianCoord) -> Self {
function main (line 69) | fn main() {
FILE: ch08/ex08/examples/ch08_02_trait_basics.rs
type CartesianCoord (line 3) | pub struct CartesianCoord {
type PolarCoord (line 9) | pub struct PolarCoord {
type Coordinates (line 15) | pub trait Coordinates {
method to_cartesian (line 17) | fn to_cartesian(self) -> CartesianCoord;
method from_cartesian (line 18) | fn from_cartesian(cart: CartesianCoord) -> Self;
method to_cartesian (line 23) | fn to_cartesian(self) -> CartesianCoord {
method from_cartesian (line 26) | fn from_cartesian(cart: CartesianCoord) -> Self {
method to_cartesian (line 33) | fn to_cartesian(self) -> CartesianCoord {
method from_cartesian (line 39) | fn from_cartesian(cart: CartesianCoord) -> Self {
method to_cartesian (line 49) | fn to_cartesian(self) -> CartesianCoord {
method from_cartesian (line 55) | fn from_cartesian(cart: CartesianCoord) -> Self {
function print_point (line 73) | fn print_point(point: impl Coordinates) {
function as_cartesian (line 84) | fn as_cartesian(point: &(impl Coordinates + Clone)) -> CartesianCoord {
function double_point (line 90) | fn double_point<P: Coordinates>(point: P) -> P {
function make_point (line 99) | fn make_point<T>(x: T, y: T) -> CartesianCoord
type ConvertTo (line 108) | trait ConvertTo<Output> {
method convert (line 109) | fn convert(&self) -> Output;
function to (line 113) | fn to<T>(i: i32) -> T
function main (line 122) | fn main() {
FILE: ch08/ex08/examples/ch08_03_trait_basics.rs
type CartesianCoord (line 3) | pub struct CartesianCoord {
type PolarCoord (line 9) | pub struct PolarCoord {
type Coordinates (line 15) | pub trait Coordinates {
method to_cartesian (line 17) | fn to_cartesian(self) -> CartesianCoord;
method from_cartesian (line 18) | fn from_cartesian(cart: CartesianCoord) -> Self;
method to_cartesian (line 23) | fn to_cartesian(self) -> CartesianCoord {
method from_cartesian (line 26) | fn from_cartesian(cart: CartesianCoord) -> Self {
method to_cartesian (line 33) | fn to_cartesian(self) -> CartesianCoord {
method from_cartesian (line 39) | fn from_cartesian(cart: CartesianCoord) -> Self {
method to_cartesian (line 49) | fn to_cartesian(self) -> CartesianCoord {
method from_cartesian (line 55) | fn from_cartesian(cart: CartesianCoord) -> Self {
function print_point (line 60) | fn print_point(point: impl Coordinates) {
type Matrix (line 65) | struct Matrix([[f64; 2]; 2]);
type LinearTransform (line 67) | trait LinearTransform: Coordinates {
method transform (line 68) | fn transform(self, matrix: &Matrix) -> Self
method rotate (line 82) | fn rotate(self, theta: f64) -> Self
method rotate (line 97) | fn rotate(mut self, theta: f64) -> Self {
function main (line 103) | fn main() {
FILE: ch08/ex08/examples/ch08_04_trait_generics.rs
type Init (line 1) | trait Init<T> {
method init (line 2) | fn init(t: T) -> Self;
function init (line 7) | fn init(t: T) -> Self {
function main (line 12) | fn main() {
FILE: ch08/ex08/examples/ch08_05_trait_generics.rs
type As (line 1) | trait As<T> {
method cast (line 2) | fn cast(self) -> T;
function cast (line 7) | fn cast(self) -> u64 {
function cast (line 14) | fn cast(self) -> u32 {
function main (line 19) | fn main() {
FILE: ch08/ex08/examples/ch08_06_overload.rs
type Overload1 (line 27) | trait Overload1<T> {
method call (line 28) | fn call(&self, t: T) -> &'static str;
function call (line 32) | fn call(&self, _: i32) -> &'static str {
function call (line 38) | fn call(&self, _: char) -> &'static str {
function main (line 43) | fn main() {
FILE: ch08/ex08/examples/ch08_07_trait_object.rs
function main (line 3) | fn main() {
function stringify (line 22) | fn stringify(t: impl ToString) -> String {
FILE: ch08/ex08/examples/ch08_08_existential_impl_trait.rs
function to_n (line 2) | fn to_n(n: i32) -> impl Iterator {
function to_n_even (line 13) | fn to_n_even(n: i32) -> impl Iterator {
function one (line 19) | fn one() -> impl fmt::Display {
function main (line 32) | fn main() {
function gen_counter (line 38) | fn gen_counter(init: i32) -> impl FnMut() -> i32 {
FILE: ch08/ex08/examples/ch08_09_associated_const.rs
type CartesianCoord (line 3) | pub struct CartesianCoord {
type Dimension (line 9) | trait Dimension {
constant DIMENSION (line 10) | const DIMENSION: u32;
constant DIMENSION (line 24) | const DIMENSION: u32 = 2;
function main (line 13) | fn main() {
FILE: ch08/ex08/examples/ch08_10_associated_type.rs
type Server (line 3) | trait Server {
method handle (line 10) | fn handle(&self, req: Self::Request) -> Self::Response;
type Response (line 17) | type Response = String;
type Request (line 20) | type Request = String;
method handle (line 23) | fn handle(&self, req: Self::Request) -> Self::Response {
type EchoServer (line 13) | struct EchoServer;
function handle (line 38) | fn handle<S: Server<Request = String>>(server: S, req: &str) -> S::Respo...
function main (line 42) | fn main() {
type ToJson (line 48) | trait ToJson {}
type Server2 (line 50) | trait Server2 {
method handle (line 53) | fn handle(&self, req: Self::Request) -> ToJson;
type Foo (line 56) | trait Foo<T> {}
type Bar (line 57) | trait Bar {
type Baz (line 62) | struct Baz;
FILE: ch08/ex08/examples/ch08_11_sized.rs
function main (line 1) | fn main() {
FILE: ch08/ex08/examples/ch08_12_trait_techniques.rs
type Either (line 2) | enum Either<A, B> {
function fmt (line 14) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function main (line 22) | fn main() {
FILE: ch08/ex08/tests/integration_tests.rs
function run_ch08_01 (line 7) | fn run_ch08_01() {
function run_ch08_02 (line 24) | fn run_ch08_02() {
function run_ch08_03 (line 37) | fn run_ch08_03() {
function run_ch08_04 (line 45) | fn run_ch08_04() {
function run_ch08_05 (line 53) | fn run_ch08_05() {
function run_ch08_06 (line 61) | fn run_ch08_06() {
function run_ch08_07 (line 69) | fn run_ch08_07() {
function run_ch08_08 (line 77) | fn run_ch08_08() {
function run_ch08_09 (line 85) | fn run_ch08_09() {
function run_ch08_10 (line 93) | fn run_ch08_10() {
function run_ch08_11 (line 101) | fn run_ch08_11() {
function run_ch08_12 (line 114) | fn run_ch08_12() {
FILE: ch09/parser/src/main.rs
type Loc (line 7) | struct Loc(usize, usize);
method merge (line 11) | fn merge(&self, other: &Loc) -> Loc {
method fmt (line 564) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
type Annot (line 19) | struct Annot<T> {
function new (line 25) | fn new(value: T, loc: Loc) -> Self {
type TokenKind (line 31) | enum TokenKind {
method fmt (line 549) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
type Token (line 49) | type Token = Annot<TokenKind>;
method number (line 53) | fn number(n: u64, loc: Loc) -> Self {
method plus (line 56) | fn plus(loc: Loc) -> Self {
method minus (line 60) | fn minus(loc: Loc) -> Self {
method asterisk (line 64) | fn asterisk(loc: Loc) -> Self {
method slash (line 68) | fn slash(loc: Loc) -> Self {
method lparen (line 72) | fn lparen(loc: Loc) -> Self {
method rparen (line 76) | fn rparen(loc: Loc) -> Self {
type LexErrorKind (line 83) | enum LexErrorKind {
type LexError (line 88) | type LexError = Annot<LexErrorKind>;
method invalid_char (line 91) | fn invalid_char(c: char, loc: Loc) -> Self {
method eof (line 94) | fn eof(loc: Loc) -> Self {
method fmt (line 570) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function consume_byte (line 100) | fn consume_byte(input: &[u8], pos: usize, b: u8) -> Result<(u8, usize), ...
function recognize_many (line 117) | fn recognize_many(input: &[u8], mut pos: usize, mut f: impl FnMut(u8) ->...
function lex_number (line 124) | fn lex_number(input: &[u8], pos: usize) -> Result<(Token, usize), LexErr...
function lex_plus (line 138) | fn lex_plus(input: &[u8], start: usize) -> Result<(Token, usize), LexErr...
function lex_minus (line 148) | fn lex_minus(input: &[u8], start: usize) -> Result<(Token, usize), LexEr...
function lex_asterisk (line 151) | fn lex_asterisk(input: &[u8], start: usize) -> Result<(Token, usize), Le...
function lex_slash (line 154) | fn lex_slash(input: &[u8], start: usize) -> Result<(Token, usize), LexEr...
function lex_lparen (line 157) | fn lex_lparen(input: &[u8], start: usize) -> Result<(Token, usize), LexE...
function lex_rparen (line 160) | fn lex_rparen(input: &[u8], start: usize) -> Result<(Token, usize), LexE...
function skip_spaces (line 163) | fn skip_spaces(input: &[u8], pos: usize) -> Result<((), usize), LexError> {
function lex (line 169) | fn lex(input: &str) -> Result<Vec<Token>, LexError> {
function test_lexer (line 208) | fn test_lexer() {
type UniOpKind (line 226) | enum UniOpKind {
type UniOp (line 233) | type UniOp = Annot<UniOpKind>;
method plus (line 236) | fn plus(loc: Loc) -> Self {
method minus (line 240) | fn minus(loc: Loc) -> Self {
type BinOpKind (line 247) | enum BinOpKind {
type BinOp (line 258) | type BinOp = Annot<BinOpKind>;
method add (line 261) | fn add(loc: Loc) -> Self {
method sub (line 264) | fn sub(loc: Loc) -> Self {
method mult (line 267) | fn mult(loc: Loc) -> Self {
method div (line 270) | fn div(loc: Loc) -> Self {
type AstKind (line 277) | enum AstKind {
type Ast (line 286) | type Ast = Annot<AstKind>;
method num (line 290) | fn num(n: u64, loc: Loc) -> Self {
method uniop (line 295) | fn uniop(op: UniOp, e: Ast, loc: Loc) -> Self {
method binop (line 299) | fn binop(op: BinOp, l: Ast, r: Ast, loc: Loc) -> Self {
type ParseError (line 312) | enum ParseError {
method fmt (line 581) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
function parse_left_binop (line 329) | fn parse_left_binop<Tokens>(
function parse_atom (line 357) | fn parse_atom<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, Pars...
function parse_expr1 (line 384) | fn parse_expr1<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, Par...
function parse_expr2 (line 412) | fn parse_expr2<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, Par...
function parse_expr3 (line 436) | fn parse_expr3<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, Par...
function parse_expr (line 462) | fn parse_expr<Tokens>(tokens: &mut Peekable<Tokens>) -> Result<Ast, Pars...
function parse (line 470) | fn parse(tokens: Vec<Token>) -> Result<Ast, ParseError> {
function test_parser (line 482) | fn test_parser() {
type Err (line 520) | type Err = Error;
method from_str (line 521) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type Error (line 531) | enum Error {
method from (line 537) | fn from(e: LexError) -> Self {
method from (line 543) | fn from(e: ParseError) -> Self {
method fmt (line 603) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method show_diagnostic (line 632) | fn show_diagnostic(&self, input: &str) {
method source (line 613) | fn source(&self) -> Option<&(dyn StdError + 'static)> {
function print_annot (line 623) | fn print_annot(input: &str, loc: Loc) {
function show_trace (line 659) | fn show_trace<E: StdError>(e: E) {
type Interpreter (line 672) | struct Interpreter;
method new (line 682) | pub fn new() -> Self {
method eval (line 686) | pub fn eval(&mut self, expr: &Ast) -> Result<i64, InterpreterError> {
method eval_uniop (line 707) | fn eval_uniop(&mut self, op: &UniOp, n: i64) -> i64 {
method eval_binop (line 714) | fn eval_binop(&mut self, op: &BinOp, l: i64, r: i64) -> Result<i64, In...
type InterpreterErrorKind (line 675) | enum InterpreterErrorKind {
type InterpreterError (line 679) | type InterpreterError = Annot<InterpreterErrorKind>;
method fmt (line 732) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
method show_diagnostic (line 750) | fn show_diagnostic(&self, input: &str) {
method description (line 741) | fn description(&self) -> &str {
type RpnCompiler (line 759) | struct RpnCompiler;
method new (line 762) | pub fn new() -> Self {
method compile (line 766) | pub fn compile(&mut self, expr: &Ast) -> String {
method compile_inner (line 772) | pub fn compile_inner(&mut self, expr: &Ast, buf: &mut String) {
method compile_uniop (line 794) | fn compile_uniop(&mut self, op: &UniOp, buf: &mut String) {
method compile_binop (line 801) | fn compile_binop(&mut self, op: &BinOp, buf: &mut String) {
function prompt (line 815) | fn prompt(s: &str) -> io::Result<()> {
function main (line 823) | fn main() {
FILE: ch10/wordcount/src/lib.rs
type CountOption (line 10) | pub enum CountOption {
method default (line 21) | fn default() -> Self {
function count (line 51) | pub fn count(input: impl BufRead, option: CountOption) -> HashMap<String...
function word_count_works (line 80) | fn word_count_works() {
function word_count_works2 (line 92) | fn word_count_works2() {
function word_count_works3 (line 115) | fn word_count_works3() {
function word_count_do_not_contain_unknown_words (line 126) | fn word_count_do_not_contain_unknown_words() {
FILE: ch10/wordcount/src/main.rs
function main (line 8) | fn main() {
FILE: ch10/wordcount/tests/char.rs
function char_count_works (line 11) | fn char_count_works() {
function char_count_utf8 (line 27) | fn char_count_utf8() {
FILE: ch10/wordcount/tests/line.rs
function line_count_works (line 10) | fn line_count_works() {
function line_count_lfcr (line 29) | fn line_count_lfcr() {
FILE: ch11/log-collector/api/src/lib.rs
type Log (line 6) | pub struct Log {
type DateTimeRange (line 14) | pub struct DateTimeRange {
type Query (line 23) | pub type Query = DateTimeRange;
type Response (line 33) | pub struct Response(pub usize);
type Query (line 42) | pub type Query = DateTimeRange;
type Response (line 46) | pub struct Response(pub Vec<Log>);
type Request (line 55) | pub struct Request {
FILE: ch11/log-collector/cli/src/main.rs
type ApiClient (line 14) | struct ApiClient {
method post_logs (line 20) | fn post_logs(&self, req: &api::logs::post::Request) -> reqwest::Result...
method get_logs (line 28) | fn get_logs(&self) -> reqwest::Result<api::logs::get::Response> {
method get_csv (line 35) | fn get_csv<W: io::Write>(&self, w: &mut W) -> reqwest::Result<u64> {
function do_post_csv (line 43) | fn do_post_csv(api_client: &ApiClient) {
function do_get_json (line 57) | fn do_get_json(api_client: &ApiClient) {
function do_get_csv (line 63) | fn do_get_csv(api_client: &ApiClient) {
function main (line 69) | fn main() {
FILE: ch11/log-collector/server/migrations/00000000000000_diesel_initial_setup/up.sql
function diesel_set_updated_at (line 26) | CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
FILE: ch11/log-collector/server/migrations/2018-12-28-161332_create_logs/up.sql
type logs (line 2) | CREATE TABLE logs (
FILE: ch11/log-collector/server/src/db.rs
function insert_log (line 7) | pub fn insert_log(cn: &PgConnection, log: &NewLog) -> QueryResult<i64> {
function insert_logs (line 15) | pub fn insert_logs(cn: &PgConnection, logs: &[NewLog]) -> QueryResult<Ve...
function logs (line 23) | pub fn logs(
FILE: ch11/log-collector/server/src/handlers.rs
function load_file (line 13) | fn load_file(conn: &PgConnection, file: impl Read) -> Result<usize, Erro...
function handle_post_csv (line 41) | pub fn handle_post_csv(
function handle_post_logs (line 68) | pub fn handle_post_logs(
function handle_get_logs (line 89) | pub fn handle_get_logs(
function handle_get_csv (line 110) | pub fn handle_get_csv(
FILE: ch11/log-collector/server/src/main.rs
type Server (line 17) | pub struct Server {
method new (line 22) | pub fn new() -> Self {
function app (line 34) | pub fn app(server: Server) -> App<Server> {
function main (line 46) | fn main() {
FILE: ch11/log-collector/server/src/model.rs
type NewLog (line 6) | pub struct NewLog {
type Log (line 13) | pub struct Log {
FILE: ch11/start-aw/src/main.rs
type HelloPath (line 6) | struct HelloPath {
function hello (line 11) | fn hello(req: &HttpRequest) -> impl Responder {
function main (line 16) | fn main() {
FILE: ch11/static-files/src/main.rs
function main (line 4) | fn main() {
FILE: ch11/templates/src/main.rs
type AppState (line 5) | struct AppState {
type HelloPath (line 10) | struct HelloPath {
function hello_template (line 14) | fn hello_template(
function main (line 32) | fn main() {
FILE: ch12/c-api/main.c
type point (line 5) | struct point {
type point (line 13) | struct point
type point (line 13) | struct point
function main (line 16) | int
FILE: ch12/c-api/src/lib.rs
type point (line 6) | pub struct point {
function pow (line 11) | fn pow(x: c_int) -> c_int {
function dist (line 19) | pub extern "C" fn dist(p1: &point, p2: &point) -> c_double {
FILE: ch12/cffi-ownership/build.rs
function main (line 1) | fn main() {
FILE: ch12/cffi-ownership/c_src/ownership.c
function take_ownership (line 4) | void
FILE: ch12/cffi-ownership/src/main.rs
function make_memory (line 5) | fn make_memory() -> *mut c_int;
function main (line 8) | fn main() {
FILE: ch12/cffi_readline.rs
function readline (line 11) | fn readline(prompt: *const c_schar) -> *mut c_schar;
function main (line 14) | fn main() {
FILE: ch12/ffi-global/src/main.rs
function main (line 9) | fn main() {
FILE: ch12/onigmo-rs/onigmo-sys/build.rs
function main (line 4) | fn main() {
FILE: ch12/onigmo-rs/onigmo-sys/examples/simple.rs
function main (line 5) | fn main() {
FILE: ch12/onigmo-rs/onigmo/examples/simple.rs
function main (line 1) | fn main() {
FILE: ch12/onigmo-rs/onigmo/src/lib.rs
type Error (line 7) | pub struct Error(OnigPosition, Option<OnigErrorInfo>, String);
method new (line 13) | fn new(pos: OnigPosition, error_info: Option<OnigErrorInfo>) -> Self {
method fmt (line 29) | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
type Result (line 8) | type Result<T> = ::std::result::Result<T, Error>;
type Regex (line 40) | pub struct Regex(regex_t);
method new (line 42) | pub fn new(pattern: &str) -> Result<Self> {
method search (line 66) | pub fn search(&mut self, s: &str) -> Option<Region> {
method drop (line 97) | fn drop(&mut self) {
type Region (line 106) | pub struct Region(NonNull<OnigRegion>);
method new (line 109) | pub fn new() -> Option<Self> {
method as_ptr_mut (line 117) | fn as_ptr_mut(&mut self) -> *mut OnigRegion {
method as_ptr (line 121) | fn as_ptr(&self) -> *const OnigRegion {
method positions (line 152) | pub fn positions(&self) -> PositionIter {
method clone (line 127) | fn clone(&self) -> Self {
method drop (line 138) | fn drop(&mut self) {
type PositionIter (line 147) | pub struct PositionIter<'a>(&'a Region, Range<i32>);
type Item (line 163) | type Item = (usize, usize);
method next (line 164) | fn next(&mut self) -> Option<Self::Item> {
FILE: ch12/opaque/src/main.rs
type File (line 6) | enum File {}
function fopen (line 12) | fn fopen(fname: *const c_char, mode: *const c_char) -> *mut File;
function fgetc (line 15) | fn fgetc(stream: *mut File) -> c_int;
function fclose (line 18) | fn fclose(stream: *mut File) -> c_int;
function main (line 21) | fn main() {
FILE: ch12/ptr.rs
function main (line 1) | fn main() {
FILE: ch12/ptr_ownership.rs
function main (line 1) | fn main() {
FILE: ch12/repr-c/src/main.rs
type Timeval (line 14) | struct Timeval {
type Timezone (line 25) | struct Timezone {
function gettimeofday (line 33) | fn gettimeofday(tv: *mut Timeval, tz: *mut Timezone) -> c_int;
function main (line 36) | fn main() {
FILE: ch12/small_cffi.rs
function cos (line 5) | fn cos(x: c_double) -> c_double;
function main (line 8) | fn main() {
FILE: ch12/static-link/build.rs
function main (line 1) | fn main() {
FILE: ch12/static-link/c_src/fib.c
function fib (line 1) | unsigned long long
FILE: ch12/static-link/src/main.rs
function fib (line 6) | fn fib(n: c_int) -> c_ulonglong;
function main (line 9) | fn main() {
Condensed preview — 205 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (285K chars).
[
{
"path": ".circleci/config.yml",
"chars": 6819,
"preview": "version: 2\n\n.template:\n test: &cargo-test\n docker:\n - image: circleci/rust:1.32.0\n steps:\n - checkout\n "
},
{
"path": ".gitignore",
"chars": 140,
"preview": "# Visual Studio Codeの設定ファイルが置かれる\n.vscode/\n\n# rustfmtが作成するバックアップファイル\n*.rs.bk\n\n# Emacsが作成するバックアップファイル\n*~\n\n# macOSのFinderが作"
},
{
"path": "LICENSE",
"chars": 1518,
"preview": "BSD 3-Clause License\n\nCopyright (c) 2019, ghmagazine\nAll rights reserved.\n\nRedistribution and use in source and binary f"
},
{
"path": "README.md",
"chars": 2390,
"preview": "# 『実践Rust入門』のサンプルプログラム\n\n**実践Rust入門**</br>\n[言語仕様から開発手法まで]</br></br>\nκeen(著)、河野達也(著)、小松礼人(著)</br>\nB5判/576ページ/本体価格3,980円+税<"
},
{
"path": "RELEASES.md",
"chars": 551,
"preview": "# 1.0.0 (2019-04-24)\n\n* サンプルプログラムの初版を公開\n* サンプルプログラムの内容は以下を除いて書籍の**初版**に掲載されているものと同等\n * `ch12/onigmo-rs/onigmo-sys/build"
},
{
"path": "ch02/ex02/.gitignore",
"chars": 75,
"preview": "# Cargoが選択した依存ライブラリのバージョン郡\nCargo.lock\n\n# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch02/ex02/Cargo.toml",
"chars": 175,
"preview": "[package]\nname = \"ex02\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n\n["
},
{
"path": "ch02/ex02/examples/println.rs",
"chars": 433,
"preview": "fn main() {\n // 引数としてフォーマット文字列と1つの文字列を受け取る\n println!(\n \"Hello, {}!\",\n \"Takashi\",\n );\n // → 実行す"
},
{
"path": "ch02/ex02/tests/integration_tests.rs",
"chars": 441,
"preview": "use cli_test_dir::*;\n\n// バイナリを実行して入出力を確認するテスト\n// `cargo test` で実行できる\n\nconst CMD: &'static str = \"./examples/println\";\n\n#"
},
{
"path": "ch02/examples/README.md",
"chars": 269,
"preview": "# 可変個引数の例について\n\n**2019年4月22日**\n\n本書 初版 第2章 2-2-7項で`println!`マクロの可変個引数のコード例が紹介されていますが、ディレクトリ名とファイル名に誤りがありましたので、訂正いたします。\n\n- "
},
{
"path": "ch02/hello/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch02/hello/Cargo.toml",
"chars": 366,
"preview": "[package] # パッケージセクションの始まり\nname = \"hello\" # 名前\nversion = \"0.1.0\" # バージョン\nauthors = [\"Rust Bicyc"
},
{
"path": "ch02/hello/src/main.rs",
"chars": 116,
"preview": "// エントリポイントとなる関数\nfn main() {\n // println!はマクロ。stdout(標準出力)に\"Hello, world!\"と出力する\n println!(\"Hello, world!\");\n}\n"
},
{
"path": "ch02/hello/tests/integration_tests.rs",
"chars": 383,
"preview": "use cli_test_dir::*;\n\n// バイナリを実行して入出力を確認するテスト\n// `cargo test` で実行できる\n\n#[test]\nfn run_hello() {\n let testdir = TestDir"
},
{
"path": "ch02/rpn/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch02/rpn/Cargo.toml",
"chars": 174,
"preview": "[package]\nname = \"rpn\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n\n[d"
},
{
"path": "ch02/rpn/src/main.rs",
"chars": 1919,
"preview": "fn main() {\n // 変数expをRPN形式の文字列に束縛する\n // このRPNは数式 6.1 + 5.2 * 4.3 - 3.4 / 2.5 * 1.6 と等しい\n let exp = \"6.1 5.2 4."
},
{
"path": "ch02/rpn/tests/integration_tests.rs",
"chars": 407,
"preview": "use cli_test_dir::*;\n\n// バイナリを実行して入出力を確認するテスト\n// `cargo test` で実行できる\n\n#[test]\nfn run_rpn() {\n let testdir = TestDir::"
},
{
"path": "ch03/bitonic-sorter/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch03/bitonic-sorter/Cargo.toml",
"chars": 258,
"preview": "[package]\nname = \"bitonic-sorter\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition ="
},
{
"path": "ch03/bitonic-sorter/examples/benchmark.rs",
"chars": 2381,
"preview": "use num_cpus;\n\nuse bitonic_sorter::SortOrder;\n// 第3段階のsort関数をseq_sortという別名で使用する\nuse bitonic_sorter::third::sort as seq_s"
},
{
"path": "ch03/bitonic-sorter/py-src/.gitignore",
"chars": 6,
"preview": "*.pyc\n"
},
{
"path": "ch03/bitonic-sorter/py-src/bitonic_sorter.py",
"chars": 1513,
"preview": "# -*- coding:utf-8-unix -*-\nu\"Bitonic Merge Sortモジュール。\"\n\n# Pythonによるsort関数\ndef sort(x, up):\n u\"\"\"\n リストxの要素を、upで指定さ"
},
{
"path": "ch03/bitonic-sorter/src/first.rs",
"chars": 1662,
"preview": "// pubはこのsort関数が他のモジュールからアクセスできることを示す\n// 引数xの型 `&mut [u32]` について\n// &は値をポインタ経由で借用することを示す(借用については7章で説明)\n// mutは値が変更可能"
},
{
"path": "ch03/bitonic-sorter/src/fourth.rs",
"chars": 6098,
"preview": "use super::SortOrder;\nuse rayon;\nuse std::cmp::Ordering;\n\n// 並列に処理するかを決める、しきい値\nconst PARALLEL_THRESHOLD: usize = 4096;\n\n"
},
{
"path": "ch03/bitonic-sorter/src/lib.rs",
"chars": 249,
"preview": "pub mod utils;\n\n// 第1段階:初歩的な実装。u32型の値のソートのみに対応\npub mod first;\n\n// 第2段階:ジェネリクスでさまざまなデータ型に対応\npub mod second;\n\n// 第3段階:クロージ"
},
{
"path": "ch03/bitonic-sorter/src/second.rs",
"chars": 2445,
"preview": "use super::SortOrder;\n\n// 成功時はOK(())を、失敗時はErr(文字列)を返す\npub fn sort<T: Ord>(x: &mut [T], order: &SortOrder) -> Result<(), "
},
{
"path": "ch03/bitonic-sorter/src/third.rs",
"chars": 6426,
"preview": "use super::SortOrder;\nuse std::cmp::Ordering;\n\npub fn sort<T: Ord>(x: &mut [T], order: &SortOrder) -> Result<(), String>"
},
{
"path": "ch03/bitonic-sorter/src/utils.rs",
"chars": 987,
"preview": "use rand::{Rng, SeedableRng};\nuse rand::distributions::Standard;\nuse rand_pcg::Pcg64Mcg;\n\npub fn new_u32_vec(n: usize) -"
},
{
"path": "ch03/bitonic-sorter/tests/integration_tests.rs",
"chars": 754,
"preview": "use cli_test_dir::*;\n\n// バイナリを実行して入出力を確認するテスト\n// `cargo test` で実行できる\n\n#[test]\nfn run_benchmark() {\n let testdir = Tes"
},
{
"path": "ch04/ex04/.gitignore",
"chars": 75,
"preview": "# Cargoが選択した依存ライブラリのバージョン郡\nCargo.lock\n\n# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch04/ex04/Cargo.toml",
"chars": 187,
"preview": "[package]\nname = \"ex04\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n\n["
},
{
"path": "ch04/ex04/examples/ch04_01_unit.rs",
"chars": 314,
"preview": "// 戻り値の型を省略。コンパイラは戻り値がユニット型だと解釈する\nfn hello() {\n println!(\"Hello\");\n}\n\nfn main() {\n // 関数を呼び出し、(ないはずの)戻り値に変数retを束縛す"
},
{
"path": "ch04/ex04/examples/ch04_02_bool.rs",
"chars": 348,
"preview": "#[allow(unused_variables)]\nfn main() {\n let b1 = true;\n let b2 = !b1; // false、否定\n\n let n1 = 8;\n let n"
},
{
"path": "ch04/ex04/examples/ch04_03_integer.rs",
"chars": 507,
"preview": "#[allow(unused_variables)]\nfn main() {\n let n1 = 10_000; // i32型(整数リテラルのデフォルトの型)\n let n2 = 0u8; // u8型("
},
{
"path": "ch04/ex04/examples/ch04_04_overflowed1.rs",
"chars": 159,
"preview": "fn main() {\n let n1 = std::u8::MAX; // u8型の最大値は255u8\n let n2 = 1u8;\n // 答えは256だがu8型では表現できない(オーバーフロー)\n let n"
},
{
"path": "ch04/ex04/examples/ch04_05_overflowed2.rs",
"chars": 432,
"preview": "fn main() {\n let n1 = 200u8;\n let n2 = 3u8;\n\n // n1 × n2 = 600を計算する\n // std::u8::MAXは255なので桁あふれする\n\n // 検査"
},
{
"path": "ch04/ex04/examples/ch04_06_float.rs",
"chars": 186,
"preview": "#[allow(unused_variables)]\nfn main() {\n let f1 = 10.0; // f64型(小数リテラルのデフォルトの型)\n let f2 = -1_234.56f32; // "
},
{
"path": "ch04/ex04/examples/ch04_07_char.rs",
"chars": 780,
"preview": "#[allow(unused_variables)]\nfn main() {\n let c1 = 'A'; // char型\n let c2 = 'a';\n assert!(c1 < c2); "
},
{
"path": "ch04/ex04/examples/ch04_08_reference1.rs",
"chars": 571,
"preview": "// 関数f1は呼び出し元の値のコピーを引数nに束縛し、1に変更する\n#[allow(unused_assignments)]\nfn f1(mut n: u32) {\n n = 1;\n println!(\"f1: n"
},
{
"path": "ch04/ex04/examples/ch04_09_reference2.rs",
"chars": 332,
"preview": "fn main() {\n let c1 = 'A'; // char型\n let c1_ptr = &c1; // &char型。不変の参照(イミュータブルな参照)\n assert_eq!(*c1_"
},
{
"path": "ch04/ex04/examples/ch04_10_raw_pointer.rs",
"chars": 473,
"preview": "fn main() {\n let c1 = 'A'; // char型\n // `&`で参照を作り、型強制で生ポインタに変換する\n let c1_ptr: *const char = &"
},
{
"path": "ch04/ex04/examples/ch04_11_fn_pointer.rs",
"chars": 929,
"preview": "// この関数は引数を2倍した値を返す\nfn double(n: i32) -> i32 {\n n + n\n}\n\n// この関数は引数の絶対値を返す\nfn abs(n: i32) -> i32 {\n if n >= 0 { n "
},
{
"path": "ch04/ex04/examples/ch04_12_fn_pointer_vs_closure.rs",
"chars": 1677,
"preview": "fn main() {\n let x = 4; // 変数xを4に束縛する\n\n // クロージャを定義する。するとxがクロージャの環境に捕捉される(キャプチャされる)\n let adder = |n| n + x;\n "
},
{
"path": "ch04/ex04/examples/ch04_13_tuple.rs",
"chars": 1167,
"preview": "fn main() {\n let t1 = (88, true);\n\n // フィールド0の要素(左から数えて最初の要素)を取り出す\n assert_eq!(t1.0, 88);\n\n // フィールド1の要素(2番目"
},
{
"path": "ch04/ex04/examples/ch04_14_array.rs",
"chars": 1977,
"preview": "fn main() {\n #[allow(unused_variables)]\n let a1 = [false, true, false]; // [bool; 3]型\n let a2 = [0.0, -1.0,"
},
{
"path": "ch04/ex04/examples/ch04_15_slice1.rs",
"chars": 926,
"preview": "// この関数は&[char]型のスライスを引数に取り、その情報を表示する\nfn print_info(name: &str, sl: &[char]) {\n println!(\n \" {:9} - {}, {:?},"
},
{
"path": "ch04/ex04/examples/ch04_16_slice2.rs",
"chars": 2071,
"preview": "fn main() {\n let mut a1 = [5, 4, 3, 2]; // 配列。[i32; 4]型\n let s1 = &mut a1[1..3]; // 可変のスライス。&mut[i32]"
},
{
"path": "ch04/ex04/examples/ch04_17_str.rs",
"chars": 3505,
"preview": "fn main() {\n let s1 = \"abc1\"; // &'static str型\n let s2 = \"abc2\";\n assert!(s1 < s2);\n assert!(s1 != s2);\n\n "
},
{
"path": "ch04/ex04/tests/integration_tests.rs",
"chars": 5206,
"preview": "use cli_test_dir::*;\n\n// バイナリを実行して入出力を確認するテスト\n// `cargo test` で実行できる\n\n#[test]\nfn run_ch04_01() {\n let testdir = TestD"
},
{
"path": "ch05/ex05/.gitignore",
"chars": 75,
"preview": "# Cargoが選択した依存ライブラリのバージョン郡\nCargo.lock\n\n# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch05/ex05/Cargo.toml",
"chars": 187,
"preview": "[package]\nname = \"ex05\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n\n["
},
{
"path": "ch05/ex05/examples/ch05_01_box.rs",
"chars": 381,
"preview": "fn main() {\n let t1 = (5, \"birds\".to_string()); // (i32, String)型のタプル。スタックに置かれる\n let mut b1 = Box::new(t1); "
},
{
"path": "ch05/ex05/examples/ch05_02_vec.rs",
"chars": 1555,
"preview": "fn main() {\n #[allow(unused_variables)]\n let v1 = vec![false, true, false]; // Vec<bool>型\n let v2 = vec![0.0"
},
{
"path": "ch05/ex05/examples/ch05_03_boxed_slice.rs",
"chars": 559,
"preview": "fn main() {\n // 4要素のベクタVec<i32>を作り、要素を1つ足して5要素に拡張する\n let mut v1 = vec![0, 1, 2, 3];\n v1.push(4);\n println!(\""
},
{
"path": "ch05/ex05/examples/ch05_04_hash_map.rs",
"chars": 641,
"preview": "fn main() {\n use std::collections::HashMap;\n\n let mut m1 = HashMap::new(); // またはwith_capacity(要素数)\n\n //"
},
{
"path": "ch05/ex05/examples/ch05_05_string1.rs",
"chars": 705,
"preview": "fn main() {\n // strリテラルからStringを作る。どちらの方法でも結果は同じ\n let mut s1 = \"ラズベリー\".to_string();\n let mut s2 = String::from("
},
{
"path": "ch05/ex05/examples/ch05_06_string2.rs",
"chars": 1169,
"preview": "fn main() {\n let i = 42; // i32型\n assert_eq!(i.to_string(), \"42\");\n\n let f = 4.3 + 0.1; // f64型\n "
},
{
"path": "ch05/ex05/examples/ch05_07_string3.rs",
"chars": 397,
"preview": "// この関数は引数として&str型の名前を取り、&str型の\"Hello, 名前!\"を返す\n// fn f1(name: &str) -> &str {\n// let s = format!(\"Hello, {}!\", name)"
},
{
"path": "ch05/ex05/examples/ch05_08_string4.rs",
"chars": 609,
"preview": "fn main() {\n let utf16: Vec<u16> = vec![0x61, 0x62, 0x6f22, 0x5b57];\n\n // Vec<u16>の値をUTF-16と解釈しStringを作成する(UTF-8へ変"
},
{
"path": "ch05/ex05/examples/ch05_09_range.rs",
"chars": 819,
"preview": "fn main() {\n let a = ['a', 'b', 'c', 'd', 'e'];\n\n // 糖衣構文と実際の範囲の対応\n assert_eq!(a[ .. ], ['a', 'b', 'c', 'd', '"
},
{
"path": "ch05/ex05/examples/ch05_10_option.rs",
"chars": 1910,
"preview": "fn main() {\n let a1 = ['a', 'b', 'c', 'd'];\n assert_eq!(a1.get(0), Some(&'a')); // インデックス0は配列a1の範囲内なので`Some(&値)`が"
},
{
"path": "ch05/ex05/examples/ch05_11_result.rs",
"chars": 990,
"preview": "fn main() {\n // str::parse()は文字列を指定した型(ここではi32型)に変換する\n assert_eq!(\"10\".parse::<i32>(), Ok(10)); // 変換できたらOK(値)が"
},
{
"path": "ch05/ex05/examples/ch05_12_type_alias.rs",
"chars": 489,
"preview": "type UserName = String;\ntype Id = i64;\ntype Timestamp = i64;\ntype User = (Id, UserName, Timestamp);\n\nfn new_user(name: U"
},
{
"path": "ch05/ex05/examples/ch05_13_struct1.rs",
"chars": 1589,
"preview": "struct Polygon {\n vertexes: Vec<(i32, i32)>, // 頂点の座標\n #[allow(dead_code)]\n stroke_width: u8, // 輪郭の"
},
{
"path": "ch05/ex05/examples/ch05_14_struct2.rs",
"chars": 752,
"preview": "#[allow(dead_code)]\n// #[derive(Default)]\nstruct Polygon {\n vertexes: Vec<(i32, i32)>,\n stroke_width: u8,\n fill"
},
{
"path": "ch05/ex05/examples/ch05_15_struct3.rs",
"chars": 226,
"preview": "struct Triangle(Vertex, Vertex, Vertex);\nstruct Vertex(i32, i32);\n\nfn main() {\n let vx0 = Vertex(0, 0);\n let vx1 ="
},
{
"path": "ch05/ex05/examples/ch05_16_struct4.rs",
"chars": 519,
"preview": "struct UserName(String);\nstruct Id(u64);\nstruct Timestamp(u64);\ntype User = (Id, UserName, Timestamp);\n\n#[allow(dead_cod"
},
{
"path": "ch05/ex05/examples/ch05_17_struct5.rs",
"chars": 232,
"preview": "#[derive(Debug, PartialEq)]\nstruct UniqueValue;\n// 以下の形式も可能\n// struct UniqueValue {}\n// struct UniqueValue();\n\nfn main()"
},
{
"path": "ch05/ex05/examples/ch05_18_enum1.rs",
"chars": 716,
"preview": "// 平日を表すWeekday型を定義する\n// Debugトレイトを自動導出すると\"{:?}\"で表示できるようになる\n// PartialEqトレイトを自動導出すると==演算子が使えるようになる\n#[allow(dead_code)]\n#"
},
{
"path": "ch05/ex05/examples/ch05_19_enum2.rs",
"chars": 1076,
"preview": "type UserName = String;\n\n#[allow(dead_code)]\n#[derive(Debug)]\nenum Task {\n Open,\n AssignedTo(UserName),\n Workin"
},
{
"path": "ch05/ex05/examples/ch05_20_adv_types1.rs",
"chars": 769,
"preview": "#[allow(dead_code)]\n#[derive(Default)]\npub struct Polygon<T> {\n pub vertexes: Vec<T>,\n pub stroke_width: u8,\n p"
},
{
"path": "ch05/ex05/examples/ch05_21_adv_types2.rs",
"chars": 287,
"preview": "#[derive(Default)]\nstruct A {\n f0: u8,\n f1: u32,\n f2: u8,\n}\n\nfn main() {\n let a: A = Default::default();\n "
},
{
"path": "ch05/ex05/examples/ch05_22_type_cast1.rs",
"chars": 340,
"preview": "fn main() {\n let i1 = 42; // i32型\n #[allow(unused_variables)]\n let f1 = i1 as f64 / 2.5; // i32型からf64型へキャスト\n\n "
},
{
"path": "ch05/ex05/examples/ch05_23_type_cast2.rs",
"chars": 616,
"preview": "fn main() {\n let t1 = ('a', 42);\n // let t2 = t1 as (u32, u8);\n // → error[E0605]: non-primitive cast: `(char, "
},
{
"path": "ch05/ex05/examples/ch05_24_transmute.rs",
"chars": 616,
"preview": "fn main() {\n let p1 = Box::new(10); // Box<i32>型\n\n // boxポインタを生ポインタ*mut i32型に変換したいが型キャストできない\n // let p2 = p1 a"
},
{
"path": "ch05/ex05/examples/ch05_25_type_coercion1.rs",
"chars": 1435,
"preview": "fn main() {\n intro();\n coercion_sites();\n transitivity();\n}\n\nfn intro() {\n // 整数リテラル3、4、5は通常はi32型と解釈されるが、\n "
},
{
"path": "ch05/ex05/examples/ch05_26_type_coercion2.rs",
"chars": 375,
"preview": "fn f1(n: &mut usize, str: &str, slice: &[i32]) {\n *n = str.len() + slice.len()\n}\n\nfn main() {\n let mut b1 = Box::n"
},
{
"path": "ch05/ex05/examples/ch05_27_type_coercion3.rs",
"chars": 269,
"preview": "fn f1(slice: &[usize]) -> usize {\n slice.len()\n}\n\nfn f2(slice: &mut [usize]) {\n // ポインタの弱体化により&mut [usize]型から&[usi"
},
{
"path": "ch05/ex05/examples/ch05_28_type_coercion4.rs",
"chars": 490,
"preview": "fn f1(p: &[i32]) -> i32 { p[0] }\nfn f2(p: Box<[i32]>) -> i32 { p[0] }\n\nfn main() {\n let a1 = [1, 2, 3, 4];\n as"
},
{
"path": "ch05/ex05/examples/ch05_29_type_coercion5.rs",
"chars": 124,
"preview": "fn main() {\n let v1: Vec<u8> = vec![3, 4, 5];\n assert_eq!(v1.first(), Some(&3u8)); // first()メソッドのレシーバはv1が束縛されたベク"
},
{
"path": "ch05/ex05/tests/integration_tests.rs",
"chars": 7780,
"preview": "use cli_test_dir::*;\n\n// バイナリを実行して入出力を確認するテスト\n// `cargo test` で実行できる\n\n#[test]\nfn run_ch05_01() {\n let testdir = TestD"
},
{
"path": "ch06/leap-year/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch06/leap-year/Cargo.toml",
"chars": 180,
"preview": "[package]\nname = \"leap-year\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"201"
},
{
"path": "ch06/leap-year/src/main.rs",
"chars": 687,
"preview": "// 第6章 基本構文\n// 入力された年がうるう年かどうかを判断するプログラム\n\n// `std::io` 名前空間を `io` としてインポート\nuse std::io;\n// `std::io::Write` トレイトを使う\nuse "
},
{
"path": "ch06/leap-year/tests/integration_tests.rs",
"chars": 1186,
"preview": "use cli_test_dir::*;\n\n// バイナリ(leap-year)を実行して入出力を確認するテスト\n// `cargo test` で実行できる\n\nconst CMD: &'static str = \"./leap-year\""
},
{
"path": "ch07/ex07/.gitignore",
"chars": 75,
"preview": "# Cargoが選択した依存ライブラリのバージョン郡\nCargo.lock\n\n# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch07/ex07/Cargo.toml",
"chars": 197,
"preview": "[package]\nname = \"ex07\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n\n["
},
{
"path": "ch07/ex07/examples/ch07_01_value_scope.rs",
"chars": 832,
"preview": "// 構造体を定義する\n// println!の\"{:?}\"で表示できるようにDebugトレイトを自動導出しておく\n#[derive(Debug)]\nstruct Parent(usize, Child, Child); // Parent"
},
{
"path": "ch07/ex07/examples/ch07_02_move_semantics.rs",
"chars": 1180,
"preview": "#[derive(Debug)]\nstruct Parent(usize, Child, Child);\n\nuse std::ops::Drop;\n\nimpl Drop for Parent {\n fn drop(&mut self)"
},
{
"path": "ch07/ex07/examples/ch07_03_nll.rs",
"chars": 637,
"preview": "use std::collections::HashMap;\n\n// この関数はHashMapにキーに対応する値がある場合はそれを変更し\n// ない場合はデフォルト値を挿入する\nfn process_or_default(key: char"
},
{
"path": "ch07/ex07/examples/ch07_04_static_lifetime.rs",
"chars": 721,
"preview": "#[allow(dead_code)]\nstatic I0: i32 = 42; // static変数。'staticスコープを持つ\n\n// この関数は'staticライフタイムを持つ任意の型を引数にとる\nfn tak"
},
{
"path": "ch07/ex07/examples/ch07_05_rc.rs",
"chars": 1465,
"preview": "#[derive(Debug)]\nstruct Child(usize);\n\nimpl Drop for Child {\n fn drop(&mut self) {\n println!(\"Dropping {:?}\", "
},
{
"path": "ch07/ex07/examples/ch07_06_simple_refcell.rs",
"chars": 1169,
"preview": "use std::cell::RefCell;\n\nstruct A {\n #[allow(dead_code)]\n c: char,\n #[allow(dead_code)]\n s: String,\n}\n\nstruc"
},
{
"path": "ch07/ex07/examples/ch07_07_tls_refcell.rs",
"chars": 1017,
"preview": "use std::cell::RefCell;\nuse std::collections::HashSet;\n\nthread_local!(\n // TLSに変数RABBITSを作成する。thread_localマクロはmutキーワー"
},
{
"path": "ch07/ex07/examples/ch07_08_arc_rwlock.rs",
"chars": 1169,
"preview": "use std::collections::HashSet;\nuse std::error::Error;\nuse std::sync::{Arc, RwLock};\n\n// ?演算子を使うためmain関数からResult型を返すようにする"
},
{
"path": "ch07/ex07/examples/ch07_09_static_rwlock.rs",
"chars": 1136,
"preview": "use lazy_static::lazy_static;\nuse std::collections::HashSet;\nuse std::error::Error;\nuse std::sync::RwLock;\n\nlazy_static!"
},
{
"path": "ch07/ex07/examples/ch07_10_closure.rs",
"chars": 2692,
"preview": "// この関数はクロージャFとcharを引数にとる\n// FはFnを実装し、引数にcharをとり、boolを返す\nfn apply_fn<F>(f: &F, ch: char) where F: Fn(char) -> bool {\n "
},
{
"path": "ch07/ex07/tests/integration_tests.rs",
"chars": 3825,
"preview": "use cli_test_dir::*;\n\n// examplesディレクトリ配下にあるバイナリを実行して入出力を確認するテスト\n// `cargo test` で実行できる\n\n#[test]\nfn run_ch07_01() {\n "
},
{
"path": "ch07/toy-vec/.gitignore",
"chars": 75,
"preview": "# Cargoが選択した依存ライブラリのバージョン郡\nCargo.lock\n\n# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch07/toy-vec/Cargo.toml",
"chars": 178,
"preview": "[package]\nname = \"toy-vec\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\""
},
{
"path": "ch07/toy-vec/examples/toy_vec_01.rs",
"chars": 237,
"preview": "use toy_vec::ToyVec;\n\nfn main() {\n let mut v = ToyVec::new();\n v.push(\"Java Finch\".to_string()); // 桜文鳥\n v.pus"
},
{
"path": "ch07/toy-vec/examples/toy_vec_02.rs",
"chars": 409,
"preview": "use toy_vec::ToyVec;\n\nfn main() {\n let _e: Option<&String>;\n {\n let mut v = ToyVec::new();\n v.push(\""
},
{
"path": "ch07/toy-vec/examples/toy_vec_03.rs",
"chars": 539,
"preview": "use toy_vec::ToyVec;\n\nfn main() {\n let mut v = ToyVec::new();\n v.push(\"Java Finch\".to_string()); // 桜文鳥\n v."
},
{
"path": "ch07/toy-vec/src/lib.rs",
"chars": 12747,
"preview": "use std::fmt;\n\npub struct ToyVec<T> {\n elements: Box<[T]>, // T型の要素を格納する領域。各要素はヒープ領域に置かれる\n len: usize, /"
},
{
"path": "ch07/toy-vec/tests/integration_tests.rs",
"chars": 831,
"preview": "use cli_test_dir::*;\n\n// バイナリを実行して入出力を確認するテスト\n// `cargo test` で実行できる\n\n#[test]\nfn run_toy_vec_01() {\n let testdir = Te"
},
{
"path": "ch08/ex08/.gitignore",
"chars": 105,
"preview": "# Cargoが選択した依存ライブラリのバージョン郡\nCargo.lock\n\n# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n\n# Javaのコンパイラが生成する成果物\n*.class\n"
},
{
"path": "ch08/ex08/Cargo.toml",
"chars": 175,
"preview": "[package]\nname = \"ex08\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n\n["
},
{
"path": "ch08/ex08/examples/ch08_01_trait_basics.rs",
"chars": 1678,
"preview": "// デカルト座標\n#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]\npub struct CartesianCoord {\n pub x: f64,\n "
},
{
"path": "ch08/ex08/examples/ch08_02_trait_basics.rs",
"chars": 2942,
"preview": "// デカルト座標\n#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]\npub struct CartesianCoord {\n pub x: f64,\n "
},
{
"path": "ch08/ex08/examples/ch08_03_trait_basics.rs",
"chars": 2352,
"preview": "// デカルト座標\n#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]\npub struct CartesianCoord {\n pub x: f64,\n "
},
{
"path": "ch08/ex08/examples/ch08_04_trait_generics.rs",
"chars": 402,
"preview": "trait Init<T> {\n fn init(t: T) -> Self;\n}\n\nimpl<T> Init<T> for Box<T> {\n // 内部では`T`でパラメータの型を参照する\n fn init(t: T)"
},
{
"path": "ch08/ex08/examples/ch08_05_trait_generics.rs",
"chars": 519,
"preview": "trait As<T> {\n fn cast(self) -> T;\n}\n\n// 実装をジェネリックにせずに個別の型に対して実装する\nimpl As<u64> for u8 {\n fn cast(self) -> u64 {"
},
{
"path": "ch08/ex08/examples/ch08_06_overload.rs",
"chars": 869,
"preview": "// trait Overload {\n// fn call(&self) -> &'static str;\n// }\n\n// impl Overload for i32 {\n// fn call(&self) -> &'s"
},
{
"path": "ch08/ex08/examples/ch08_07_trait_object.rs",
"chars": 627,
"preview": "use std::fmt::Display;\n\nfn main() {\n let mut v: Vec<&dyn Display> = vec![];\n v.push(&true);\n v.push(&1i32);\n}\n\n"
},
{
"path": "ch08/ex08/examples/ch08_08_existential_impl_trait.rs",
"chars": 737,
"preview": "#[allow(dead_code)]\nfn to_n(n: i32) -> impl Iterator {\n 0..n\n}\n\n// use std::ops::Range;\n// #[allow(dead_code)]\n// fn "
},
{
"path": "ch08/ex08/examples/ch08_09_associated_const.rs",
"chars": 479,
"preview": "// デカルト座標\n#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]\npub struct CartesianCoord {\n pub x: f64,\n "
},
{
"path": "ch08/ex08/examples/ch08_10_associated_type.rs",
"chars": 1587,
"preview": "use std::str::FromStr;\n\ntrait Server {\n // `type 型名`で関連型を宣言できる\n type Response;\n // あるいは`type 型名: トレイト境界` で境界を設定"
},
{
"path": "ch08/ex08/examples/ch08_11_sized.rs",
"chars": 137,
"preview": "fn main() {\n use std::mem::size_of;\n\n println!(\"{}\", size_of::<&i32>()); // -> 8\n println!(\"{}\", size_of::<&str"
},
{
"path": "ch08/ex08/examples/ch08_12_trait_techniques.rs",
"chars": 597,
"preview": "#[derive(Debug)]\nenum Either<A, B> {\n A(A),\n B(B),\n}\n\nuse std::fmt;\n\nimpl<A, B> fmt::Display for Either<A, B>\nwher"
},
{
"path": "ch08/ex08/examples-java/Overload.java",
"chars": 468,
"preview": "import java.util.List;\nimport java.util.ArrayList;\n\npublic class Overload {\n public static void main(String[] args) {"
},
{
"path": "ch08/ex08/tests/integration_tests.rs",
"chars": 3513,
"preview": "use cli_test_dir::{ExpectStatus, OutputExt, TestDir};\n\n// examplesディレクトリ配下にあるバイナリを実行して入出力を確認するテスト\n// `cargo test` で実行できる"
},
{
"path": "ch09/parser/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch09/parser/Cargo.toml",
"chars": 136,
"preview": "[package]\nname = \"parser\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n"
},
{
"path": "ch09/parser/src/main.rs",
"chars": 23089,
"preview": "use std::error::Error as StdError;\nuse std::fmt;\nuse std::str::FromStr;\n\n/// 位置情報。.0から.1までの区間を表す。\n#[derive(Debug, Clone,"
},
{
"path": "ch10/wordcount/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch10/wordcount/Cargo.toml",
"chars": 574,
"preview": "[package]\nname = \"bicycle-book-wordcount\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\ne"
},
{
"path": "ch10/wordcount/README.md",
"chars": 148,
"preview": "`bicycle_book_wordcount` はシンプルな文字、単語、行の出現頻度の計数機能を提供します。\nCLIからは単語数の出現頻度が使えます。\n\n```console\n$ cargo run text.txt\n{\"bb\": 1, "
},
{
"path": "ch10/wordcount/src/lib.rs",
"chars": 3439,
"preview": "//! `bicycle_book_wordcount` はシンプルな文字、単語、行の出現頻度の計数機能を提供します。\n//! 詳しくは[`count`](fn.count.html)関数のドキュメントを見て下さい。\n\nuse regex:"
},
{
"path": "ch10/wordcount/src/main.rs",
"chars": 489,
"preview": "use std::env;\nuse std::fs::File;\nuse std::io::BufReader;\n\n// libクレートに分離したものを使う\nuse bicycle_book_wordcount::count;\n\nfn ma"
},
{
"path": "ch10/wordcount/tests/char.rs",
"chars": 730,
"preview": "// アイテムのインポートも、もちろん必要\nuse std::io::Cursor;\nuse bicycle_book_wordcount::{count, CountOption};\n\n#[macro_use]\nmod utils;\n\n/"
},
{
"path": "ch10/wordcount/tests/line.rs",
"chars": 647,
"preview": "use std::io::Cursor;\nuse bicycle_book_wordcount::{count, CountOption};\n\n#[macro_use]\nmod utils;\n\n// 以下にテストを書く\n\n#[test]\nf"
},
{
"path": "ch10/wordcount/tests/utils/mod.rs",
"chars": 137,
"preview": "macro_rules! assert_map {\n ($expr: expr, {$($key: expr => $value:expr),*}) => {\n $(assert_eq!($expr[$key], $va"
},
{
"path": "ch10/wordcount/text.txt",
"chars": 13,
"preview": "aa bb cc aa\n\n"
},
{
"path": "ch11/log-collector/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch11/log-collector/Cargo.toml",
"chars": 47,
"preview": "[workspace]\nmembers = [\"server\", \"api\", \"cli\"]\n"
},
{
"path": "ch11/log-collector/api/Cargo.toml",
"chars": 235,
"preview": "[package]\nname = \"api\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n\n[d"
},
{
"path": "ch11/log-collector/api/src/lib.rs",
"chars": 1719,
"preview": "use chrono::{DateTime, Utc};\nuse serde_derive::*;\n// JSONの {\"user_agent\": \"xxx\", \"response_time\": 0, \"timestamp\": \"yyyy-"
},
{
"path": "ch11/log-collector/cli/Cargo.toml",
"chars": 284,
"preview": "[package]\nname = \"cli\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n\n[d"
},
{
"path": "ch11/log-collector/cli/src/main.rs",
"chars": 3628,
"preview": "use clap::{App, AppSettings, Arg, SubCommand};\nuse clap::{_clap_count_exprs, arg_enum};\nuse reqwest::Client;\nuse std::io"
},
{
"path": "ch11/log-collector/docker-compose.yml",
"chars": 385,
"preview": "# いろいろ書かれていますが、ローカルホストの5432番ポートにユーザ名postgres、パスワードpasswordのデータベースサーバを立てる設定です\npostgres-data:\n image: busybox\n volumes:\n"
},
{
"path": "ch11/log-collector/server/Cargo.toml",
"chars": 414,
"preview": "[package]\nname = \"server\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n"
},
{
"path": "ch11/log-collector/server/diesel.toml",
"chars": 136,
"preview": "# For documentation on how to configure this file,\n# see diesel.rs/guides/configuring-diesel-cli\n\n[print_schema]\nfile = "
},
{
"path": "ch11/log-collector/server/migrations/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "ch11/log-collector/server/migrations/00000000000000_diesel_initial_setup/down.sql",
"chars": 328,
"preview": "-- This file was automatically created by Diesel to setup helper functions\n-- and other internal bookkeeping. This file "
},
{
"path": "ch11/log-collector/server/migrations/00000000000000_diesel_initial_setup/up.sql",
"chars": 1145,
"preview": "-- This file was automatically created by Diesel to setup helper functions\n-- and other internal bookkeeping. This file "
},
{
"path": "ch11/log-collector/server/migrations/2018-12-28-161332_create_logs/down.sql",
"chars": 73,
"preview": "-- This file should undo anything in `up.sql`\nDROP TABLE IF EXISTS logs;\n"
},
{
"path": "ch11/log-collector/server/migrations/2018-12-28-161332_create_logs/up.sql",
"chars": 209,
"preview": "-- Your SQL goes here\nCREATE TABLE logs (\n id BIGSERIAL NOT NULL,\n user_agent VARCHAR NOT NULL,\n response_time INT NO"
},
{
"path": "ch11/log-collector/server/src/db.rs",
"chars": 1060,
"preview": "use crate::model::*;\nuse chrono::{DateTime, Utc};\nuse diesel::insert_into;\nuse diesel::prelude::*;\nuse diesel::result::Q"
},
{
"path": "ch11/log-collector/server/src/handlers.rs",
"chars": 3876,
"preview": "use crate::db;\nuse crate::Server;\nuse actix_web::{FutureResponse, HttpResponse, State};\nuse actix_web::{Json, Query};\nus"
},
{
"path": "ch11/log-collector/server/src/main.rs",
"chars": 1330,
"preview": "#[macro_use]\nextern crate diesel;\nuse actix_web::http::Method;\nuse actix_web::App;\nuse diesel::pg::PgConnection;\nuse die"
},
{
"path": "ch11/log-collector/server/src/model.rs",
"chars": 425,
"preview": "use crate::schema::*;\nuse chrono::NaiveDateTime;\n\n#[derive(Debug, Clone, Eq, PartialEq, Hash, Insertable)]\n#[table_name "
},
{
"path": "ch11/log-collector/server/src/schema.rs",
"chars": 147,
"preview": "table! {\n logs (id) {\n id -> Int8,\n user_agent -> Varchar,\n response_time -> Int4,\n times"
},
{
"path": "ch11/log-collector/test.csv",
"chars": 77,
"preview": "user_agent,response_time,timestamp\nhogehoge,10,\"2017-08-26T13:13:29.931320Z\"\n"
},
{
"path": "ch11/start-aw/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch11/start-aw/Cargo.toml",
"chars": 186,
"preview": "[package]\nname = \"start-aw\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018"
},
{
"path": "ch11/start-aw/src/main.rs",
"chars": 548,
"preview": "use actix_web::{server, App, HttpRequest, Responder};\n\nuse serde_derive::*;\n\n#[derive(Deserialize)]\nstruct HelloPath {\n "
},
{
"path": "ch11/start-aw/static/test.txt",
"chars": 9,
"preview": "testtest\n"
},
{
"path": "ch11/static-files/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch11/static-files/Cargo.toml",
"chars": 163,
"preview": "[package]\nname = \"static-files\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \""
},
{
"path": "ch11/static-files/src/main.rs",
"chars": 327,
"preview": "// fsをインポート\nuse actix_web::{fs, server, App};\n\nfn main() {\n server::new(|| {\n App::new().handler(\n "
},
{
"path": "ch11/templates/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch11/templates/Cargo.toml",
"chars": 208,
"preview": "[package]\nname = \"templates\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"201"
},
{
"path": "ch11/templates/src/main.rs",
"chars": 1232,
"preview": "use actix_web::{error, http, server, App, HttpResponse, Path, State};\nuse serde_derive::*;\nuse tera::{compile_templates,"
},
{
"path": "ch11/templates/templates/index.html.tera",
"chars": 160,
"preview": "<!doctype html>\n<html lang=\"ja\">\n <head>\n <meta charset=\"UTF-8\"/>\n <title>Document</title>\n </head>\n"
},
{
"path": "ch12/c-api/.gitignore",
"chars": 60,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n\n# Cコンパイラが生成する成果物\na.out\n"
},
{
"path": "ch12/c-api/Cargo.toml",
"chars": 166,
"preview": "[package]\nname = \"c-api\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n\n"
},
{
"path": "ch12/c-api/main.c",
"chars": 320,
"preview": "#include <stdio.h>\n#include <stdint.h>\n\n// Rustと同じ定義を書く\nstruct point {\n int x;\n int y;\n};\n\n// Rustの関数のプロトタイプ宣言\n// 上の`s"
},
{
"path": "ch12/c-api/src/lib.rs",
"chars": 462,
"preview": "use std::os::raw::{c_double, c_int};\n\n// Cのデータの扱いで説明したときと同様 `#[repr(C)]` で互換性を取れる\n/// 2次元平面上の点を表す型\n#[repr(C)]\npub struct"
},
{
"path": "ch12/cffi-ownership/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch12/cffi-ownership/Cargo.toml",
"chars": 192,
"preview": "[package]\nname = \"cffi-ownership\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition ="
},
{
"path": "ch12/cffi-ownership/build.rs",
"chars": 102,
"preview": "fn main() {\n cc::Build::new()\n .file(\"c_src/ownership.c\")\n .compile(\"ownership\");\n}\n\n"
},
{
"path": "ch12/cffi-ownership/c_src/ownership.c",
"chars": 274,
"preview": "#include <stdlib.h>\n#include <stdio.h>\n\nvoid\ntake_ownership(int *i, void(*dtor)(int *))\n{\n printf(\"got %d\\n\", *i);\n //"
},
{
"path": "ch12/cffi-ownership/src/main.rs",
"chars": 289,
"preview": "use std::os::raw::c_int;\n\n#[link(name = \"ownership\", kind = \"static\")]\nextern \"C\" {\n fn make_memory() -> *mut c_int;\n"
},
{
"path": "ch12/cffi_readline.rs",
"chars": 862,
"preview": "// `String`のCのNULL終端文字列に対応する型。\nuse std::ffi::CString;\n// `str`のCのnull終端文字列に対応する型。\nuse std::ffi::CStr;\nuse std::os::raw::"
},
{
"path": "ch12/ffi-global/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch12/ffi-global/Cargo.toml",
"chars": 140,
"preview": "[package]\nname = \"ffi-global\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"20"
},
{
"path": "ch12/ffi-global/src/main.rs",
"chars": 307,
"preview": "use std::os::raw::c_int;\n\n#[link(name = \"readline\")]\nextern \"C\" {\n // rustの`static`と同じく`static 名前: 型;`で宣言します。\n sta"
},
{
"path": "ch12/onigmo-rs/.gitignore",
"chars": 75,
"preview": "# Cargoが選択した依存ライブラリのバージョン郡\nCargo.lock\n\n# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch12/onigmo-rs/Cargo.toml",
"chars": 47,
"preview": "[workspace]\nmembers = [\"onigmo-sys\", \"onigmo\"]\n"
},
{
"path": "ch12/onigmo-rs/onigmo/Cargo.toml",
"chars": 186,
"preview": "[package]\nname = \"onigmo\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n"
},
{
"path": "ch12/onigmo-rs/onigmo/examples/simple.rs",
"chars": 436,
"preview": "fn main() {\n let mut reg = {\n let s = \"a(.*)b|[e-f]+\".to_string();\n onigmo::Regex::new(&s).unwrap()\n "
},
{
"path": "ch12/onigmo-rs/onigmo/src/lib.rs",
"chars": 5012,
"preview": "use onigmo_sys::*;\nuse std::error;\nuse std::fmt;\n\n// 本来は`OnigPosition`のままでなくenumを定義して変換した方がいいが長くなるのでここでは省略。\n#[derive(Deb"
},
{
"path": "ch12/onigmo-rs/onigmo-sys/Cargo.toml",
"chars": 180,
"preview": "[package]\nname = \"onigmo-sys\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"20"
},
{
"path": "ch12/onigmo-rs/onigmo-sys/build.rs",
"chars": 787,
"preview": "use std::env;\nuse std::path::PathBuf;\n\nfn main() {\n // onigmoの共有ライブラリを使うことをcargoがrustcに伝えるように伝える\n println!(\"cargo:"
},
{
"path": "ch12/onigmo-rs/onigmo-sys/examples/simple.rs",
"chars": 2916,
"preview": "use onigmo_sys::*;\nuse std::mem;\nuse std::str::from_utf8_unchecked;\n\nfn main() {\n unsafe {\n // 正規表現のパターン文字列\n "
},
{
"path": "ch12/onigmo-rs/onigmo-sys/src/lib.rs",
"chars": 171,
"preview": "// Cの名前のまま生成されてしまうので警告を切る\n#![allow(non_upper_case_globals)]\n#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n\ni"
},
{
"path": "ch12/onigmo-rs/onigmo-sys/wrapper.h",
"chars": 20,
"preview": "#include <onigmo.h>\n"
},
{
"path": "ch12/opaque/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch12/opaque/Cargo.toml",
"chars": 136,
"preview": "[package]\nname = \"opaque\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n"
},
{
"path": "ch12/opaque/src/main.rs",
"chars": 1291,
"preview": "use std::os::raw::{c_char, c_int};\n\n// オペーク型を表わす型を導入する。\n// バリアントのない列挙型は値が作れないのでユーザが勝手にインスタンスを作ることはできない。\n// この列挙型へのポインタでオ"
},
{
"path": "ch12/ptr.rs",
"chars": 675,
"preview": "fn main() {\n let x = 1;\n // 参照からconstなポインタが作れる\n let xptr: *const i32 = &x;\n // 逆の変換はできない\n // let xref: &i"
},
{
"path": "ch12/ptr_ownership.rs",
"chars": 432,
"preview": "fn main() {\n let boxed = Box::new(true);\n // ここでboxedの所有権はムーブしてしまう\n let ptr: *mut bool = Box::into_raw(boxed);\n"
},
{
"path": "ch12/repr-c/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch12/repr-c/Cargo.toml",
"chars": 151,
"preview": "[package]\nname = \"repr-c\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2018\"\n"
},
{
"path": "ch12/repr-c/src/main.rs",
"chars": 1253,
"preview": "use libc::{suseconds_t, time_t};\nuse std::mem;\nuse std::os::raw::c_int;\nuse std::ptr;\n\n// #[repr(C)]をつけることでCと相互運用できる型になる"
},
{
"path": "ch12/small_cffi.rs",
"chars": 256,
"preview": "use std::os::raw::c_double;\n\n// インポートするCの関数群は`extern \"C\" { .. }`で囲む\nextern \"C\" {\n fn cos(x: c_double) -> c_double;\n}\n"
},
{
"path": "ch12/static-link/.gitignore",
"chars": 36,
"preview": "# コンパイラが生成する成果物や中間ファイルが置かれる\ntarget/\n"
},
{
"path": "ch12/static-link/Cargo.toml",
"chars": 174,
"preview": "[package]\nname = \"static-link\"\nversion = \"0.1.0\"\nauthors = [\"Rust Bicycle Book <bicycle-book@example.com>\"]\nedition = \"2"
},
{
"path": "ch12/static-link/build.rs",
"chars": 72,
"preview": "fn main() {\n cc::Build::new().file(\"c_src/fib.c\").compile(\"fib\");\n}\n\n"
}
]
// ... and 5 more files (download for full content)
About this extraction
This page contains the full source code of the ghmagazine/rustbook GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 205 files (209.7 KB), approximately 82.2k tokens, and a symbol index with 596 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.