Comprehensive Rust ðŠ ãžãããã
This is a free Rust course developed by the Android team at Google. The course covers the full spectrum of Rust, from basic syntax to advanced topics like generics and error handling.
ã³ãŒã¹ã®ææ°ããŒãžã§ã³ã¯ https://google.github.io/comprehensive-rust/ ã«ãããŸããä»ã®å Žæã§ãèªã¿ã®å Žåã¯ããã¡ãã§ææ°æ å ±ãã確èªãã ããã
The course is available in other languages. Select your preferred language in the top right corner of the page or check the Translations page for a list of all available translations.
The course is also available as a PDF.
æ¬è¬åº§ã®ç®çã¯ãRustãæããäºã§ããRustã«é¢ããåæç¥èã¯äžèŠãšããŠããã次ã®ç®æšãèšå®ããŠããŸãïŒ
- Rustã®åºæ¬æ§æãšèšèªã«ã€ããŠã®ç解ãæ·±ããã
- æ¢åã®ããã°ã©ã ãä¿®æ£ããããæ°èŠããã°ã©ã ãRustã§æžããããã«ããã
- äžè¬çãªRustã®ã€ãã£ãªã ã玹ä»ããã
ã³ãŒã¹ã®æåã®4æ¥éããRust ã®åºç€ããšåŒã³ãŸãã
Building on this, you're invited to dive into one or more specialized topics:
- Android: a half-day course on using Rust for Android platform development (AOSP). This includes interoperability with C, C++, and Java.
- Chromium: a half-day course on using Rust within Chromium based browsers. This includes interoperability with C++ and how to include third-party crates in Chromium.
- Bare-metal: a whole-day class on using Rust for bare-metal (embedded) development. Both microcontrollers and application processors are covered.
- Concurrency: a whole-day class on concurrency in Rust. We cover both classical concurrency (preemptively scheduling using threads and mutexes) and async/await concurrency (cooperative multitasking using futures).
æ¬è¬åº§ã®å¯Ÿè±¡å€
Rustã¯éåžžã«æ±çšæ§ã®é«ãèšèªã§ãããæ°æ¥ã§å šãŠãç¶²çŸ ããäºã¯ã§ããŸãããæ¬è¬åº§ã®ç®æšãšããŠèšå®ãããŠããªããã®ã«ã¯ã以äžã®ãããªãã®ããããŸãïŒ
- Learning how to develop macros: please see Chapter 19.5 in the Rust Book and Rust by Example instead.
åæç¥è
The course assumes that you already know how to program. Rust is a statically-typed language and we will sometimes make comparisons with C and C++ to better explain or contrast the Rust approach.
If you know how to program in a dynamically-typed language such as Python or JavaScript, then you will be able to follow along just fine too.
ããã¯ã¹ããŒã«ãŒããŒãã®äžäŸã§ããããã䜿çšããŠã¹ã©ã€ããææããŸããè¬åž«ãã«ããŒãã¹ãèŠç¹ããææ¥ã§ããåºã質åãžã®åçãªã©ãå«ãŸããŸãã
è¬åº§ã®éå¶ã«ã€ããŠ
ãã®ããŒãžã¯è¬åž«çšã§ãã
以äžã¯ãGoogleå ã§ã®è¬åº§ã®éå¶æ¹æ³ã«é¢ããæ å ±ã§ãã
ã¯ã©ã¹ã¯éåžžåå9æããååŸ4æãŸã§ã§ãéäžã§1æéã®æŒé£äŒæ©ããããŸããã€ãŸããååã®ã¯ã©ã¹ã3æéãååŸã®ã¯ã©ã¹ã3æéãšãªããŸããã©ã¡ãã®ã»ãã·ã§ã³ã«ããäŒæ©ãšãåè¬è ãæŒç¿ã«åãçµãããã®æéãè€æ°åå«ãŸããŠããŸãã
è¬åº§éå§ãŸã§ã«ã以äžã«ãããæºåãæžãŸããŠãããšè¯ãã§ãããïŒ
-
è³æã«æ £ããŠãããŠãã ãããèŠç¹ã匷調ããããã«ã¹ããŒã«ãŒããŒããçšæãããŠããŸãïŒå 容ã®è¿œå ã«ãååãã ããïŒïŒããã¬ãŒã³æã«ã¯ãã¹ã¯ãªãŒã³ãèŠãããç¶æ ã§ä¿ã€ããã«ãã¹ããŒã«ãŒããŒãã¯ãããã¢ãããŠã£ã³ããŠã§éããŠãã ããïŒã¹ããŒã«ãŒããŒãã®æšªã«ããå°ããªç¢å°ãã¯ãªãã¯ïŒã
-
Decide on the dates. Since the course takes four days, we recommend that you schedule the days over two weeks. Course participants have said that they find it helpful to have a gap in the course since it helps them process all the information we give them.
-
ååãªåºãã®éšå±ã確ä¿ããŠãããŠãã ããã15~25åçšåºŠã®ã¯ã©ã¹ãæšå¥šããŠããŸããåè¬è ã«ãšã£ãŠè³ªåããããã人æ°ã§ããã1人ã®è¬åž«ã質åã«çããæéã確ä¿ã§ããèŠæš¡ã ããã§ãããŸããçããã¯PCã§äœæ¥ãããå¿ èŠããããããè¬åž«ãå«ãã人æ°åã®æºãçšæããŠãããŠãã ãããã©ã€ãã³ãŒãã£ã³ã°åœ¢åŒã§ã®å®æœãæ³å®ããŠãããããè¬å£ã¯äžèŠã§ãã
-
åœæ¥ã¯å°ãæ©ãã«å°çããŠæºåãããŠãã ãããèªåã®PCã§å®è¡ãã
mdbook serve
ããçŽæ¥ãã¬ãŒã³ãè¡ãäºãæšå¥šããŸãïŒã€ã³ã¹ããŒã«æé ã¯ãã¡ãïŒãããã«ãããããŒãžåãæ¿ãæã«é 延ãªãã§æé©ãªããã©ãŒãã³ã¹ãåŸãããŸãããŸããPCã䜿çšããäºã§ãåè¬è ãèªåèªèº«ãèŠã€ããã¿ã€ããã¹ãªã©ãä¿®æ£å¯èœã«ãªããŸãã -
ç·Žç¿åé¡ã¯å人ãå°ããã°ã«ãŒãã§è§£ããŠãã ãããåçãã¬ãã¥ãŒããæéãå«ããåç·Žç¿åé¡ã«30~45åãè²»ãããŸããåè¬è ãè¡ãè©°ãŸã£ãŠãããã©ãããäœã質åãããããªã©ç¢ºèªããŠãã ãããè€æ°ã®åè¬è ãåãåé¡ã§è©°ãŸã£ãŠããå Žåãã¯ã©ã¹å šäœã«å¯ŸããŠãããå ±æãã解決çãæäŸããŠãã ãããäŸãã°ãæ¢ããŠããæ å ±ãæšæºã©ã€ãã©ãªã®ã©ãã«ãããã瀺ãããªã©ã
以äžã§ããéå¶é 匵ã£ãŠãã ããïŒçããã«ãšã£ãŠã楜ããæéã«ãªããŸãããã«ïŒ
æ¬è¬åº§ã®æ¹åã«åããŠãã£ãŒãããã¯ããé¡ãããŸããããŸããã£ãç¹ãæ¹åç¹ã«ã€ããŠå¹ åºããæèŠãèãããã ãããåè¬è ããã®ãã£ãŒãããã¯ãæè¿ããŠãããŸãïŒ
è¬åº§ã®æ§æ
ãã®ããŒãžã¯è¬åž«çšã§ãã
Rust ã®åºç€
Rust ã®åºç€ ãæ§æããæåã® 4 æ¥éã§ãããŸããŸãªé ç®ãé§ã足ã§åŠã³ãŸãã
ã³ãŒã¹ã®ã¹ã±ãžã¥ãŒã«:
- Day 1 Morning (2 hours and 5 minutes, including breaks)
Segment | Duration |
---|---|
ãããã | 5 minutes |
Hello, World | 15 minutes |
åãšå€ | 40 minutes |
å¶åŸ¡ãããŒã®åºæ¬ | 40 minutes |
- Day 1 Afternoon (2 hours and 35 minutes, including breaks)
Segment | Duration |
---|---|
ã¿ãã«ãšé å | 35 minutes |
åç § | 55 minutes |
ãŠãŒã¶ãŒå®çŸ©å | 50 minutes |
- Day 2 Morning (2 hours and 10 minutes, including breaks)
Segment | Duration |
---|---|
ãããã | 3 minutes |
ãã¿ãŒã³ãããã³ã° | 1 hour |
Methods and Traits | 50 minutes |
- Day 2 Afternoon (3 hours and 15 minutes, including breaks)
Segment | Duration |
---|---|
ãžã§ããªã¯ã¹ïŒgenericsïŒ | 45 minutes |
æšæºã©ã€ãã©ãªå ã®å | 1 hour |
æšæºã©ã€ãã©ãªå ã®ãã¬ã€ã | 1 hour and 10 minutes |
- Day 3 Morning (2 hours and 20 minutes, including breaks)
Segment | Duration |
---|---|
ãããã | 3 minutes |
ã¡ã¢ãªç®¡ç | 1 hour |
ã¹ããŒããã€ã³ã¿ | 55 minutes |
- Day 3 Afternoon (1 hour and 55 minutes, including breaks)
Segment | Duration |
---|---|
åçš | 55 minutes |
ã©ã€ãã¿ã€ã | 50 minutes |
- Day 4 Morning (2 hours and 40 minutes, including breaks)
Segment | Duration |
---|---|
ãããã | 3 minutes |
ã€ãã¬ãŒã¿ | 45 minutes |
ã¢ãžã¥ãŒã« | 40 minutes |
ãã¹ã | 45 minutes |
- Day 4 Afternoon (2 hours and 20 minutes, including breaks)
Segment | Duration |
---|---|
ãšã©ãŒåŠç | 1 hour and 5 minutes |
Unsafe Rust | 1 hour and 5 minutes |
å°éçãªãããã¯
In addition to the 4-day class on Rust Fundamentals, we cover some more specialized topics:
Rust in Android
The Rust in Android deep dive is a half-day course on using Rust for Android platform development. This includes interoperability with C, C++, and Java.
AOSPã®ãã§ãã¯ã¢ãŠããå¿
èŠã§ããåã端æ«ããè¬åº§ã®ãªããžããªããã§ãã¯ã¢ãŠãããsrc/android/
ãã£ã¬ã¯ããªãAOSPãã§ãã¯ã¢ãŠãã®ã«ãŒãã«ç§»åããŠãã ãããããã«ãããAndroidãã«ãã·ã¹ãã ãsrc/android/
å
ã®Android.bp
ã確èªã§ããããã«ãªããŸãã
ãšãã¥ã¬ãŒã¿ãŸãã¯å®éã®ããã€ã¹ã§adb sync
ãæ©èœããäºã確èªããsrc/android/build_all.sh
ã䜿çšããŠå
šãŠã®Androidã®äŸãäºåã«ãã«ãããŠãã ãããã¹ã¯ãªãããèªãã§å®è¡ã³ãã³ãã確èªããæåã§å®è¡ããéã«æ£åžžã«åäœããäºã確èªããŠãã ããã
Rust in Chromium
Chromium ã§ã® Rust ã¯åæ¥ã³ãŒã¹ã§ãChromium ãã©ãŠã¶ã®äžéšãšã㊠Rust ã䜿çšããæ¹æ³ã«ã€ããŠè©³ãã説æããŸããChromium ã® gn
ãã«ãã·ã¹ãã 㧠Rust ã䜿çšããããšã§ããµãŒãããŒã㣠ã©ã€ãã©ãªïŒãã¯ã¬ãŒããïŒãããã³ C++ ãšã®çžäºéçšæ§ãå°å
¥ã§ããŸãã
åè¬è ã¯ãChromium ããã«ãã§ããå¿ èŠããããŸããæéãççž®ã§ãããããã°ã®ã³ã³ããŒãã³ã ãã«ãã æšå¥š ããŸãããã©ã®ãããªãã«ãã§ãåé¡ãããŸãããäœæãã Chromium ãã©ãŠã¶ãå®è¡ã§ããããšã確èªããŸãã
Bare-Metal Rust
The Bare-Metal Rust deep dive is a full day class on using Rust for bare-metal (embedded) development. Both microcontrollers and application processors are covered.
ãã€ã¯ãã³ã³ãããŒã©ã®ç« ã§ã¯ãäºåã«BBCmicro:bitv2éçºããŒããè³Œå ¥ããå¿ èŠããããŸãããŸããwelcomeããŒãžã§èª¬æãããŠããããã«ãè€æ°ã®ããã±ãŒãžãã€ã³ã¹ããŒã«ããå¿ èŠããããŸãã
Rustã§ã®äžŠè¡æ§
The Concurrency in Rust deep dive is a full day class on classical as well as async
/await
concurrency.
æ°èŠã¯ã¬ãŒãã®äœæãšãäŸåé¢ä¿ïŒdependenciesïŒã®ããŠã³ããŒããå¿
èŠã§ãããã®åŸãäŸãsrc/main.rs
ã«ã³ããããŠå®è¡ããäºãã§ããŸãïŒ
cargo init concurrency
cd concurrency
cargo add tokio --features full
cargo run
ã³ãŒã¹ã®ã¹ã±ãžã¥ãŒã«:
- Morning (3 hours and 20 minutes, including breaks)
Segment | Duration |
---|---|
ã¹ã¬ãã | 30 minutes |
ãã£ãã« | 20 minutes |
SendãšSync | 15 minutes |
ç¶æ å ±æ | 30 minutes |
ç·Žç¿åé¡ | 1 hour and 10 minutes |
- Afternoon (3 hours and 20 minutes, including breaks)
Segment | Duration |
---|---|
Asyncã®åºç€ | 30 minutes |
ãã£ãã«ãšå¶åŸ¡ãã㌠| 20 minutes |
èœãšãç©Ž | 55 minutes |
ç·Žç¿åé¡ | 1 hour and 10 minutes |
ãã©ãŒããã
æ¬è¬åº§ã¯ã€ã³ã¿ã©ã¯ãã£ããªåœ¢åŒã§è¡ããŸããç©æ¥µçã«è³ªåããŠãRustãžã®ç解ãæ·±ããŠãã ããïŒ
ããŒããŒã ã·ã§ãŒãã«ãã
mdBookã«ã¯ã䟿å©ãªã·ã§ãŒãã«ããããŒãããã€ãååšããŸãïŒ
- Arrow-Left: Navigate to the previous page.
- Arrow-Right: Navigate to the next page.
- Ctrl + Enter: Execute the code sample that has focus.
- s: Activate the search bar.
翻蚳
æ¬è³æã¯ããã©ã³ãã£ã¢ã«ãã£ãŠç¿»èš³ãããŠããŸãïŒ
- ãã«ãã¬ã«èªïŒãã©ãžã«ïŒ: @rastringerã@hugojacobã@joaovicmendesã@henrif75
- Chinese (Simplified) by @suetfei, @wnghl, @anlunx, @kongy, @noahdragon, @superwhd, @SketchK, and @nodmp.
- äžåœèªïŒç¹äœåïŒ: @hueichã@victorhsiehã@mingycã@kuanhungchenã@johnathan79717
- Farsi by @DannyRavi, @javad-jafari, @Alix1383, @moaminsharifi , @hamidrezakp and @mehrad77.
- Japanese by @CoinEZ-JPN, @momotaro1105, @HidenoriKobayashi and @kantasv.
- Korean by @keispace, @jiyongp, @jooyunghan, and @namhyung.
- ã¹ãã€ã³èª: @deavid
- Ukrainian by @git-user-cpp, @yaremam and @reta.
- Farsi by @DannyRavi, @javad-jafari, @Alix1383, @moaminsharifi, @hamidrezakp and @mehrad77.
ç»é¢å³äžã®èšèªåãæ¿ããã¿ã³ãããåãæ¿ããè¡ãªã£ãŠãã ããã
Incomplete Translations
é²è¡äžã®ç¿»èš³ãå€æ°ãããŸããææ°ã®ç¿»èš³ãžã®ãªã³ã¯ã以äžã«ç€ºããŸãã
- Arabic by @younies
- ãã³ã¬ã«èª: @raselmandol
- French by @KookaS, @vcaen and @AdrienBaudemont.
- ãã€ãèª: @Throvnã@ronaldfw
- Italian by @henrythebuilder and @detro.
The full list of translations with their current status is also available either as of their last update or synced to the latest version of the course.
ãã®åãçµã¿ã«ãååããã ããå Žåã¯ãour instructionsãã芧ãã ããã翻蚳ã¯issue trackerã§ç®¡çãããŠããŸãã
Cargoã®äœ¿çš
RustãåŠã³å§ãããšããŸããªãRustãšã³ã·ã¹ãã ã§åºã䜿ãããŠãããã«ãã·ã¹ãã å Œããã±ãŒãžãããŒãžã£ã§ããCargoãšããæšæºããŒã«ã«åºäŒããŸããããã§ã¯ãCargoã®æŠèŠã䜿çšæ¹æ³ããããŠæ¬è¬åº§ã«ãããéèŠæ§ã«ã€ããŠç°¡åã«èª¬æããŸãã
ã€ã³ã¹ããŒã«
https://rustup.rs/ ã®æé ã«æ²¿ã£ãŠã€ã³ã¹ããŒã«ããŠãã ããã
This will give you the Cargo build tool (cargo
) and the Rust compiler (rustc
). You will also get rustup
, a command line utility that you can use to install to different compiler versions.
Rust ãã€ã³ã¹ããŒã«ããããRust ã§åäœããããã«ãšãã£ã¿ãŸã㯠IDE ãèšå®ããå¿ èŠããããŸããã»ãšãã©ã®ãšãã£ã¿ã§ã¯ãrust-analyzer ãšéä¿¡ããããšã§ãããè¡ããŸããrust-analyzer ã¯ãVS CodeãEmacsãVim / Neovim ãªã©ãå€ãã®ãšãã£ã¿åãã«ãªãŒãã³ã³ããªãŒãæ©èœãšãå®çŸ©ã«ç§»åãæ©èœãæäŸããŸããRustRover ãšããå¥ã® IDE ãçšæãããŠããŸãã
-
On Debian/Ubuntu, you can also install Cargo, the Rust source and the Rust formatter via
apt
. However, this gets you an outdated Rust version and may lead to unexpected behavior. The command would be:sudo apt install cargo rust-src rustfmt
-
On macOS, you can use Homebrew to install Rust, but this may provide an outdated version. Therefore, it is recommended to install Rust from the official site.
Rust ãšã³ã·ã¹ãã
Rustãšã³ã·ã¹ãã ã®äž»èŠããŒã«ã¯ä»¥äžã®éãã§ãïŒ
-
rustc
ïŒ Rustã®ã³ã³ãã€ã©ã§ãã.rs
ãã¡ã€ã«ããã€ããªãä»ã®äžé圢åŒã«å€æããŸãã -
cargo
: the Rust dependency manager and build tool. Cargo knows how to download dependencies, usually hosted on https://crates.io, and it will pass them torustc
when building your project. Cargo also comes with a built-in test runner which is used to execute unit tests. -
rustup
: the Rust toolchain installer and updater. This tool is used to install and updaterustc
andcargo
when new versions of Rust are released. In addition,rustup
can also download documentation for the standard library. You can have multiple versions of Rust installed at once andrustup
will let you switch between them as needed.
èŠç¹ïŒ
-
Rustèšèªãšã³ã³ãã€ã©ã¯ã6é±éã®ãªãªãŒã¹ãµã€ã¯ã«ãæ¡çšããŠããŸããæ°ãããªãªãŒã¹ã¯ãå€ããªãªãŒã¹ãšã®åŸæ¹äºææ§ãç¶æããªãããæ°æ©èœãæäŸããŸãã
-
ãªãªãŒã¹ãã£ãã«ã¯3ã€ãããŸãïŒãstableããbetaããnightlyãã
-
æ°æ©èœã¯ãnightlyãã§ãã¹ãããããbetaãã6é±éæ¯ã«ãstableããšãªããŸãã
-
äŸåé¢ä¿ã¯ã代æ¿ã® ã¬ãžã¹ããªãgitããã©ã«ããªã©ãã解決ããããšãã§ããŸãã
-
Rustã«ã¯editionsïŒãšãã£ã·ã§ã³ïŒããããŸãïŒçŸåšã®ãšãã£ã·ã§ã³ã¯Rust2021ã§ãã以åã¯Rust2015ãšRust2018ã§ããã
-
ãšãã£ã·ã§ã³ã§ã¯ãåŸæ¹éäºæãªå€æŽãå ããäºãã§ããŸãã
-
ã³ãŒãã®ç Žæãé²ãããã«ããšãã£ã·ã§ã³ã¯ãªããã€ã³æ¹åŒã§ãïŒ
Cargo.toml
ã§ãã¯ã¬ãŒãã«å¯ŸããŠé©çšããããšãã£ã·ã§ã³ãéžæããŸãã -
ãšã³ã·ã¹ãã ã®åæãé¿ããããã«ãã³ã³ãã€ã©ã¯ç°ãªããšãã£ã·ã§ã³ã®ã³ãŒããæ··åšãããäºãã§ããŸãã
-
ã³ã³ãã€ã©ãçŽæ¥äœ¿çšããäºã¯éåžžã«çšã§ãããåºæ¬çã«ã¯
cargo
ãä»ããŸãã -
It might be worth alluding that Cargo itself is an extremely powerful and comprehensive tool. It is capable of many advanced features including but not limited to:
- ãããžã§ã¯ãã»ããã±ãŒãžã®æ§é 管ç
- workspacesïŒã¯ãŒã¯ã¹ããŒã¹ïŒ
- éçºçšãšã©ã³ã¿ã€ã çšã®äŸåé¢ä¿ç®¡çã»ãã£ãã·ã¥
- build scriptingïŒãã«ãã¹ã¯ãªããïŒ
- global installation
- cargo clippyãªã©ã®ãµãã³ãã³ããã©ã°ã€ã³ã«ããæ¡åŒµ
-
詳现ã¯official Cargo Bookãåç §ããŠãã ããã
-
è¬åº§ã®ãµã³ãã«ã³ãŒã
æ¬è¬åº§ã¯ãäž»ã«ãã©ãŠã¶å ã§å®è¡å¯èœãªäŸã䜿ããŸããããããäºã§ãã»ããã¢ããã容æã«ãªããäžè²«ããéçºç°å¢ã®æäŸãå¯èœãšãªããŸãã
ãã ããã§ããã°Cargoãã€ã³ã¹ããŒã«ããŠãã ããïŒ ç·Žç¿åé¡ã§äœ¿ãããšäŸ¿å©ã§ãããŸãæçµæ¥ã«äŸåé¢ä¿ãæ±ã課é¡ãæ±ããŸãããããã§ã¯Cargoãå¿ èŠã«ãªããŸãã
è¬åº§ã®ã³ãŒããããã¯ã¯ã€ã³ã¿ã©ã¯ãã£ãã§ãïŒ
fn main() { println!("Edit me!"); }
You can use Ctrl + Enter to execute the code when focus is in the text box.
ã»ãšãã©ã®ãµã³ãã«ã³ãŒãã¯äžèšã®ããã«ç·šéå¯èœã§ãããäžéšã ã以äžã®ãããªçç±ããç·šéäžå¯ãšãªã£ãŠããŸãïŒ
-
è¬åº§ã®ããŒãžå ã«åã蟌ãŸãããã¬ã€ã°ã©ãŠã³ãã§ãŠããããã¹ãã¯å®è¡ã§ããŸãããã³ãŒããå®éã®ãã¬ã€ã°ã©ãŠã³ãã§éãããã¢ã³ã¹ãã¬ãŒã·ã§ã³ãè¡ãå¿ èŠããããŸãã
-
è¬åº§ã®ããŒãžå ã«åã蟌ãŸãããã¬ã€ã°ã©ãŠã³ãã§ã¯ãããŒãžç§»åãããšç¶æ ã倱ãããŸãïŒæ ã«ãåè¬çã¯ããŒã«ã«ç°å¢ãå®éã®ãã¬ã€ã°ã©ãŠã³ãã䜿çšããŠåé¡ã解ãå¿ èŠããããŸãã
Cargoã䜿ã£ãŠããŒã«ã«ã§å®è¡
ã³ãŒããããŒã«ã«ã§è©Šãããå ŽåãRust Bookã®æé ã«åŸã£ãŠRustãã€ã³ã¹ããŒã«ããŠãã ãããæ£åžžã«ã€ã³ã¹ããŒã«ãããããrustc
ãšcargo
ã䜿ããããã«ãªããŸããææ°ã®stableãªãªãŒã¹ã®ããŒãžã§ã³ã¯ä»¥äžã®éãã§ãïŒ
% rustc --version
rustc 1.69.0 (84c898d65 2023-04-16)
% cargo --version
cargo 1.69.0 (6e9a83356 2023-04-12)
Rust ã¯äžäœäºææ§ãç¶æããŠãããããæ°ããããŒãžã§ã³ã䜿çšããããšãã§ããŸãã
With this in place, follow these steps to build a Rust binary from one of the examples in this training:
-
ãCopy to clipboardãã§ã³ãŒããã³ããŒã
-
cargo new exercise
ã§exercise/
ãã£ã¬ã¯ããªãäœæïŒ$ cargo new exercise Created binary (application) `exercise` package
-
exercise/
ãã£ã¬ã¯ããªã«ç§»åããcargo run
ã§ãã€ããªããã«ãããŠå®è¡ïŒ$ cd exercise $ cargo run Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise) Finished dev [unoptimized + debuginfo] target(s) in 0.75s Running `target/debug/exercise` Hello, world!
-
src/main.rs
ã®ãã€ã©ãŒãã¬ãŒãã³ãŒãããã³ããŒããã³ãŒãã§çœ®ãæããŠãã ãããäŸãã°ãåã®ããŒãžã®äŸã䜿ã£ãå Žåãsrc/main.rs
ã¯ä»¥äžã®ããã«ãªããŸããfn main() { println!("Edit me!"); }
-
cargo run
ã§æŽæ°ããããã€ããªããã«ãããŠå®è¡ïŒ$ cargo run Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise) Finished dev [unoptimized + debuginfo] target(s) in 0.24s Running `target/debug/exercise` Edit me!
-
cargo check
ã§ãããžã§ã¯ãã®ãšã©ãŒãã§ãã¯ãè¡ããcargo build
ã§ã³ã³ãã€ã«ã ãïŒå®è¡ã¯ããïŒãè¡ããŸããéåžžã®ãããã°ãã«ãã§ã¯ãçæããããã¡ã€ã«ã¯target/debug/
ã«æ ŒçŽãããŸããæé©åããããªãªãŒã¹ãã«ãã«ã¯cargo build ârelease
ã䜿ãããã¡ã€ã«ã¯target/release/
ã«æ ŒçŽãããŸãã -
ãããžã§ã¯ãã«äŸåé¢ä¿ãè¿œå ããã«ã¯ã
Cargo.toml
ãç·šéããŸãããã®åŸãcargo
ã³ãã³ããå®è¡ãããšãèªåçã«äžè¶³ããŠããäŸåé¢ä¿ãããŠã³ããŒããããŠã³ã³ãã€ã«ãããŸãã
åè¬è ã«Cargoã®ã€ã³ã¹ããŒã«ãšããŒã«ã«ãšãã£ã¿ã®äœ¿çšãå§ããŠãã ãããéåžžã®éçºç°å¢ãæã€äºã§ãäœæ¥ãã¹ã ãŒãºã«ãªããŸãã
Day 1ãžãããã
This is the first day of Rust Fundamentals. We will cover a lot of ground today:
- Rustã®åºæ¬çãªæ§æïŒ å€æ°ãã¹ã«ã©ãŒåãšè€ååãåæåãæ§é äœãåç §ãé¢æ°ãã¡ãœããã
- Types and type inference.
- å¶åŸ¡ãããŒã®æ§é : ã«ãŒããæ¡ä»¶ãªã©ã
- ãŠãŒã¶ãŒå®çŸ©å: æ§é äœãšåæåã
- ãã¿ãŒã³ ãããã³ã°: åæåãæ§é äœãé åã®å解ã
ã¹ã±ãžã¥ãŒã«
Including 10 minute breaks, this session should take about 2 hours and 5 minutes. It contains:
Segment | Duration |
---|---|
ãããã | 5 minutes |
Hello, World | 15 minutes |
åãšå€ | 40 minutes |
å¶åŸ¡ãããŒã®åºæ¬ | 40 minutes |
åè¬çã«äŒããŠãã ããïŒ
- They should ask questions when they get them, don't save them to the end.
- The class is meant to be interactive and discussions are very much encouraged!
- As an instructor, you should try to keep the discussions relevant, i.e., keep the discussions related to how Rust does things vs some other language. It can be hard to find the right balance, but err on the side of allowing discussions since they engage people much more than one-way communication.
- The questions will likely mean that we talk about things ahead of the slides.
- This is perfectly okay! Repetition is an important part of learning. Remember that the slides are just a support and you are free to skip them as you like.
1 æ¥ç®ã¯ãä»ã®èšèªã«ãå ±éãã Rust ã®ãåºæ¬çãªãäºé ã瀺ããŸããRust ã®ããé«åºŠãªéšåã«ã€ããŠã¯ãåŸæ¥èª¬æããŸãã
æ宀ã§æããå Žåã¯ãããã§ã¹ã±ãžã¥ãŒã«ã確èªããããšãããããããŸããåã»ã°ã¡ã³ãã®çµããã«æŒç¿ãè¡ããäŒæ©ãæãã§ããçãåãããããŠãã ãããäžèšã®æéé åã¯ããããŸã§ã³ãŒã¹ãäºå®ã©ããã«é²ããããã®ç®å®ã§ãã®ã§ãå¿ èŠã«å¿ããŠæè»ã«èª¿æŽããŠãã ããã
Hello, World
This segment should take about 15 minutes. It contains:
Slide | Duration |
---|---|
Rustãšã¯ïŒ | 10 minutes |
Rustã®ã¡ãªãã | 3 minutes |
ãã¬ã€ã°ã©ãŠã³ã | 2 minutes |
Rustãšã¯ïŒ
Rustã¯2015幎ã«1.0çããªãªãŒã¹ãããæ°ããããã°ã©ãã³ã°èšèªã§ãïŒ
- Rustã¯C++ãšåæ§ã«ãéçã«ã³ã³ãã€ã«ãããèšèªã§ã
rustc
ã¯ããã¯ãšã³ãã«LLVMã䜿çšããŠããŸãã
- Rustã¯å€ãã®ãã©ãããã©ãŒã ãšã¢ãŒããã¯ãã£ããµããŒãããŠããŸãïŒ
- x86, ARM, WebAssembly, âŠ
- Linux, Mac, Windows, âŠ
- Rustã¯æ§ã
ãªããã€ã¹ã§äœ¿çšãããŠããŸãïŒ
- ãã¡ãŒã ãŠã§ã¢ãããŒãããŒãã
- ã¹ããŒããã£ã¹ãã¬ã€ã
- æºåž¯é»è©±ã
- ãã¹ã¯ãããã
- ãµãŒãã
RustãšC++ã䌌ãŠãããšãã:
- é«ãæè»æ§ã
- é«åºŠãªå¶åŸ¡æ§ã
- Can be scaled down to very constrained devices such as microcontrollers.
- ã©ã³ã¿ã€ã ãã¬ããŒãžã³ã¬ã¯ã·ã§ã³ããªãã
- ããã©ãŒãã³ã¹ãç ç²ã«ãããä¿¡é Œæ§ãšå®å šæ§ã«çŠç¹ãåœãŠãŠããã
Rustã®ã¡ãªãã
Rustã®ãŠããŒã¯ãªã»ãŒã«ã¹ãã€ã³ããããã€ã玹ä»ããŸãïŒ
-
ã³ã³ãã€ã«æã®ã¡ã¢ãªå®å šæ§ - ã¯ã©ã¹å šäœã®ã¡ã¢ãªã®ãã°ãã³ã³ãã€ã«æã«é²æ¢ããŸãã
- æªåæåã®å€æ°ããªãã
- äºé解æŸãèµ·ããªãã
- 解æŸæžã¿ã¡ã¢ãªäœ¿çšïŒuse-after-freeïŒããªãã
NULL
ïŒãã«ïŒãã€ã³ã¿ããªãã- ãã¥ãŒããã¯ã¹ïŒmutexïŒã®ããã¯ã®è§£é€å¿ãããªãã
- ã¹ã¬ããéã§ããŒã¿ç«¶åããªãã
- ã€ãã¬ãŒã¿ãç¡å¹åãããªãã
-
æªå®çŸ©ã®ã©ã³ã¿ã€ã åäœããªã - Rust ã¹ããŒãã¡ã³ãã§è¡ãããåŠçãæªèŠå®ã®ãŸãŸæ®ãããšã¯ãããŸããã
- é åãžã®ã¢ã¯ã»ã¹ã«ã¯å¢çãã§ãã¯ãè¡ãããã
- Integer overflow is defined (panic or wrap-around).
-
ææ°ã®èšèªæ©èœ - é«æ°Žæºèšèªã«å¹æµããè¡šçŸåãããã人éã䜿ããããæ©èœãåããŠããŸãã
- åæåãšãã¿ãŒã³ãããã³ã°
- ãžã§ããªã¯ã¹
- ãªãŒããŒãããã®ãªãFFI
- ãŒãã³ã¹ãæœè±¡å
- åªç§ãªã³ã³ãã€ã«ãšã©ãŒã
- çµã¿èŸŒã¿ã®äŸåé¢ä¿ãããŒãžã£ã
- çµã¿èŸŒã¿ã®ãã¹ããµããŒãã
- Language Server ProtocolïŒLSPïŒã®ãµããŒãã
ããã«ã¯ããŸãæéããããªãã§ãã ããããããã®ãã€ã³ãã«ã€ããŠã¯ãåŸã»ã©è©³ãã説æããŸãã
åè¬è ã«ã©ã®èšèªã®çµéšãããããå°ããŠãã ãããåçã«å¿ããŠãRustã®ããŸããŸãªç¹åŸŽã匷調ããããšãã§ããŸãïŒ
-
CãŸãã¯C++ã®çµéšãããå ŽåïŒ Rustã¯åçšãã§ãã«ãŒãä»ããŠå®è¡æãšã©ãŒã®äžéšãæé€ããŠãããŸããããã«å ããCãC++ãšåçã®ããã©ãŒãã³ã¹ãåŸãããšãã§ããã¡ã¢ãªå®å šæ§ã®åé¡ã¯ãããŸãããããã«ããã¿ãŒã³ãããã³ã°ãçµã¿èŸŒã¿ã®äŸåé¢ä¿ç®¡çãªã©ã®æ§é èŠçŽ ãå«ãçŸä»£çãªèšèªã§ãã
-
Experience with Java, Go, Python, JavaScript...: You get the same memory safety as in those languages, plus a similar high-level language feeling. In addition you get fast and predictable performance like C and C++ (no garbage collector) as well as access to low-level hardware (should you need it).
ãã¬ã€ã°ã©ãŠã³ã
ãã®ã³ãŒã¹ã®äŸãæŒç¿ã«ã¯ãçã Rust ããã°ã©ã ãç°¡åã«å®è¡ã§ãã Rust ãã¬ã€ã°ã©ãŠã³ã ã䜿çšããŸããæåã®ãhello-worldãããã°ã©ã ãå®è¡ããŠã¿ãŸãããã次ã®ãããªäŸ¿å©ãªæ©èœããããŸãã
-
ãToolsã ã«ãã
rustfmt
ãªãã·ã§ã³ã䜿çšããŠãã³ãŒãããstandardãã®åœ¢åŒã§ãã©ãŒãããããŸãã -
Rust ã«ã¯ãã³ãŒããçæããããã®äž»èŠãªããããã¡ã€ã«ãã 2 ã€ãããŸãã1 ã€ã¯ DebugïŒè¿œå ã®ã©ã³ã¿ã€ã ãã§ãã¯ããããæé©åãå°ãªãïŒã§ããã 1 ã€ã¯ ReleaseïŒã©ã³ã¿ã€ã ãã§ãã¯ãå°ãªããæé©åãå€ãïŒã§ãããããã¯äžéšã® [Debug] ããã¢ã¯ã»ã¹ã§ããŸãã
-
èå³ãããå Žåã¯ãã...ã ã®äžã«ãã ãASMã ã䜿çšãããšãçæãããã¢ã»ã³ã㪠ã³ãŒãã確èªã§ããŸãã
åè¬è ãäŒæ©ã«å ¥ã£ããããã¬ã€ã°ã©ãŠã³ããéããŠããããè©ŠããŠã¿ãããä¿ããŸããã¿ããéãããŸãŸã«ããŠãã³ãŒã¹ã®æ®ãã®éšåã§åŠç¿ããããšãè©ŠãããããããŸããããããã¯ãRust ã®æé©åãçæãããã¢ã»ã³ããªã«ã€ããŠè©³ããç¥ãããåè¬è ã«ç¹ã«åœ¹ç«ã¡ãŸãã
åãšå€
This segment should take about 40 minutes. It contains:
Slide | Duration |
---|---|
Hello, World | 5 minutes |
å€æ° | 5 minutes |
å€ | 5 minutes |
ç®è¡ | 3 minutes |
åæšè« | 3 minutes |
æŒç¿: ãã£ãããã | 15 minutes |
Hello, World
ãã£ããäžçªã·ã³ãã«ãªããã°ã©ã ã§ããå®çªã®Hello Worldããã¿ãŠã¿ãŸãããïŒ
fn main() { println!("Hello ð!"); }
ããã°ã©ã ã®äžèº«ïŒ
- é¢æ°ã¯
fn
ã§å°å ¥ãããŸãã - CãC++ãšåæ§ã«ããããã¯ã¯æ³¢æ¬åŒ§ã§å²ã¿ãŸãã
main
é¢æ°ã¯ããã°ã©ã ã®ãšã³ããªãŒãã€ã³ãã«ãªããŸãã- Rustã«ã¯è¡ççãªãã¯ããããã
println!
ã¯ãã®äžäŸã§ãã - Rustã®æååã¯UTF-8ã§ãšã³ã³ãŒããããã©ããªUnicodeæåã§ãå«ãäºãã§ããŸãã
This slide tries to make the students comfortable with Rust code. They will see a ton of it over the next four days so we start small with something familiar.
èŠç¹ïŒ
-
Rust is very much like other languages in the C/C++/Java tradition. It is imperative and it doesn't try to reinvent things unless absolutely necessary.
-
Rust is modern with full support for things like Unicode.
-
Rust uses macros for situations where you want to have a variable number of arguments (no function overloading).
-
Macros being 'hygienic' means they don't accidentally capture identifiers from the scope they are used in. Rust macros are actually only partially hygienic.
-
Rust ã¯ãã«ããã©ãã€ã ã§ããããšãã°ã匷å㪠ãªããžã§ã¯ãæåããã°ã©ãã³ã°æ©èœ ãåããŠããäžæ¹ãéé¢æ°åèšèªã§ããã«ãããããããããŸããŸãª é¢æ°çæŠå¿µ ãå å ããŠããŸãã
å€æ°
Rust ã¯éçåä»ãã«ãã£ãŠåå®å
šæ§ãæäŸããŸããå€æ°ã®ãã€ã³ãã£ã³ã°ã¯ let
ã䜿çšããŠè¡ããŸãã
fn main() { let x: i32 = 10; println!("x: {x}"); // x = 20; // println!("x: {x}"); }
-
x = 20
ã®ã³ã¡ã³ãåã解é€ããŠãå€æ°ãããã©ã«ãã§äžå€ã§ããããšã瀺ããŸããå€æŽãèš±å¯ããã«ã¯ãmut
ããŒã¯ãŒããè¿œå ããŸãã -
ããã§ã®
i32
ã¯å€æ°ã®åã§ããããã¯ã³ã³ãã€ã«æã«æå®ããå¿ èŠããããŸãããå€ãã®å Žåãããã°ã©ããŒã¯åæšè«ïŒåŸè¿°ïŒã䜿çšããããšã§ãããçç¥ã§ããŸãã
å€
åºæ¬çãªçµã¿èŸŒã¿åãšãååã®ãªãã©ã«å€ã®æ§æã以äžã«ç€ºããŸãã
å | ãªãã©ã« | |
---|---|---|
笊å·ä»ãæŽæ° | i8 ãi16 ãi32 ãi64 ãi128 ãisize | -10 ã0 ã1_000 ã123_i64 |
笊å·ãªãæŽæ° | u8 ãu16 ãu32 ãu64 ãu128 ãusize | 0 ã123 ã10_u16 |
æµ®åå°æ°ç¹æ° | f32 ãf64 | 3.14 ã-10.0e20 ã2_f32 |
Unicode ã¹ã«ã©ãŒå€ | char | 'a' ã'α' ã'â' |
ããŒã«å€ | bool | true ãfalse |
ååã®å¹ ã¯æ¬¡ã®ãšããã§ãã
iN
ãuN
ãfN
㯠N ãããå¹ ã§ããisize
ãšusize
ã¯ãã€ã³ã¿ã®å¹ ã§ããchar
㯠32 ãããå¹ ã§ããbool
㯠8 ãããå¹ ã§ãã
äžèšã«ã¯ç€ºãããŠããªãæ§æããããŸãã
- æ°åã®ã¢ã³ããŒã¹ã³ã¢ã¯ãã¹ãŠçç¥ã§ããŸããã¢ã³ããŒã¹ã³ã¢ã¯èªã¿ãããããããã«ã®ã¿äœ¿çšããŸãããã®ããã
1_000
ã¯1000
ïŒãŸãã¯10_00
ïŒã123_i64
ã¯123i64
ãšèšè¿°ã§ããŸãã
ç®è¡
fn interproduct(a: i32, b: i32, c: i32) -> i32 { return a * b + b * c + c * a; } fn main() { println!("result: {}", interproduct(120, 100, 248)); }
main
以å€ã®é¢æ°ãåºãŠããã®ã¯ä»åãåããŠã§ããããã®æå³ã¯æ確ã§ããã€ãŸãã3 ã€ã®æŽæ°ãåãã1 ã€ã®æŽæ°ãè¿ããŸããé¢æ°ã«ã€ããŠã¯ãåŸã§è©³ãã説æããŸãã
ç®è¡ã¯ä»ã®èšèªãšãã䌌ãŠãããåªå é äœãé¡äŒŒããŠããŸãã
What about integer overflow? In C and C++ overflow of signed integers is actually undefined, and might do unknown things at runtime. In Rust, it's defined.
i32
ã i16
ã«å€æŽããŠãæŽæ°ãªãŒããŒãããŒã確èªããŸããããã¯ããããã°ãã«ãã§ã¯ãããã¯ã«ãªãïŒãã§ãã¯ããïŒããªãªãŒã¹ãã«ãã§ã¯ã©ãããããŸãããªãŒããŒãããŒã飜åããã£ãªãŒãªã©ã®ãªãã·ã§ã³ããããã¡ãœããæ§æã䜿çšããŠãããã®ãªãã·ã§ã³ã«ã¢ã¯ã»ã¹ããŸãïŒäŸ: (a * b).saturating_add(b * c).saturating_add(c * a)
ïŒã
å®éã«ã¯ãã³ã³ãã€ã©ã¯å®æ°åŒã®ãªãŒããŒãããŒãæ€åºããŸãããã®äŸã§å¥ã®é¢æ°ãå¿ èŠã«ãªãã®ã¯ãã®ããã§ãã
åæšè«
Rust ã¯ãã©ã®ããã«å€æ°ã 䜿çšãããŠããã ã確èªããããšã§ãåãå€å¥ããŸãã
fn takes_u32(x: u32) { println!("u32: {x}"); } fn takes_i8(y: i8) { println!("i8: {y}"); } fn main() { let x = 10; let y = 20; takes_u32(x); takes_i8(y); // takes_u32(y); }
ãã®ã¹ã©ã€ãã¯ãå€æ°ã®å®£èšãšäœ¿çšæ¹æ³ã«ããå¶çŽã«åºã¥ããŠãRust ã³ã³ãã€ã©ãåãæšæž¬ããä»çµã¿ã瀺ããŠããŸãã
ãã®ããã«å®£èšãããå€æ°ã¯ãã©ã®ãããªããŒã¿ãä¿æã§ããåçãªãä»»æã®åãã§ã¯ãªãããšããç¹ã匷調ããããšãéåžžã«éèŠã§ãããã®ãããªå®£èšã«ãã£ãŠçæããããã·ã³ã³ãŒãã¯ãåã®æ瀺çãªå®£èšãšåäžã§ããã³ã³ãã€ã©ã代ããã«äœæ¥ãè¡ããããç°¡æœãªã³ãŒãã®äœæãæ¯æŽããŸãã
æŽæ°ãªãã©ã«ã®åã«å¶çŽããªãå ŽåãRust ã¯ããã©ã«ã㧠i32
ã䜿çšããŸããããã¯ããšã©ãŒ ã¡ãã»ãŒãžã« {integer}
ãšããŠè¡šç€ºãããããšããããŸããåæ§ã«ãæµ®åå°æ°ç¹ãªãã©ã«ã¯ããã©ã«ã㧠f64
ã«ãªããŸãã
fn main() { let x = 3.14; let y = 20; assert_eq!(x, y); // ãšã©ãŒ: `{float} == {integer}` ã®å®è£ ããããŸãã }
æŒç¿: ãã£ãããã
The Fibonacci sequence begins with [0,1]
. For n>1, the n'th Fibonacci number is calculated recursively as the sum of the n-1'th and n-2'th Fibonacci numbers.
n çªç®ã®ãã£ããããæ°ãèšç®ããé¢æ° fib(n)
ãèšè¿°ããŸãããã®é¢æ°ã¯ãã€ãããã¯ããã§ããããã
fn fib(n: u32) -> u32 { if n < 2 { // ããŒã¹ã±ãŒã¹ã todo!("ãããå®è£ ããŠãã ãã") } else { // ååž°çãªã±ãŒã¹ã todo!("ãããå®è£ ããŠãã ãã") } } fn main() { let n = 20; println!("fib({n}) = {}", fib(n)); }
解ç
fn fib(n: u32) -> u32 { if n < 2 { return n; } else { return fib(n - 1) + fib(n - 2); } } fn main() { let n = 20; println!("fib({n}) = {}", fib(n)); }
å¶åŸ¡ãããŒã®åºæ¬
This segment should take about 40 minutes. It contains:
Slide | Duration |
---|---|
if åŒ | 4 minutes |
ã«ãŒã | 5 minutes |
break ãš continue | 4 minutes |
ãããã¯ãšã¹ã³ãŒã | 5 minutes |
é¢æ° | 3 minutes |
ãã¯ã | 2 minutes |
æŒç¿: ã³ã©ããæ°å | 15 minutes |
if
åŒ
Rust ã® if
åŒ ã¯ãä»ã®èšèªã«ããã if
æãšå
šãåãããã«äœ¿ããŸãã
fn main() { let x = 10; if x == 0 { println!("zero!"); } else if x < 100 { println!("biggish"); } else { println!("huge"); } }
ããã«ãif
ãåŒãšããŠãçšããããšãã§ããŸããããããã®ãããã¯ã«ããæåŸã®åŒããif
åŒã®å€ãšãªããŸãã
fn main() { let x = 10; let size = if x < 20 { "small" } else { "large" }; println!("number size: {}", size); }
Because if
is an expression and must have a particular type, both of its branch blocks must have the same type. Show what happens if you add ;
after "small"
in the second example.
An if
expression should be used in the same way as the other expressions. For example, when it is used in a let
statement, the statement must be terminated with a ;
as well. Remove the ;
before println!
to see the compiler error.
ã«ãŒã
Rust ã«ã¯ãwhile
ãloop
ãfor
ã® 3 ã€ã®ã«ãŒã ããŒã¯ãŒãããããŸãã
while
while
ããŒã¯ãŒã ã¯ãä»ã®èšèªã«ããã while
ãšéåžžã«ãã䌌ãåããããŸãã
fn main() { let mut x = 200; while x >= 10 { x = x / 2; } println!("Final x: {x}"); }
for
The for
loop iterates over ranges of values or the items in a collection:
fn main() { for x in 1..5 { println!("x: {x}"); } for elem in [1, 2, 3, 4, 5] { println!("elem: {elem}"); } }
- Under the hood
for
loops use a concept called "iterators" to handle iterating over different kinds of ranges/collections. Iterators will be discussed in more detail later. - Note that the first
for
loop only iterates to4
. Show the1..=5
syntax for an inclusive range.
loop
loop
ã¹ããŒãã¡ã³ã ã¯ãbreak
ãŸã§æ°žä¹
ã«ã«ãŒãããã ãã§ãã
fn main() { let mut i = 0; loop { i += 1; println!("{i}"); if i > 100 { break; } } }
break
ãš continue
次ã®ã€ãã¬ãŒã·ã§ã³ãããããŸéå§ãããå Žå㯠continue
ã䜿çšããŠãã ããã
If you want to exit any kind of loop early, use break
. With loop
, this can take an optional expression that becomes the value of the loop
expression.
fn main() { let mut i = 0; loop { i += 1; if i > 5 { break; } if i % 2 == 0 { continue; } println!("{}", i); } }
Note that loop
is the only looping construct which can return a non-trivial value. This is because it's guaranteed to only return at a break
statement (unlike while
and for
loops, which can also return when the condition fails).
Labels
continue
ãš break
ã¯ãªãã·ã§ã³ã§ã©ãã«åŒæ°ãåãããšãã§ããŸããã©ãã«ã¯ãã¹ãããã«ãŒãããæãåºãããã«äœ¿ãããŸãã
fn main() { let s = [[5, 6, 7], [8, 9, 10], [21, 15, 32]]; let mut elements_searched = 0; let target_value = 10; 'outer: for i in 0..=2 { for j in 0..=2 { elements_searched += 1; if s[i][j] == target_value { break 'outer; } } } print!("elements searched: {elements_searched}"); }
- Labeled break also works on arbitrary blocks, e.g.
#![allow(unused)] fn main() { 'label: { break 'label; println!("This line gets skipped"); } }
ãããã¯ãšã¹ã³ãŒã
ã³ãŒããããã¯
A block in Rust contains a sequence of expressions, enclosed by braces {}
. Each block has a value and a type, which are those of the last expression of the block:
fn main() { let z = 13; let x = { let y = 10; println!("y: {y}"); z - y }; println!("x: {x}"); }
æåŸã®åŒã ;
ã§çµäºããå Žåããããã¯å
šäœã®å€ãšå㯠()
ã«ãªããŸãã
- ãããã¯å
ã«ããæåŸã®è¡ãå€æŽããããšã«ãã£ãŠããããã¯å
šäœã®å€ãå€ããããšãåãããŸããäŸãã°ãè¡æ«ã®ã»ãã³ãã³ãè¿œå /åé€ãããã
return
ã䜿çšãããããããšã§ããããã¯ã®å€ã¯å€åããŸãã
ã¹ã³ãŒããšã·ã£ããŒã€ã³ã°
å€æ°ã®ã¹ã³ãŒãã¯ãå²ãŸãããããã¯å ã«éå®ãããŸãã
å€åŽã®ã¹ã³ãŒãã®å€æ°ãšãåãã¹ã³ãŒãã®å€æ°ã®äž¡æ¹ãã·ã£ããŒã€ã³ã°ã§ããŸãã
fn main() { let a = 10; println!("before: {a}"); { let a = "hello"; println!("inner scope: {a}"); let a = true; println!("shadowed in inner scope: {a}"); } println!("after: {a}"); }
- æåŸã®äŸã®å
åŽã®ãããã¯ã«
b
ãè¿œå ãããã®ãããã¯ã®å€åŽã§ã¢ã¯ã»ã¹ãè©Šã¿ãããšã§ãå€æ°ã®ã¹ã³ãŒããå¶éãããŠããããšã瀺ããŸãã - Shadowing is different from mutation, because after shadowing both variables' memory locations exist at the same time. Both are available under the same name, depending where you use it in the code.
- ã·ã£ããŒã€ã³ã°å€æ°ã®åã¯ããŸããŸã§ãã
- ã·ã£ããŒã€ã³ã°ã¯äžèŠãããã«ããããã«èŠããŸããã
.unwrap()
ã®åŸã®å€ãä¿æããå Žåã«äŸ¿å©ã§ãã
é¢æ°
fn gcd(a: u32, b: u32) -> u32 { if b > 0 { gcd(b, a % b) } else { a } } fn main() { println!("gcd: {}", gcd(143, 52)); }
- 宣èšãã©ã¡ãŒã¿ã®åŸã«ã¯åïŒäžéšã®ããã°ã©ãã³ã°èšèªãšéïŒãæ»ãå€ã®åãç¶ããŸãã
- é¢æ°æ¬äœïŒãŸãã¯ä»»æã®ãããã¯ïŒå
ã®æåŸã®åŒãæ»ãå€ã«ãªããŸããåŒã®æ«å°Ÿã®
;
ãçç¥ããŸããreturn
ããŒã¯ãŒãã¯æ©æãªã¿ãŒã³ã«äœ¿çšã§ããŸãããé¢æ°ã®æåŸã¯ã裞ã®å€ãã®åœ¢åŒã«ããã®ãæ £çšçã§ãïŒreturn
ã䜿çšããã«ã¯gcd
ããªãã¡ã¯ã¿ãªã³ã°ããŸãïŒã - Some functions have no return value, and return the 'unit type',
()
. The compiler will infer this if the return type is omitted. - ãªãŒããŒããŒãã¯ãµããŒããããŠããŸãããåé¢æ°ã®å®è£
㯠1 ã€ã§ãã
- åžžã«åºå®æ°ã®ãã©ã¡ãŒã¿ãåãåããŸããããã©ã«ãã®åŒæ°ã¯ãµããŒããããŠããŸããããã¯ãã䜿çšããŠå¯å€é¢æ°ããµããŒãã§ããŸãã
- åžžã« 1 ã€ã®ãã©ã¡ãŒã¿åã»ãããåãåããŸãããããã®åã¯æ±çšã«ããããšãã§ããŸãããããã«ã€ããŠã¯åŸã§èª¬æããŸãã
ãã¯ã
ãã¯ãã¯ã³ã³ãã€ã«æã« Rust ã³ãŒãã«å±éãããå¯å€é·åŒæ°ãåãããšãã§ããŸãããããã¯æ«å°Ÿã® !
ã§åºå¥ãããŸããRust æšæºã©ã€ãã©ãªã«ã¯ãåçš®ã®äŸ¿å©ãªãã¯ããå«ãŸããŠããŸãã
println!(format, ..)
:std::fmt
ã§èª¬æãããŠããæžåŒãé©çšããŠã1 è¡ãæšæºåºåã«åºåããŸããformat!(format, ..)
:println!
ãšåæ§ã«æ©èœããŸãããçµæãæååãšããŠè¿ããŸããdbg!(expression)
: åŒã®å€ããã°ã«èšé²ããŠè¿ããŸããtodo!()
: äžéšã®ã³ãŒãã«æªå®è£ ã®ããŒã¯ãä»ããŸããå®è¡ãããšãããã¯ãçºçããŸããunreachable!()
: ã³ãŒãã®äžéšãã¢ã¯ã»ã¹äžèœãšããŠããŒã¯ããŸããå®è¡ãããšãããã¯ãçºçããŸãã
fn factorial(n: u32) -> u32 { let mut product = 1; for i in 1..=n { product *= dbg!(i); } product } fn fizzbuzz(n: u32) -> u32 { todo!() } fn main() { let n = 4; println!("{n}! = {}", factorial(n)); }
ãã®ã»ã¯ã·ã§ã³ã®èŠç¹ã¯ããã¯ãã®äžè¬çãªå©äŸ¿æ§ãšããã®äœ¿çšæ¹æ³ã瀺ãããšã«ãããŸãããã¯ããšããŠå®çŸ©ãããŠããçç±ãšãå±éå ã¯ç¹ã«éèŠã§ã¯ãããŸããã
ãã¯ãã®å®çŸ©ã«ã€ããŠã¯ã³ãŒã¹ã§ã¯èª¬æããŸããããåŸã®ã»ã¯ã·ã§ã³ã§å°åºãã¯ãã®äœ¿çšã«ã€ããŠèª¬æããŸãã
æŒç¿: ã³ã©ããæ°å
The Collatz Sequence is defined as follows, for an arbitrary n1 greater than zero:
- If ni is 1, then the sequence terminates at ni.
- If ni is even, then ni+1 = ni / 2.
- If ni is odd, then ni+1 = 3 * ni + 1.
For example, beginning with n1 = 3:
- 3 is odd, so n2 = 3 * 3 + 1 = 10;
- 10 is even, so n3 = 10 / 2 = 5;
- 5 is odd, so n4 = 3 * 5 + 1 = 16;
- 16 is even, so n5 = 16 / 2 = 8;
- 8 is even, so n6 = 8 / 2 = 4;
- 4 is even, so n7 = 4 / 2 = 2;
- 2 is even, so n8 = 1; and
- æ°åã¯çµäºããŸãã
ä»»æã®åæå€ n
ã«å¯Ÿããã³ã©ããæ°åã®é·ããèšç®ããé¢æ°ãäœæããŸãã
/// `n` ããå§ãŸãã³ã©ããæ°åã®é·ãã決å®ã fn collatz_length(mut n: i32) -> u32 { todo!("ãããå®è£ ããŠãã ãã") } #[test] fn test_collatz_length() { assert_eq!(collatz_length(11), 15); } fn main() { println!("Length: {}", collatz_length(11)); }
解ç
/// `n` ããå§ãŸãã³ã©ããæ°åã®é·ãã決å®ã fn collatz_length(mut n: i32) -> u32 { let mut len = 1; while n > 1 { n = if n % 2 == 0 { n / 2 } else { 3 * n + 1 }; len += 1; } len } #[test] fn test_collatz_length() { assert_eq!(collatz_length(11), 15); } fn main() { println!("Length: {}", collatz_length(11)); }
ãããã
Including 10 minute breaks, this session should take about 2 hours and 35 minutes. It contains:
Segment | Duration |
---|---|
ã¿ãã«ãšé å | 35 minutes |
åç § | 55 minutes |
ãŠãŒã¶ãŒå®çŸ©å | 50 minutes |
ã¿ãã«ãšé å
This segment should take about 35 minutes. It contains:
Slide | Duration |
---|---|
é å | 5 minutes |
ã¿ãã« | 5 minutes |
é åã®ã€ãã¬ãŒã | 3 minutes |
ãã¿ãŒã³ãšãã¹ãã©ã¯ã | 5 minutes |
æŒç¿: ãã¹ããããé å | 15 minutes |
é å
fn main() { let mut a: [i8; 10] = [42; 10]; a[5] = 0; println!("a: {a:?}"); }
-
é åå
[T; N]
ã«ã¯ãåãåT
ã®N
ïŒã³ã³ãã€ã«æå®æ°ïŒåã®èŠçŽ ãä¿æãããŸãããªããé åã®é·ãã¯ãã®åã®äžéšåã§ããã€ãŸãã[u8; 3]
ãš[u8; 4]
㯠2 ã€ã®ç°ãªãåãšã¿ãªãããŸããã¹ã©ã€ã¹ïŒãµã€ãºãå®è¡æã«æ±ºå®ãããïŒã«ã€ããŠã¯åŸã§èª¬æããŸãã -
å¢çå€ã®é åèŠçŽ ã«ã¢ã¯ã»ã¹ããŠã¿ãŠãã ãããé åã¢ã¯ã»ã¹ã¯å®è¡æã«ãã§ãã¯ãããŸããRust ã§ã¯éåžžããããã®ãã§ãã¯ãæé©åã«ããé€å»ã§ããŸãïŒUnsafe Rust ã䜿çšããããšã§åé¿ã§ããŸãïŒã
-
ãªãã©ã«ã䜿çšããŠé åã«å€ãä»£å ¥ããããšãã§ããŸãã
-
println!
ãã¯ãã¯ã?
圢åŒã®ãã©ã¡ãŒã¿ã䜿çšããŠãããã°å®è£ ãèŠæ±ããŸããã€ãŸãã{}
ã¯ããã©ã«ãã®åºåãã{:?}
ã¯ãããã°åºåãæäŸããŸããæŽæ°ãæååãªã©ã®åã¯ããã©ã«ãã®åºåãå®è£ ããŸãããé åã¯ãããã°åºåã®ã¿ãå®è£ ããŸãããã®ãããããã§ã¯ãããã°åºåã䜿çšããå¿ èŠããããŸãã -
#
ãè¿œå ãããšïŒäŸ:{a:#?}
ïŒãèªã¿ããããããªã㣠ããªã³ãã圢åŒãåŒã³åºãããŸãã
ã¿ãã«
fn main() { let t: (i8, bool) = (7, true); println!("t.0: {}", t.0); println!("t.1: {}", t.1); }
-
é åãšåæ§ã«ãã¿ãã«ã®é·ãã¯åºå®ãããŠããŸãã
-
ã¿ãã«ã¯ãç°ãªãåã®å€ãè€ååã«ã°ã«ãŒãåããŸãã
-
ã¿ãã«ã®ãã£ãŒã«ãã«ã¯ãããªãªããšå€ã®ã€ã³ããã¯ã¹ïŒäŸ:
t.0
ãt.1
ïŒã§ã¢ã¯ã»ã¹ã§ããŸãã -
The empty tuple
()
is referred to as the "unit type" and signifies absence of a return value, akin tovoid
in other languages.
é åã®ã€ãã¬ãŒã
for ã¹ããŒãã¡ã³ãã§ã¯ãé åã®å埩åŠçããµããŒããããŠããŸãïŒã¿ãã«ã¯ãµããŒããããŠããŸããïŒã
fn main() { let primes = [2, 3, 5, 7, 11, 13, 17, 19]; for prime in primes { for i in 2..prime { assert_ne!(prime % i, 0); } } }
ãã®æ©èœã¯ IntoIterator
ãã¬ã€ãã䜿çšããŸãããããã¯ãŸã 説æããŠããŸããã
The assert_ne!
macro is new here. There are also assert_eq!
and assert!
macros. These are always checked, while debug-only variants like debug_assert!
compile to nothing in release builds.
ãã¿ãŒã³ãšãã¹ãã©ã¯ã
When working with tuples and other structured values it's common to want to extract the inner values into local variables. This can be done manually by directly accessing the inner values:
fn print_tuple(tuple: (i32, i32)) { let left = tuple.0; let right = tuple.1; println!("left: {left}, right: {right}"); }
However, Rust also supports using pattern matching to destructure a larger value into its constituent parts:
fn print_tuple(tuple: (i32, i32)) { let (left, right) = tuple; println!("left: {left}, right: {right}"); }
- The patterns used here are "irrefutable", meaning that the compiler can statically verify that the value on the right of
=
has the same structure as the pattern. - A variable name is an irrefutable pattern that always matches any value, hence why we can also use
let
to declare a single variable. - Rust also supports using patterns in conditionals, allowing for equality comparison and destructuring to happen at the same time. This form of pattern matching will be discussed in more detail later.
- Edit the examples above to show the compiler error when the pattern doesn't match the value being matched on.
æŒç¿: ãã¹ããããé å
é åã«ã¯ä»ã®é åãå«ããããšãã§ããŸãã
#![allow(unused)] fn main() { let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; }
What is the type of this variable?
äžèšã®ãããªé
åã䜿çšããŠãè¡åã転眮ïŒè¡ãåã«å€æïŒãã transpose
é¢æ°ãèšè¿°ããŸãã
Copy the code below to https://play.rust-lang.org/ and implement the function. This function only operates on 3x3 matrices.
// TODO: å®è£ ãå®äºãããããããåé€ããŸãã #![allow(unused_variables, dead_code)] fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] { unimplemented!() } #[test] fn test_transpose() { let matrix = [ [101, 102, 103], // [201, 202, 203], [301, 302, 303], ]; let transposed = transpose(matrix); assert_eq!( transposed, [ [101, 201, 301], // [102, 202, 302], [103, 203, 303], ] ); } fn main() { let matrix = [ [101, 102, 103], // <-- ãã®ã³ã¡ã³ãã«ãã rustfmt ã§æ¹è¡ãè¿œå [201, 202, 203], [301, 302, 303], ]; println!("matrix: {:#?}", matrix); let transposed = transpose(matrix); println!("transposed: {:#?}", transposed); }
解ç
fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] { let mut result = [[0; 3]; 3]; for i in 0..3 { for j in 0..3 { result[j][i] = matrix[i][j]; } } result } #[test] fn test_transpose() { let matrix = [ [101, 102, 103], // [201, 202, 203], [301, 302, 303], ]; let transposed = transpose(matrix); assert_eq!( transposed, [ [101, 201, 301], // [102, 202, 302], [103, 203, 303], ] ); } fn main() { let matrix = [ [101, 102, 103], // <-- ãã®ã³ã¡ã³ãã«ãã rustfmt ã§æ¹è¡ãè¿œå [201, 202, 203], [301, 302, 303], ]; println!("matrix: {:#?}", matrix); let transposed = transpose(matrix); println!("transposed: {:#?}", transposed); }
åç §
This segment should take about 55 minutes. It contains:
Slide | Duration |
---|---|
å ±æåç § | 10 minutes |
æä»åç § | 10 minutes |
Slices | 10 minutes |
æåå | 10 minutes |
æŒç¿: ãžãªã¡ã㪠| 15 minutes |
å ±æåç §
A reference provides a way to access another value without taking ownership of the value, and is also called "borrowing". Shared references are read-only, and the referenced data cannot change.
fn main() { let a = 'A'; let b = 'B'; let mut r: &char = &a; println!("r: {}", *r); r = &b; println!("r: {}", *r); }
å T
ãžã®å
񑆇
§ã®å㯠&T
ã§ããåç
§å€ã¯ &
æŒç®åã§äœæãããŸãã*
æŒç®åã¯åç
§ããéåç
§ããããã®å€ãçæããŸãã
Rust ã¯ãã³ã°ãªã³ã°åç §ãéçã«çŠæ¢ããŸãã
fn x_axis(x: &i32) -> &(i32, i32) { let point = (*x, 0); return &point; }
-
References can never be null in Rust, so null checking is not necessary.
-
åç §ãšã¯ãåç §ããå€ããåçšãããããšã ãšèšãããŠããŸãããããã¯ãã€ã³ã¿ã«æ £ããŠããªãåè¬è ã«ãšã£ãŠç解ãããã説æã§ããã³ãŒãã§ã¯åç §ã䜿çšããŠå€ã«ã¢ã¯ã»ã¹ã§ããŸããããã®å€ã¯å ã®å€æ°ã«ãã£ãŠãææãããããŸãŸãšãªããŸããææã«ã€ããŠã¯ãã³ãŒã¹ã® 3 æ¥ç®ã§è©³ãã説æããŸãã
-
åç §ã¯ãã€ã³ã¿ãšããŠå®è£ ãããŸããäž»ãªå©ç¹ã¯ãåç §å ãããã¯ããã«å°ããã§ããããšã§ããC ãŸã㯠C++ ã«ç²ŸéããŠããåè¬è ã¯ãåç §ããã€ã³ã¿ãšããŠèªèã§ããŸãããã®ã³ãŒã¹ã®åŸåã§ãæªå å·¥ãã€ã³ã¿ã®äœ¿çšã«ããã¡ã¢ãªå®å šæ§ã®ãã°ã Rust ã§é²æ¢ããæ¹æ³ã«ã€ããŠèª¬æããŸãã
-
Rust ã¯åç §ãèªåçã«äœæããªããããåžžã«
&
ãä»ããå¿ èŠããããŸãã -
Rust will auto-dereference in some cases, in particular when invoking methods (try
r.is_ascii()
). There is no need for an->
operator like in C++. -
ãã®äŸã§ã¯ã
r
ã¯å¯å€ã§ãããããåä»£å ¥ãå¯èœã§ãïŒr = &b
ïŒãããã«ããr
ãåãã€ã³ããããä»ã®å€ãåç §ããããã«ãªããŸããããã¯ãåç §ã«ä»£å ¥ãããšåç §å ã®å€ãå€æŽããã C++ ãšã¯ç°ãªããŸãã -
å ±æåç §ã§ã¯ãå€ãå¯å€ã§ãã£ãŠããåç §å ã®å€ã¯å€æŽã§ããŸããã
*r = 'X'
ãšæå®ããŠã¿ãŠãã ããã -
Rust ã¯ããã¹ãŠã®åç §ã®ã©ã€ãã¿ã€ã ã远跡ããŠãååãªåç¶æéã確ä¿ããŠããŸããå®å šãª Rust ã§ã¯ããã³ã°ãªã³ã°åç §ãçºçããããšã¯ãããŸããã
x_axis
ã¯point
ãžã®åç §ãè¿ããŸãããé¢æ°ãæ»ããšpoint
ã®å²ãåœãŠã解é€ããããããã³ã³ãã€ã«ãããŸããã -
åçšã«ã€ããŠã¯æææš©ã®ãšããã§è©³ãã説æããŸãã
æä»åç §
æä»åç
§ã¯å¯å€åç
§ãšãåŒã°ããåç
§å
ã®å€ãå€æŽã§ããŸããå㯠&mut T
ã§ãã
fn main() { let mut point = (1, 2); let x_coord = &mut point.0; *x_coord = 20; println!("point: {point:?}"); }
èŠç¹ïŒ
-
ãæä»ããšã¯ããã®åç §ã®ã¿ã䜿çšããŠå€ã«ã¢ã¯ã»ã¹ã§ããããšãæå³ããŸããä»ã®åç §ïŒå ±æãŸãã¯æä»ïŒãåæã«ååšããããšã¯ã§ãããæä»åç §ãååšããéã¯åç §å ã®å€ã«ã¢ã¯ã»ã¹ã§ããŸããã
x_coord
ãæå¹ãªç¶æ ã§&point.0
ãäœæããããpoint.0
ãå€æŽããŠã¿ãŠãã ããã -
let mut x_coord: &i32
ãšlet x_coord: &mut i32
ã®éãã«æ³šæããŠãã ãããåè ã¯ç°ãªãå€ã«ãã€ã³ãã§ããå ±æåç §ãè¡šãã®ã«å¯ŸããåŸè ã¯å¯å€ã®å€ãžã®æä»åç §ãè¡šããŸãã
Slices
ã¹ã©ã€ã¹ã¯ããã倧ããªã³ã¬ã¯ã·ã§ã³ã«å¯Ÿãããã¥ãŒãæäŸããŸãã
fn main() { let mut a: [i32; 6] = [10, 20, 30, 40, 50, 60]; println!("a: {a:?}"); let s: &[i32] = &a[2..4]; println!("s: {s:?}"); }
- ã¹ã©ã€ã¹ã¯ãã¹ã©ã€ã¹ãããåããããŒã¿ãåçšããŸãã
-
ã¹ã©ã€ã¹ãäœæããã«ã¯ã
a
ãåçšããéå§ã€ã³ããã¯ã¹ãšçµäºã€ã³ããã¯ã¹ãè§ãã£ãã§å²ãã§æå®ããŸãã -
ã¹ã©ã€ã¹ãã€ã³ããã¯ã¹ 0 ããå§ãŸãå ŽåãRust ã®ç¯å²æ§æã«ããéå§ã€ã³ããã¯ã¹ãçç¥ã§ããŸããã€ãŸãã
&a[0..a.len()]
ãš&a[..a.len()]
ã¯åãã§ãã -
æåŸã®ã€ã³ããã¯ã¹ã«ã€ããŠãåãããšãèšããã®ã§ã
&a[2..a.len()]
ãš&a[2..]
ã¯åãã§ãã -
é åå šäœã®ã¹ã©ã€ã¹ãç°¡åã«äœæããã«ã¯ã
&a[..]
ãšæžãããšãåºæ¥ãŸãã -
s
㯠i32 ã®ã¹ã©ã€ã¹ãžã®åç §ã§ããs
ã®åïŒ&[i32]
ïŒã«é åã®é·ããå«ãŸããªããªã£ãããšã«æ³šç®ããŠãã ãããããã«ãããããŸããŸãªãµã€ãºã®ã¹ã©ã€ã¹ã«å¯ŸããŠèšç®ãå®è¡ã§ããŸãã -
ã¹ã©ã€ã¹ã¯åžžã«å¥ã®ãªããžã§ã¯ãããåçšããŸãããã®äŸã§ã¯ã
a
ã¯å°ãªããšãã¹ã©ã€ã¹ãååšããéã¯ãåç¶ã ããŠããïŒã¹ã³ãŒãå ã«ãã)å¿ èŠããããŸãã
æåå
We can now understand the two string types in Rust:
&str
is a slice of UTF-8 encoded bytes, similar to&[u8]
.String
is an owned buffer of UTF-8 encoded bytes, similar toVec<T>
.
fn main() { let s1: &str = "World"; println!("s1: {s1}"); let mut s2: String = String::from("Hello "); println!("s2: {s2}"); s2.push_str(s1); println!("s2: {s2}"); let s3: &str = &s2[s2.len() - s1.len()..]; println!("s3: {s3}"); }
-
&str
introduces a string slice, which is an immutable reference to UTF-8 encoded string data stored in a block of memory. String literals ("Hello"
), are stored in the programâs binary. -
Rust's
String
type is a wrapper around a vector of bytes. As with aVec<T>
, it is owned. -
ä»ã®å€ãã®åãšåæ§ã«ã
String::from()
ã¯æååãªãã©ã«ããæååãäœæããŸããString::new()
ã¯æ°ãã空ã®æååãäœæããŸããpush()
ã¡ãœãããšpush_str()
ã¡ãœããã䜿çšããŠãããã«æååããŒã¿ãè¿œå ã§ããŸãã -
format!()
ãã¯ãã䜿çšãããšãåçãªå€ããæææååãç°¡åã«çæã§ããŸãããã㯠println!() ãšåã圢åŒæå®ãåãå ¥ããŸãã -
&
ã䜿çšããŠString
ãã&str
ã¹ã©ã€ã¹ãåçšããå¿ èŠã«å¿ããŠç¯å²ãéžæã§ããŸããæåå¢çã«æããããŠããªããã€ãç¯å²ãéžæãããšããã®åŒã§ãããã¯ãèµ·ãããŸããchars
ã€ãã¬ãŒã¿ã¯æååäœã§åŠçãããããæ£ããæåå¢çãååŸããããšããããšãããããã®ã€ãã¬ãŒã¿ã䜿çšããã»ããæãŸããã§ãã -
C++ ããã°ã©ããŒåãã®èª¬æïŒ
&str
ã¯åžžã«ã¡ã¢ãªäžã®æå¹ãªæååãæããŠãããããªC++ ã® std::string_view ãšèããããŸããRust ã®String
ã¯ãC++ ã®std::string
ãšããããåçã§ãïŒäž»ãªéãã¯ãUTF-8 ã§ãšã³ã³ãŒãããããã€ãã®ã¿ãå«ããããšãã§ããçãæååã«å¯Ÿããæé©åãè¡ãããªãããšã§ãïŒã -
ãã€ãæååãªãã©ã«ã䜿çšãããšã
&[u8]
å€ãçŽæ¥äœæã§ããŸããfn main() { println!("{:?}", b"abc"); println!("{:?}", &[97, 98, 99]); }
-
æªå å·¥ã®æååã䜿çšãããšããšã¹ã±ãŒããç¡å¹ã«ããŠ
&str
å€ãäœæã§ããŸãïŒr"\n" == "\\n"
ïŒãäºéåŒçšç¬Šãåã蟌ãã«ã¯ãåŒçšç¬Šã®äž¡åŽã«åéã®#
ã䜿çšããŸããfn main() { println!(r#"<a href="link.html">link</a>"#); println!("<a href=\"link.html\">link</a>"); }
æŒç¿: ãžãªã¡ããª
ããã§ã¯ãç¹ã [f64;3]
ãšããŠè¡šçŸãã 3 次å
ãžãªã¡ããªã®ãŠãŒãã£ãªãã£é¢æ°ãããã€ãäœæããŸããé¢æ°ã·ã°ããã£ã¯ä»»æã§æå®ããŠãã ããã
// 座æšã®äºä¹ãåèšããŠå¹³æ¹æ ¹ãåãã // ãã¯ã¿ãŒã®å€§ãããèšç®ããŸãã`sqrt()` ã¡ãœããã䜿çšããŠã`v.sqrt()` ãšåæ§ã« // å¹³æ¹æ ¹ãèšç®ããŸãã fn magnitude(...) -> f64 { todo!() } // 倧ãããèšç®ãããã¹ãŠã®åº§æšããã®å€§ããã§å²ãããšã§ // ãã¯ã¿ãŒãæ£èŠåããŸãã fn normalize(...) { todo!() } // 次㮠`main` ã䜿çšããŠåŠçããã¹ãããŸãã fn main() { println!("Magnitude of a unit vector: {}", magnitude(&[0.0, 1.0, 0.0])); let mut v = [1.0, 2.0, 9.0]; println!("Magnitude of {v:?}: {}", magnitude(&v)); normalize(&mut v); println!("Magnitude of {v:?} after normalization: {}", magnitude(&v)); }
解ç
/// æå®ããããã¯ã¿ãŒã®å€§ãããèšç®ããŸãã fn magnitude(vector: &[f64; 3]) -> f64 { let mut mag_squared = 0.0; for coord in vector { mag_squared += coord * coord; } mag_squared.sqrt() } /// åããå€ããã«ãã¯ã¿ãŒã®å€§ããã 1.0 ã«å€æŽããŸãã fn normalize(vector: &mut [f64; 3]) { let mag = magnitude(vector); for item in vector { *item /= mag; } } fn main() { println!("Magnitude of a unit vector: {}", magnitude(&[0.0, 1.0, 0.0])); let mut v = [1.0, 2.0, 9.0]; println!("Magnitude of {v:?}: {}", magnitude(&v)); normalize(&mut v); println!("Magnitude of {v:?} after normalization: {}", magnitude(&v)); }
ãŠãŒã¶ãŒå®çŸ©å
This segment should take about 50 minutes. It contains:
Slide | Duration |
---|---|
ååä»ãæ§é äœ | 10 minutes |
ã¿ãã«æ§é äœ | 10 minutes |
åæåïŒenumsïŒ | 5 minutes |
éç | 5 minutes |
åãšã€ãªã¢ã¹ | 2 minutes |
æŒç¿: ãšã¬ããŒã¿ãŒã§ã®ã€ãã³ã | 15 minutes |
ååä»ãæ§é äœ
C ã C++ ãšåæ§ã«ãRust ã¯ã«ã¹ã¿ã æ§é äœããµããŒãããŠããŸãã
struct Person { name: String, age: u8, } fn describe(person: &Person) { println!("{} is {} years old", person.name, person.age); } fn main() { let mut peter = Person { name: String::from("Peter"), age: 27 }; describe(&peter); peter.age = 28; describe(&peter); let name = String::from("Avery"); let age = 39; let avery = Person { name, age }; describe(&avery); let jackie = Person { name: String::from("Jackie"), ..avery }; describe(&jackie); }
ããŒãã€ã³ã:
- æ§é äœã¯ãC ã C++ ã«ãããŠãšåãããã«æ©èœããŸãã
- C++ ãšåæ§ã«ããŸã C ãšã¯ç°ãªããåãå®çŸ©ããã®ã« typedef ã¯å¿ èŠãããŸããã
- C++ ãšã¯ç°ãªããæ§é äœéã«ç¶æ¿ã¯ãããŸããã
- ããã§ãæ§é äœã«ã¯ããŸããŸãªåãããããšã説æããŸãããã
- ãµã€ãºããŒãã®æ§é äœïŒäŸ:
struct Foo;
ïŒã¯ãããåã«ãã¬ã€ããå®è£ ããŠãããã®ã®ãå€èªäœã«æ ŒçŽããããŒã¿ããªãå Žåã«äœ¿çšã§ããŸãã - 次ã®ã¹ã©ã€ãã§ã¯ããã£ãŒã«ãåãéèŠã§ãªãå Žåã«äœ¿çšãããã¿ãã«æ§é äœã玹ä»ããŸãã
- ãµã€ãºããŒãã®æ§é äœïŒäŸ:
- é©åãªååã®å€æ°ããã§ã«ããå Žåã¯ãçç¥åœ¢ã䜿çšããŠæ§é äœãäœæã§ããŸãã
- æ§æ
..avery
ã䜿çšãããšãæ瀺çã«ãã¹ãŠã®ãã£ãŒã«ããå ¥åããªããŠããå€ãæ§é äœã®ãã£ãŒã«ãã®å€§éšåãã³ããŒã§ããŸãããã®æ§æã¯ãåžžã«æåŸã®èŠçŽ ã«ããå¿ èŠããããŸãã
ã¿ãã«æ§é äœ
ãã£ãŒã«ãåãéèŠã§ãªãå Žåã¯ãã¿ãã«æ§é äœã䜿çšã§ããŸãã
struct Point(i32, i32); fn main() { let p = Point(17, 23); println!("({}, {})", p.0, p.1); }
ããã¯å€ãã®å Žåãåäžãã£ãŒã«ã ã©ãããŒïŒãã¥ãŒã¿ã€ããšåŒã°ããŸãïŒã«äœ¿çšãããŸãã
struct PoundsOfForce(f64); struct Newtons(f64); fn compute_thruster_force() -> PoundsOfForce { todo!("Ask a rocket scientist at NASA") } fn set_thruster_force(force: Newtons) { // ... } fn main() { let force = compute_thruster_force(); set_thruster_force(force); }
- ãã¥ãŒã¿ã€ãã¯ãããªããã£ãåã®å€ã«é¢ããè¿œå æ
å ±ããšã³ã³ãŒãããåªããæ¹æ³ã§ãã次ã«äŸã瀺ããŸãã
- æ°å€ã¯ããã€ãã®åäœã§æž¬å®ãããŸãïŒäžèšã®äŸã§ã¯
Newtons
ïŒã - ãã®å€ã¯äœææã«æ€èšŒã«åæ Œããããã
PhoneNumber(String)
ãŸãã¯OddNumber(u32)
ã䜿çšãããã³ã«åæ€èšŒããå¿ èŠã¯ãããŸããã
- æ°å€ã¯ããã€ãã®åäœã§æž¬å®ãããŸãïŒäžèšã®äŸã§ã¯
- ãã¥ãŒã¿ã€ãã® 1 ã€ã®ãã£ãŒã«ãã«ã¢ã¯ã»ã¹ããŠã
Newtons
åã«f64
ã®å€ãè¿œå ããæ¹æ³ã瀺ããŸãã- Rust ã§ã¯éåžžãäžæçãªããšïŒèªåã©ãã解é€ããæŽæ°ãšããŠã®ããŒã«å€ã®äœ¿çšãªã©ïŒã¯å¥œãŸããŸããã
- æŒç®åã®ãªãŒããŒããŒãã«ã€ããŠã¯ã3 æ¥ç®ïŒãžã§ããªã¯ã¹ïŒã§èª¬æããŸãã
- ãã®äŸã¯ãããŒãº ã¯ã©ã€ã¡ã€ã ãªãŒãã¿ãŒã®å€±æãåèã«ããŠããŸãã
åæåïŒenumsïŒ
enum
ããŒã¯ãŒãã䜿çšãããšãããã€ãã®ç°ãªãããªã¢ã³ããæã€åãäœæã§ããŸãã
#[derive(Debug)] enum Direction { Left, Right, } #[derive(Debug)] enum PlayerMove { Pass, // åçŽãªããªã¢ã³ã Run(Direction), // Tuple variant Teleport { x: u32, y: u32 }, // æ§é äœããªã¢ã³ã } fn main() { let player_move: PlayerMove = PlayerMove::Run(Direction::Left); println!("On this turn: {player_move:?}"); }
ããŒãã€ã³ã:
- åæåã䜿çšãããšã1 ã€ã®åã§äžé£ã®å€ãåéã§ããŸãã
Direction
ã¯ããªã¢ã³ããæã€åã§ããDirection
ã«ã¯ãDirection::Left
ãšDirection::Right
ã® 2 ã€ã®å€ããããŸããPlayerMove
ã¯ã3 ã€ã®ããªã¢ã³ããæã€åã§ããRust ã¯ãã€ããŒãã«å ããŠå€å¥åŒãæ ŒçŽããããšã§ãå®è¡æã«ã©ã®ããªã¢ã³ããPlayerMove
å€ã«å«ãŸããŠããããææ¡ã§ããããã«ããŸãã- ããã§æ§é äœãšåæåãæ¯èŒããããšãããããããŸãã
- ã©ã¡ãã§ãããã£ãŒã«ãã®ãªãã·ã³ãã«ãªããŒãžã§ã³ïŒåäœæ§é äœïŒããããŸããŸãªãã£ãŒã«ããããããŒãžã§ã³ïŒããªã¢ã³ã ãã€ããŒãïŒã䜿çšã§ããŸãã
- åå¥ã®æ§é äœã䜿çšããŠãåæåã®ããŸããŸãªããªã¢ã³ããå®è£ ããããšãã§ããŸããããã®å Žåããããããã¹ãŠåæåã§å®çŸ©ãããŠããå Žåãšåãåã«ã¯ãªããŸããã
- Rust ã¯å€å¥åŒãä¿åããããã«æå°éã®ã¹ããŒã¹ã䜿çšããŸãã
-
å¿ èŠã«å¿ããŠãå¿ èŠæå°éã®ãµã€ãºã®æŽæ°ãæ ŒçŽããŸãã
-
èš±å¯ãããããªã¢ã³ãå€ããã¹ãŠã®ããããã¿ãŒã³ãã«ããŒããŠããªãå Žåãç¡å¹ãªããããã¿ãŒã³ã䜿çšããŠå€å¥åŒããšã³ã³ãŒãããŸãïŒããããã®æé©åãïŒãããšãã°ã
Option<&u8>
ã«ã¯None
ããªã¢ã³ãã«å¯ŸããæŽæ°ãžã®ãã€ã³ã¿ãŸãã¯NULL
ãæ ŒçŽãããŸãã -
å¿ èŠã«å¿ããŠïŒããšãã° C ãšã®äºææ§ã確ä¿ããããã«ïŒå€å¥åŒãå¶åŸ¡ã§ããŸãã
#[repr(u32)] enum Bar { A, // 0 B = 10000, C, // 10001 } fn main() { println!("A: {}", Bar::A as u32); println!("B: {}", Bar::B as u32); println!("C: {}", Bar::C as u32); }
repr
ããªãå Žåã10001 㯠2 ãã€ãã«åãŸããããå€å¥åŒã®åã«ã¯ 2 ãã€ãã䜿çšãããŸãã
-
ãã®ä»
Rust ã«ã¯ãåæåãå ããã¹ããŒã¹ãå°ãªãããããã«äœ¿çšã§ããæé©åãããã€ããããŸãã
-
null ãã€ã³ã¿ã®æé©å: äžéšã®åã§ãRust ã¯
size_of::<T>()
ãsize_of::<Option<T>>()
ãšçããããšãä¿èšŒããŸãã以äžã®ãµã³ãã«ã³ãŒãã¯ããããåäœã®è¡šçŸãå®éã«ã©ã®ããã«ãªããã瀺ããŠããŸããã³ã³ãã€ã©ã¯ãã®è¡šçŸã«é¢ããŠä¿èšŒããªãã®ã§ãããã¯ãŸã£ããå®å šã§ã¯ãªãããšã«æ³šæããŠãã ããã
use std::mem::transmute; macro_rules! dbg_bits { ($e:expr, $bit_type:ty) => { println!("- {}: {:#x}", stringify!($e), transmute::<_, $bit_type>($e)); }; } fn main() { unsafe { println!("bool:"); dbg_bits!(false, u8); dbg_bits!(true, u8); println!("Option<bool>:"); dbg_bits!(None::<bool>, u8); dbg_bits!(Some(false), u8); dbg_bits!(Some(true), u8); println!("Option<Option<bool>>:"); dbg_bits!(Some(Some(false)), u8); dbg_bits!(Some(Some(true)), u8); dbg_bits!(Some(None::<bool>), u8); dbg_bits!(None::<Option<bool>>, u8); println!("Option<&i32>:"); dbg_bits!(None::<&i32>, usize); dbg_bits!(Some(&0i32), usize); } }
const
Constants are evaluated at compile time and their values are inlined wherever they are used:
const DIGEST_SIZE: usize = 3; const ZERO: Option<u8> = Some(42); fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] { let mut digest = [ZERO.unwrap_or(0); DIGEST_SIZE]; for (idx, &b) in text.as_bytes().iter().enumerate() { digest[idx % DIGEST_SIZE] = digest[idx % DIGEST_SIZE].wrapping_add(b); } digest } fn main() { let digest = compute_digest("Hello"); println!("digest: {digest:?}"); }
Rust RFC Book ã«ãããšãå®æ°å€æ°ã¯äœ¿çšæã«ã€ã³ã©ã€ã³åãããŸãã
ã³ã³ãã€ã«æã« const
å€ãçæããããã«åŒã³åºããã®ã¯ãconst
ãšããŒã¯ãããé¢æ°ã®ã¿ã§ãããã ããconst
é¢æ°ã¯å®è¡æã«åŒã³åºãããšãã§ããŸãã
- Mention that
const
behaves semantically similar to C++'sconstexpr
- å®è¡æã«è©äŸ¡ãããå®æ°ãå¿ èŠã«ãªãããšã¯ããŸããããŸããããéçå€æ°ã䜿çšããããã䟿å©ã§å®å šã§ãã
static
éçå€æ°ã¯ããã°ã©ã ã®å®è¡å šäœãéããŠåç¶ããããã移åããŸããã
static BANNER: &str = "Welcome to RustOS 3.14"; fn main() { println!("{BANNER}"); }
Rust RFC Book ã§èª¬æãããŠããããã«ãéçå€æ°ã¯äœ¿çšæã«ã€ã³ã©ã€ã³åããããå®éã®é¢é£ããã¡ã¢ãªäœçœ®ã«ååšããŸããããã¯å®å
šã§ãªãã³ãŒããåã蟌ã¿ã³ãŒãã«æçšã§ãããå€æ°ã¯ããã°ã©ã ã®å®è¡å
šäœãéããŠåç¶ããŸããã°ããŒãã« ã¹ã³ãŒãã®å€ã«ãªããžã§ã¯ã ID ãå¿
èŠãªãå Žåã¯ãäžè¬çã« const
ã䜿çšãããŸãã
static
is similar to mutable global variables in C++.static
ã¯ãªããžã§ã¯ã IDïŒã¡ã¢ãªå ã®ã¢ãã¬ã¹ïŒãšãå éšå¯å€æ§ãæã€åã«å¿ èŠãªç¶æ ïŒMutex<T>
ãªã©ïŒãæäŸããŸãã
ãã®ä»
static
å€æ°ã¯ã©ã®ã¹ã¬ããããã§ãã¢ã¯ã»ã¹ã§ãããããSync
ã§ããå¿
èŠããããŸããå
éšã®å¯å€æ§ã¯ãMutex
ãã¢ãããã¯ãªã©ã®æ¹æ³ã§å®çŸã§ããŸãã
ãã¯ã std::thread_local
ã䜿çšããŠãã¹ã¬ãã ããŒã«ã«ã®ããŒã¿ãäœæã§ããŸãã
åãšã€ãªã¢ã¹
åãšã€ãªã¢ã¹ã¯ãå¥ã®åã®ååãäœæããŸãããã® 2 ã€ã®åã¯åãæå³ã§äœ¿çšã§ããŸãã
enum CarryableConcreteItem { Left, Right, } type Item = CarryableConcreteItem; // ãšã€ãªã¢ã¹ã¯é·ããŠè€éãªåã«äœ¿çšãããšäŸ¿å©ã§ãã use std::cell::RefCell; use std::sync::{Arc, RwLock}; type PlayerInventory = RwLock<Vec<Arc<RefCell<Item>>>>;
C ããã°ã©ããŒã¯ãããã typedef
ãšåæ§ã®ãã®ãšèããã§ãããã
æŒç¿: ãšã¬ããŒã¿ãŒã§ã®ã€ãã³ã
ãšã¬ããŒã¿ãŒå¶åŸ¡ã·ã¹ãã ã§ã€ãã³ããè¡šãããŒã¿æ§é ãäœæããŸããããŸããŸãªã€ãã³ããæ§ç¯ããããã®åãšé¢æ°ãèªç±ã«å®çŸ©ããŠæ§ããŸããã#[derive(Debug)]
ã䜿çšããŠãåã {:?}
ã§ãã©ãŒãããã§ããããã«ããŸãã
ãã®æŒç¿ã«å¿
èŠãªã®ã¯ãmain
ããšã©ãŒãªãã§å®è¡ãããããã«ãããŒã¿æ§é ãäœæããŠå
¥åããããšã ãã§ãããã®ã³ãŒã¹ã®æ¬¡ã®ããŒãã§ã¯ããããã®æ§é ããããŒã¿ãååŸããæ¹æ³ã説æããŸãã
#[derive(Debug)] /// ã³ã³ãããŒã©ãåå¿ããå¿ èŠããããšã¬ããŒã¿ãŒ ã·ã¹ãã å ã®ã€ãã³ãã enum Event { // TODO: å¿ èŠãªããªã¢ã³ããè¿œå ãã } /// é転æ¹åã #[derive(Debug)] enum Direction { Up, Down, } /// ãããæå®ã®éã«å°çããã fn car_arrived(floor: i32) -> Event { todo!() } /// ããã®ãã¢ãéããã fn car_door_opened() -> Event { todo!() } /// ããã®ãã¢ãéãŸã£ãã fn car_door_closed() -> Event { todo!() } /// æå®ã®éã®ãšã¬ããŒã¿ãŒ ãããŒã§æ¹åãã¿ã³ãæŒãããã fn lobby_call_button_pressed(floor: i32, dir: Direction) -> Event { todo!() } /// ãšã¬ããŒã¿ãŒã®ããã®éæ°ãã¿ã³ãæŒãããã fn car_floor_button_pressed(floor: i32) -> Event { todo!() } fn main() { println!( "A ground floor passenger has pressed the up button: {:?}", lobby_call_button_pressed(0, Direction::Up) ); println!("The car has arrived on the ground floor: {:?}", car_arrived(0)); println!("The car door opened: {:?}", car_door_opened()); println!( "A passenger has pressed the 3rd floor button: {:?}", car_floor_button_pressed(3) ); println!("The car door closed: {:?}", car_door_closed()); println!("The car has arrived on the 3rd floor: {:?}", car_arrived(3)); }
解ç
#[derive(Debug)] /// ã³ã³ãããŒã©ãåå¿ããå¿ èŠããããšã¬ããŒã¿ãŒ ã·ã¹ãã å ã®ã€ãã³ãã enum Event { /// ãã¿ã³ãæŒãããã ButtonPressed(Button), /// è»äž¡ãæå®ã®éã«å°çããã CarArrived(Floor), /// ããã®ãã¢ãéããã CarDoorOpened, /// ããã®ãã¢ãéãŸã£ãã CarDoorClosed, } /// éã¯æŽæ°ãšããŠè¡šãããã type Floor = i32; /// é転æ¹åã #[derive(Debug)] enum Direction { Up, Down, } /// ãŠãŒã¶ãŒãã¢ã¯ã»ã¹ã§ãããã¿ã³ã #[derive(Debug)] enum Button { /// æå®ã®éã®ãšã¬ããŒã¿ãŒ ãããŒã«ãããã¿ã³ã LobbyCall(Direction, Floor), /// ããå ã®éæ°ãã¿ã³ã CarFloor(Floor), } /// ãããæå®ã®éã«å°çããã fn car_arrived(floor: i32) -> Event { Event::CarArrived(floor) } /// ããã®ãã¢ãéããã fn car_door_opened() -> Event { Event::CarDoorOpened } /// ããã®ãã¢ãéãŸã£ãã fn car_door_closed() -> Event { Event::CarDoorClosed } /// æå®ã®éã®ãšã¬ããŒã¿ãŒ ãããŒã§æ¹åãã¿ã³ãæŒãããã fn lobby_call_button_pressed(floor: i32, dir: Direction) -> Event { Event::ButtonPressed(Button::LobbyCall(dir, floor)) } /// ãšã¬ããŒã¿ãŒã®ããã®éæ°ãã¿ã³ãæŒãããã fn car_floor_button_pressed(floor: i32) -> Event { Event::ButtonPressed(Button::CarFloor(floor)) } fn main() { println!( "A ground floor passenger has pressed the up button: {:?}", lobby_call_button_pressed(0, Direction::Up) ); println!("The car has arrived on the ground floor: {:?}", car_arrived(0)); println!("The car door opened: {:?}", car_door_opened()); println!( "A passenger has pressed the 3rd floor button: {:?}", car_floor_button_pressed(3) ); println!("The car door closed: {:?}", car_door_closed()); println!("The car has arrived on the 3rd floor: {:?}", car_arrived(3)); }
2æ¥ç®ã®è¬åº§ãžãããã
Rust ã«ã€ããŠããªãå€ãã®ããšãåŠãã§ããŸããããä»æ¥ã¯ Rust ã®åã·ã¹ãã ã«çŠç¹ãåœãŠãŸãã
- ãã¿ãŒã³ ãããã³ã°: æ§é ããã®ããŒã¿ã®æœåºã
- ã¡ãœãã: é¢æ°ãšåã®é¢é£ä»ãã
- ãã¬ã€ã: è€æ°ã®åã§å ±æãããæåã
- ãžã§ããªã¯ã¹: ä»ã®åã§ã®åã®ãã©ã¡ãŒã¿åã
- æšæºã©ã€ãã©ãªã®åãšãã¬ã€ã: Rust ã®è±å¯ãªæšæºã©ã€ãã©ãªã®çŽ¹ä»ã
ã¹ã±ãžã¥ãŒã«
Including 10 minute breaks, this session should take about 2 hours and 10 minutes. It contains:
Segment | Duration |
---|---|
ãããã | 3 minutes |
ãã¿ãŒã³ãããã³ã° | 1 hour |
Methods and Traits | 50 minutes |
ãã¿ãŒã³ãããã³ã°
This segment should take about 1 hour. It contains:
Slide | Duration |
---|---|
Matching Values | 10 minutes |
æ§é äœã®ãã¹ãã©ã¯ã | 4 minutes |
åæåã®ãã¹ãã©ã¯ã | 4 minutes |
Letå¶åŸ¡ãã㌠| 10 minutes |
æŒç¿: åŒã®è©äŸ¡ | 30 minutes |
Matching Values
match
ããŒã¯ãŒãã䜿çšãããšã1 ã€ä»¥äžã®ãã¿ãŒã³ã«å¯ŸããŠå€ãç
§åã§ããŸããäžããé ã«ç
§åãè¡ãããæåã«äžèŽãããã¿ãŒã³ã®ã¿ãå®è¡ãããŸãã
C ã C++ ã® switch
ãšåæ§ã«ããã¿ãŒã³ã«ã¯åçŽãªå€ãæå®ã§ããŸãã
#[rustfmt::skip] fn main() { let input = 'x'; match input { 'q' => println!("Quitting"), 'a' | 's' | 'w' | 'd' => println!("Moving around"), '0'..='9' => println!("Number input"), key if key.is_lowercase() => println!("Lowercase: {key}"), _ => println!("Something else"), } }
The _
pattern is a wildcard pattern which matches any value. The expressions must be exhaustive, meaning that it covers every possibility, so _
is often used as the final catch-all case.
äžèŽãåŒãšããŠäœ¿çšã§ããŸããif
ãšåæ§ã«ãåãããã¢ãŒã ã¯åãåã«ããå¿
èŠããããŸããåã¯ããããã¯ã®æåŸã®åŒã§ãïŒååšããå ŽåïŒãäžèšã®äŸã§ã¯ãå㯠()
ã§ãã
ãã¿ãŒã³ã®å€æ°ïŒãã®äŸã§ã¯ key
ïŒã«ããããããã¢ãŒã å
ã§äœ¿çšã§ãããã€ã³ãã£ã³ã°ãäœæãããŸãã
ãããã¬ãŒãã«ãããæ¡ä»¶ã true ã®å Žåã«ã®ã¿ã¢ãŒã ãäžèŽããŸãã
ããŒãã€ã³ã:
-
ç¹å®ã®æåããã¿ãŒã³ã§ã©ã®ããã«äœ¿çšããããã説æããŸãã
|
ãor
ãšããŠæå®ãã..
ã¯å¿ èŠã«å¿ããŠå±éã§ãã1..=5
㯠5 ãå«ãç¯å²ãè¡šã_
ã¯ã¯ã€ã«ãã«ãŒããè¡šã
-
ãã¿ãŒã³ã®ã¿ã§ã¯è¡šçŸã§ããªãè€éãªæŠå¿µãç°¡æœã«è¡šçŸãããå Žåãç¬ç«ããæ§ææ©èœã§ãããããã¬ãŒãã¯éèŠãã€å¿ èŠã§ãã
-
ãããã¬ãŒãã¯ããããã¢ãŒã å ã®åå¥ã®
if
åŒãšã¯ç°ãªããŸããåå²ãããã¯å ïŒ=>
ã®åŸïŒã®if
åŒã¯ããããã¢ãŒã ãéžæãããåŸã«å®è¡ãããŸãããã®ãããã¯å ã§if
æ¡ä»¶ãæºããããªãã£ãå Žåãå ã®match
åŒã®ä»ã®ã¢ãŒã ã¯èæ ®ãããŸããã -
ã¬ãŒãã§å®çŸ©ãããæ¡ä»¶ã¯ã
|
ãä»ãããã¿ãŒã³å ã®ãã¹ãŠã®åŒã«é©çšãããŸãã
More To Explore
-
Another piece of pattern syntax you can show students is the
@
syntax which binds a part of a pattern to a variable. For example:#![allow(unused)] fn main() { let opt = Some(123); match opt { outer @ Some(inner) => { println!("outer: {outer:?}, inner: {inner}"); } None => {} } }
In this example
inner
has the value 123 which it pulled from theOption
via destructuring,outer
captures the entireSome(inner)
expression, so it contains the fullOption::Some(123)
. This is rarely used but can be useful in more complex patterns.
æ§é äœïŒstructsïŒ
Like tuples, Struct can also be destructured by matching:
struct Foo { x: (u32, u32), y: u32, } #[rustfmt::skip] fn main() { let foo = Foo { x: (1, 2), y: 3 }; match foo { Foo { x: (1, b), y } => println!("x.0 = 1, b = {b}, y = {y}"), Foo { y: 2, x: i } => println!("y = 2, x = {i:?}"), Foo { y, .. } => println!("y = {y}, other fields were ignored"), } }
foo
ã®ãªãã©ã«å€ãä»ã®ãã¿ãŒã³ãšäžèŽããããã«å€æŽããŸããFoo
ã«æ°ãããã£ãŒã«ããè¿œå ããå¿ èŠã«å¿ããŠãã¿ãŒã³ã«å€æŽãå ããŸãã- ãã£ããã£ãšå®æ°åŒãåºå¥ãã¥ããå ŽåããããŸãã2 ã€ç®ã®ã¢ãŒã ã®
2
ãå€æ°ã«å€æŽããŠã¿ãŠãããŸãæ©èœããªãããšã確èªããŸãããããconst
ã«å€æŽããŠãåã³åäœããããšã確èªããŸãã
åæåïŒenumsïŒ
Like tuples, enums can also be destructured by matching:
ãã¿ãŒã³ã¯ãå€æ°ãå€ã®äžéšã«ãã€ã³ãããããã«ã䜿çšã§ããŸãã以äžã®ããã«ããŠãåã®æ§é ã調ã¹ãããšãã§ããŸããåçŽãª enum
ããå§ããŸãããã
enum Result { Ok(i32), Err(String), } fn divide_in_two(n: i32) -> Result { if n % 2 == 0 { Result::Ok(n / 2) } else { Result::Err(format!("cannot divide {n} into two equal parts")) } } fn main() { let n = 100; match divide_in_two(n) { Result::Ok(half) => println!("{n} divided in two is {half}"), Result::Err(msg) => println!("sorry, an error happened: {msg}"), } }
ããã§ã¯ãã¢ãŒã ïŒarm, ãã¿ãŒã³ã䞊ã¹ããã®ïŒã䜿çšã㊠Result
å€ã®å解ãè¡ã£ãŠããŸããæåã®ã¢ãŒã ã§ã¯ãhalf
㯠Ok
ããªã¢ã³ãå
ã®å€ã«ãã€ã³ããããŸãã2 ã€ç®ã®ã¢ãŒã ã§ã¯ msg
ããšã©ãŒ ã¡ãã»ãŒãžã«ãã€ã³ããããŸãã
if
/else
åŒã¯ãåŸã§match
ã§ã¢ã³ããã¯ãããåæåãè¿ããŠããŸãã- åæåã®å®çŸ©ã« 3 ã€ç®ã®ããªã¢ã³ãïŒåæåã®èŠçŽ ã®ããšïŒãè¿œå ããã³ãŒãå®è¡æã«ãšã©ãŒã衚瀺ããŠã¿ãŸããããã³ãŒããç¶²çŸ ãããŠããªãç®æã瀺ããã³ã³ãã€ã©ãã©ã®ããã«ãã³ããæäŸããããšããŠãããã説æããŸãã
- åæåããªã¢ã³ãã®å€ã«ã¯ããã¿ãŒã³ãäžèŽããå Žåã«ã®ã¿ã¢ã¯ã»ã¹ã§ããŸãã
- æ€çŽ¢ãç¶²çŸ çã§ãªãå Žåã«ã©ããªããã瀺ããŸãããã¹ãŠã®ã±ãŒã¹ãåŠçãããã¿ã€ãã³ã°ã確èªããããšã§ãRust ã³ã³ãã€ã©ã®å©ç¹ã匷調ããŸãã
Letå¶åŸ¡ãããŒ
Rust ã«ã¯ãä»ã®èšèªãšã¯ç°ãªãå¶åŸ¡ãããŒæ§é ãããã€ããããŸãããããã¯ãã¿ãŒã³ ãããã³ã°ã«äœ¿çšãããŸãã
if let
åŒlet else
åŒwhile let
åŒ
if let
åŒ
if let
åŒ ã䜿çšãããšãå€ããã¿ãŒã³ã«äžèŽãããã©ããã«å¿ããŠç°ãªãã³ãŒããå®è¡ã§ããŸãã
use std::time::Duration; fn sleep_for(secs: f32) { if let Ok(duration) = Duration::try_from_secs_f32(secs) { std::thread::sleep(duration); println!("slept for {duration:?}"); } } fn main() { sleep_for(-10.0); sleep_for(0.8); }
let else
åŒ
ãã¿ãŒã³ããããããŠé¢æ°ããæ»ããšããäžè¬çãªã±ãŒã¹ã§ã¯ãlet else
ã䜿çšããŸãããelseãã±ãŒã¹ã¯çºæ£ããå¿
èŠããããŸãïŒreturn
ãbreak
ããããã¯ãªã©ããããã¯ããæãããã®ä»¥å€ã®ãã¹ãŠïŒã
fn hex_or_die_trying(maybe_string: Option<String>) -> Result<u32, String> { if let Some(s) = maybe_string { if let Some(first_byte_char) = s.chars().next() { if let Some(digit) = first_byte_char.to_digit(16) { Ok(digit) } else { return Err(String::from("not a hex digit")); } } else { return Err(String::from("got empty string")); } } else { return Err(String::from("got None")); } } fn main() { println!("result: {:?}", hex_or_die_trying(Some(String::from("foo")))); }
if let
ã«äŒŒã while let
掟çç©ããããŸããããã¯ããã¿ãŒã³ã«ç
§ãããŠå€ããã¹ãããŸãã
fn main() { let mut name = String::from("Comprehensive Rust ðŠ"); while let Some(c) = name.pop() { println!("character: {c}"); } // (There are more efficient ways to reverse a string!) }
ãã㧠String::pop
ã¯ãæååã空ã«ãªããŸã§ Some(c)
ãè¿ãããã®åŸ None
ãè¿ããŸããwhile let
ã䜿çšãããšããã¹ãŠã®ã¢ã€ãã ã«å¯ŸããŠå埩åŠçãç¶è¡ã§ããŸãã
if-let
match
ãšã¯ç°ãªããif let
ã§ã¯ãã¹ãŠã®åå²ãç¶²çŸ ããå¿ èŠã¯ãªããããmatch
ãããç°¡æœã«ãªããŸãã- äžè¬çãªäœ¿çšæ¹æ³ã¯ã
Option
ãæäœãããšãã«Some
å€ãåŠçããããšã§ãã match
ãšã¯ç°ãªããif let
ã¯ãã¿ãŒã³ ãããã³ã°ã§ã¬ãŒãç¯ããµããŒãããŠããŸããã
let-else
次ã«ç€ºãããã«ãif let
ã¯ç©ã¿éãªã£ãŠããŸãããšããããŸããlet-else
ã®æ§æã¯ããã®ãã¹ããããã³ãŒããå¹³åŠã«ããå©ããšãªããŸããèªã¿ã¥ããããŒãžã§ã³ãåè¬è
åãã«æžãçŽããŠãåè¬è
ãå€åã確èªã§ããããã«ããŸãã
æžãæããããŒãžã§ã³ã¯æ¬¡ã®ãšããã§ãã
#![allow(unused)] fn main() { fn hex_or_die_trying(maybe_string: Option<String>) -> Result<u32, String> { let Some(s) = maybe_string else { return Err(String::from("got None")); }; let Some(first_byte_char) = s.chars().next() else { return Err(String::from("got empty string")); }; let Some(digit) = first_byte_char.to_digit(16) else { return Err(String::from("not a hex digit")); }; return Ok(digit); } }
while-let
- å€ããã¿ãŒã³ã«äžèŽããéãã
while let
ã«ãŒããç¹°ãè¿ãããããšã説æããŸãã name.pop()
ã§unwrapããå€ããªãå Žåã«äžæãã if ã¹ããŒãã¡ã³ãã䜿çšããŠãwhile let
ã«ãŒããç¡éã«ãŒãã«æžãæããããšãã§ããŸããwhile let
ã¯ãäžèšã®ã·ããªãªã®ç³è¡£æ§æãšããŠäœ¿çšã§ããŸãã
æŒç¿: åŒã®è©äŸ¡
æŒç®åŒçšã®ç°¡åãªååž°ãšããªã¥ãšãŒã¿ãäœæããŠã¿ãŸãããã
An example of a small arithmetic expression could be 10 + 20
, which evaluates to 30
. We can represent the expression as a tree:
A bigger and more complex expression would be (10 * 9) + ((3 - 4) * 5)
, which evaluate to 85
. We represent this as a much bigger tree:
In code, we will represent the tree with two types:
#![allow(unused)] fn main() { /// 2 ã€ã®ãµãåŒã«å¯ŸããŠå®è¡ããæŒç®ã #[derive(Debug)] enum Operation { Add, Sub, Mul, Div, } /// ããªãŒåœ¢åŒã®åŒã #[derive(Debug)] enum Expression { /// 2 ã€ã®ãµãåŒã«å¯ŸããæŒç®ã Op { op: Operation, left: Box<Expression>, right: Box<Expression> }, /// ãªãã©ã«å€ Value(i64), } }
ããã§ã® Box
åã¯ã¹ããŒã ãã€ã³ã¿ã§ãã詳现ã¯ãã®è¬åº§ã§åŸã»ã©èª¬æããŸãããã¹ãã§èŠãããããã«ãåŒã¯ Box::new
ã§ãããã¯ã¹åãã§ããŸããããã¯ã¹åãããåŒãè©äŸ¡ããã«ã¯ãéåç
§æŒç®åïŒ*
ïŒã䜿çšããŠãããã¯ã¹å解é€ãããŸãïŒeval(*boxed_expr)
ïŒã
äžéšã®åŒã¯è©äŸ¡ã§ããããšã©ãŒãè¿ãããŸããæšæºã® Result<Value, String>
åã¯ãæåããå€ïŒOk(Value)
ïŒãŸãã¯ãšã©ãŒïŒErr(String)
ïŒã®ãããããè¡šãåæåã§ãããã®åã«ã€ããŠã¯ãåŸã»ã©è©³ãã説æããŸãã
ã³ãŒããã³ããŒã㊠Rust ãã¬ã€ã°ã©ãŠã³ãã«è²Œãä»ããeval
ã®å®è£
ãéå§ããŸããå®æãããšããªã¥ãšãŒã¿ã¯ãã¹ãã«åæ Œããå¿
èŠããããŸããtodo!()
ã䜿çšããŠããã¹ãã 1 ã€ãã€å®æœããããšãããããããŸãã#[ignore]
ã䜿çšããŠããã¹ããäžæçã«ã¹ãããããããšãã§ããŸãã
#[test]
#[ignore]
fn test_value() { .. }
#![allow(unused)] fn main() { /// 2 ã€ã®ãµãåŒã«å¯ŸããŠå®è¡ããæŒç®ã #[derive(Debug)] enum Operation { Add, Sub, Mul, Div, } /// ããªãŒåœ¢åŒã®åŒã #[derive(Debug)] enum Expression { /// 2 ã€ã®ãµãåŒã«å¯ŸããæŒç®ã Op { op: Operation, left: Box<Expression>, right: Box<Expression> }, /// ãªãã©ã«å€ Value(i64), } fn eval(e: Expression) -> Result<i64, String> { todo!() } #[test] fn test_value() { assert_eq!(eval(Expression::Value(19)), Ok(19)); } #[test] fn test_sum() { assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(Expression::Value(10)), right: Box::new(Expression::Value(20)), }), Ok(30) ); } #[test] fn test_recursion() { let term1 = Expression::Op { op: Operation::Mul, left: Box::new(Expression::Value(10)), right: Box::new(Expression::Value(9)), }; let term2 = Expression::Op { op: Operation::Mul, left: Box::new(Expression::Op { op: Operation::Sub, left: Box::new(Expression::Value(3)), right: Box::new(Expression::Value(4)), }), right: Box::new(Expression::Value(5)), }; assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(term1), right: Box::new(term2), }), Ok(85) ); } #[test] fn test_zeros() { assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(Expression::Value(0)), right: Box::new(Expression::Value(0)) }), Ok(0) ); assert_eq!( eval(Expression::Op { op: Operation::Mul, left: Box::new(Expression::Value(0)), right: Box::new(Expression::Value(0)) }), Ok(0) ); assert_eq!( eval(Expression::Op { op: Operation::Sub, left: Box::new(Expression::Value(0)), right: Box::new(Expression::Value(0)) }), Ok(0) ); } #[test] fn test_error() { assert_eq!( eval(Expression::Op { op: Operation::Div, left: Box::new(Expression::Value(99)), right: Box::new(Expression::Value(0)), }), Err(String::from("division by zero")) ); } }
解ç
/// 2 ã€ã®ãµãåŒã«å¯ŸããŠå®è¡ããæŒç®ã #[derive(Debug)] enum Operation { Add, Sub, Mul, Div, } /// ããªãŒåœ¢åŒã®åŒã #[derive(Debug)] enum Expression { /// 2 ã€ã®ãµãåŒã«å¯ŸããæŒç®ã Op { op: Operation, left: Box<Expression>, right: Box<Expression> }, /// ãªãã©ã«å€ Value(i64), } fn eval(e: Expression) -> Result<i64, String> { match e { Expression::Op { op, left, right } => { let left = match eval(*left) { Ok(v) => v, Err(e) => return Err(e), }; let right = match eval(*right) { Ok(v) => v, Err(e) => return Err(e), }; Ok(match op { Operation::Add => left + right, Operation::Sub => left - right, Operation::Mul => left * right, Operation::Div => { if right == 0 { return Err(String::from("division by zero")); } else { left / right } } }) } Expression::Value(v) => Ok(v), } } #[test] fn test_value() { assert_eq!(eval(Expression::Value(19)), Ok(19)); } #[test] fn test_sum() { assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(Expression::Value(10)), right: Box::new(Expression::Value(20)), }), Ok(30) ); } #[test] fn test_recursion() { let term1 = Expression::Op { op: Operation::Mul, left: Box::new(Expression::Value(10)), right: Box::new(Expression::Value(9)), }; let term2 = Expression::Op { op: Operation::Mul, left: Box::new(Expression::Op { op: Operation::Sub, left: Box::new(Expression::Value(3)), right: Box::new(Expression::Value(4)), }), right: Box::new(Expression::Value(5)), }; assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(term1), right: Box::new(term2), }), Ok(85) ); } #[test] fn test_zeros() { assert_eq!( eval(Expression::Op { op: Operation::Add, left: Box::new(Expression::Value(0)), right: Box::new(Expression::Value(0)) }), Ok(0) ); assert_eq!( eval(Expression::Op { op: Operation::Mul, left: Box::new(Expression::Value(0)), right: Box::new(Expression::Value(0)) }), Ok(0) ); assert_eq!( eval(Expression::Op { op: Operation::Sub, left: Box::new(Expression::Value(0)), right: Box::new(Expression::Value(0)) }), Ok(0) ); } #[test] fn test_error() { assert_eq!( eval(Expression::Op { op: Operation::Div, left: Box::new(Expression::Value(99)), right: Box::new(Expression::Value(0)), }), Err(String::from("division by zero")) ); } fn main() { let expr = Expression::Op { op: Operation::Sub, left: Box::new(Expression::Value(20)), right: Box::new(Expression::Value(10)), }; println!("expr: {expr:?}"); println!("result: {:?}", eval(expr)); }
Methods and Traits
This segment should take about 50 minutes. It contains:
Slide | Duration |
---|---|
ã¡ãœãã | 10 minutes |
ãã¬ã€ãïŒtraitïŒ | 15 minutes |
å°åº | 3 minutes |
æŒç¿: ãžã§ããªãã¯ãªãã¬ãŒ | 20 minutes |
ã¡ãœãã
Rust ã䜿çšãããšãé¢æ°ãæ°ããåã«é¢é£ä»ããããšãã§ããŸãããã㯠impl
ãããã¯ã§å®è¡ããŸãã
#[derive(Debug)] struct Race { name: String, laps: Vec<i32>, } impl Race { // ã¬ã·ãŒããªããéçã¡ãœãã fn new(name: &str) -> Self { Self { name: String::from(name), laps: Vec::new() } } // èªèº«ã«å¯Ÿããæä»çãªèªã¿åã / æžã蟌ã¿åçšã¢ã¯ã»ã¹ fn add_lap(&mut self, lap: i32) { self.laps.push(lap); } // èªèº«ã«å¯Ÿããå ±æããã³èªã¿åãå°çšã®åçšã¢ã¯ã»ã¹ fn print_laps(&self) { println!("Recorded {} laps for {}:", self.laps.len(), self.name); for (idx, lap) in self.laps.iter().enumerate() { println!("Lap {idx}: {lap} sec"); } } // Exclusive ownership of self (covered later) fn finish(self) { let total: i32 = self.laps.iter().sum(); println!("Race {} is finished, total lap time: {}", self.name, total); } } fn main() { let mut race = Race::new("Monaco Grand Prix"); race.add_lap(70); race.add_lap(68); race.print_laps(); race.add_lap(71); race.print_laps(); race.finish(); // race.add_lap(42); }
self
åŒæ°ã¯ããã¬ã·ãŒãããã€ãŸãã¡ãœãããæäœãããªããžã§ã¯ããæå®ããŸããã¡ãœããã®äžè¬çãªã¬ã·ãŒãã¯æ¬¡ã®ãšããã§ãã
&self
: å ±æã®äžå€åç §ã䜿çšããŠãåŒã³åºãå ãããªããžã§ã¯ããåçšããŸãããã®ãªããžã§ã¯ãã¯åŸã§åã³äœ¿çšã§ããŸãã&mut self
: äžæã®å¯å€åç §ã䜿çšããŠãåŒã³åºãå ãããªããžã§ã¯ããåçšããŸãããã®ãªããžã§ã¯ãã¯åŸã§åã³äœ¿çšã§ããŸããself
: ãªããžã§ã¯ãã®æææš©ãååŸããåŒã³åºãå ããé ãããŸããã¡ãœããããªããžã§ã¯ãã®ææè ã«ãªããŸããæææš©ãæ瀺çã«éä¿¡ãããªãéããã¡ãœãããæ»ããšããªããžã§ã¯ãã¯ç Žæ£ïŒãã¢ãã±ãŒãïŒãããŸããå®å šãªæææš©ã¯ãå¿ ãããå¯å€æ§ãæå³ããããã§ã¯ãããŸãããmut self
: äžèšãšåãã§ãããã¡ãœããã¯ãªããžã§ã¯ããå€æŽã§ããŸãã- ã¬ã·ãŒããªã: æ§é äœã®éçã¡ãœããã«ãªããŸããéåžžã¯ã
new
ãšåŒã°ããã³ã³ã¹ãã©ã¯ã¿ãäœæããããã«äœ¿çšãããŸãã
ããŒãã€ã³ã:
- ã¡ãœãããé¢æ°ãšæ¯èŒããŠçŽ¹ä»ãããšããã§ãããã
- ã¡ãœããã¯åïŒæ§é äœãåæåãªã©ïŒã®ã€ã³ã¹ã¿ã³ã¹ã§åŒã³åºãããŸããæåã®ãã©ã¡ãŒã¿ã¯ã€ã³ã¹ã¿ã³ã¹ã
self
ãšããŠè¡šããŸãã - ããããããŒã¯ãã¡ãœãã ã¬ã·ãŒãæ§æã§ã³ãŒããæŽçããç®çã§ãã¡ãœããã䜿çšããããšãã§ããŸããã¡ãœããã䜿çšããããšã§ããã¹ãŠã®å®è£ ã³ãŒãã 1 ã€ã®äºæž¬å¯èœãªå Žæã«ãŸãšããããšãã§ããŸãã
- ã¡ãœããã¯åïŒæ§é äœãåæåãªã©ïŒã®ã€ã³ã¹ã¿ã³ã¹ã§åŒã³åºãããŸããæåã®ãã©ã¡ãŒã¿ã¯ã€ã³ã¹ã¿ã³ã¹ã
- ã¡ãœãã ã¬ã·ãŒãã§ãã
self
ãšããããŒã¯ãŒãã®äœ¿çšã«ã€ããŠèª¬æããŸããself: Self
ã®ç¥èªã§ããããšã瀺ããæ§é äœåã®äœ¿çšæ¹æ³ã«ã€ããŠã説æããããšãããããããŸããSelf
ã¯impl
ãããã¯ãååšããåã®åãšã€ãªã¢ã¹ã§ããããããã¯å ã®ä»ã®å Žæã§äœ¿çšã§ããããšã説æããŸããself
ã¯ä»ã®æ§é äœãšåæ§ã«äœ¿çšããããããè¡šèšã䜿çšããŠåã ã®ãã£ãŒã«ããåç §ã§ããããšã説æããŸãã- ããã§
finish
ã 2 åå®è¡ããŠã&self
ãšself
ã®éãã瀺ãããšãããããããŸãã self
ã®ããªã¢ã³ã以å€ã«ããã¬ã·ãŒãåãšããŠèš±å¯ãããŠãã ç¹å¥ãªã©ãããŒåïŒBox<Self>
ãªã©ïŒããããŸãã
ãã¬ã€ãïŒtraitïŒ
Rustã§ã¯ãåã«é¢ããŠã®æœè±¡åããã¬ã€ããçšããŠè¡ãããšãã§ããŸãããã¬ã€ãã¯ã€ã³ã¿ãŒãã§ãŒã¹ã«äŒŒãŠããŸãïŒ
trait Pet { /// Return a sentence from this pet. fn talk(&self) -> String; /// Print a string to the terminal greeting this pet. fn greet(&self); }
-
ãã¬ã€ãã¯ããã®ãã¬ã€ããå®è£ ããããã«ååã«å¿ èŠãªå€æ°ã®ã¡ãœãããå®çŸ©ããŸãã
-
In the "Generics" segment, next, we will see how to build functionality that is generic over all types implementing a trait.
ãã¬ã€ãã®å®è£
trait Pet { fn talk(&self) -> String; fn greet(&self) { println!("Oh you're a cutie! What's your name? {}", self.talk()); } } struct Dog { name: String, age: i8, } impl Pet for Dog { fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) } } fn main() { let fido = Dog { name: String::from("Fido"), age: 5 }; fido.greet(); }
-
To implement
Trait
forType
, you use animpl Trait for Type { .. }
block. -
Unlike Go interfaces, just having matching methods is not enough: a
Cat
type with atalk()
method would not automatically satisfyPet
unless it is in animpl Pet
block. -
Traits may provide default implementations of some methods. Default implementations can rely on all the methods of the trait. In this case,
greet
is provided, and relies ontalk
.
ã¹ãŒããŒãã¬ã€ã
A trait can require that types implementing it also implement other traits, called supertraits. Here, any type implementing Pet
must implement Animal
.
trait Animal { fn leg_count(&self) -> u32; } trait Pet: Animal { fn name(&self) -> String; } struct Dog(String); impl Animal for Dog { fn leg_count(&self) -> u32 { 4 } } impl Pet for Dog { fn name(&self) -> String { self.0.clone() } } fn main() { let puppy = Dog(String::from("Rex")); println!("{} has {} legs", puppy.name(), puppy.leg_count()); }
This is sometimes called "trait inheritance" but students should not expect this to behave like OO inheritance. It just specifies an additional requirement on implementations of a trait.
é¢é£å
Associated types are placeholder types which are supplied by the trait implementation.
#[derive(Debug)] struct Meters(i32); #[derive(Debug)] struct MetersSquared(i32); trait Multiply { type Output; fn multiply(&self, other: &Self) -> Self::Output; } impl Multiply for Meters { type Output = MetersSquared; fn multiply(&self, other: &Self) -> Self::Output { MetersSquared(self.0 * other.0) } } fn main() { println!("{:?}", Meters(10).multiply(&Meters(20))); }
-
Associated types are sometimes also called "output types". The key observation is that the implementer, not the caller, chooses this type.
-
Many standard library traits have associated types, including arithmetic operators and
Iterator
.
å°åº
ãµããŒããããŠãããã¬ã€ãã¯ã次ã®ããã«ã«ã¹ã¿ã åã«èªåçã«å®è£ ã§ããŸãã
#[derive(Debug, Clone, Default)] struct Player { name: String, strength: u8, hit_points: u8, } fn main() { let p1 = Player::default(); // ããã©ã«ã ãã¬ã€ã㧠`default` ã³ã³ã¹ãã©ã¯ã¿ãè¿œå ããŸãã let mut p2 = p1.clone(); // ã¯ããŒã³ ãã¬ã€ã㧠`clone` ã¡ãœãããè¿œå ããŸãã p2.name = String::from("EldurScrollz"); // ãããã° ãã¬ã€ãã§ã`{:?}` ã䜿çšããåºåã®ãµããŒããè¿œå ããŸãã println!("{p1:?} vs. {p2:?}"); }
å°åºã¯ãã¯ãã§å®è£
ãããå€ãã®ã¯ã¬ãŒãã«ã¯æçšãªæ©èœãè¿œå ããããã®äŸ¿å©ãªå°åºãã¯ããçšæãããŠããŸããããšãã°ãserde
㯠#[derive(Serialize)]
ã䜿çšããŠãæ§é äœã®ã·ãªã¢ã«åã®ãµããŒããå°åºã§ããŸãã
Exercise: Logger Trait
ãã¬ã€ã Logger
ãš log
ã¡ãœããã䜿çšããŠãã·ã³ãã«ãªãã®ã³ã°ãŠãŒãã£ãªãã£ãèšèšããŠã¿ãŸããããé²è¡ç¶æ³ããã°ã«èšé²ããã³ãŒãã¯ããã®åŸã« &impl Logger
ãåãåãããšãã§ããŸãããã®å Žåããã¹ãã§ã¯ãã¹ããã°ãã¡ã€ã«ã«ã¡ãã»ãŒãžãæžã蟌ãŸããŸãããæ¬çªç°å¢ãã«ãã§ã¯ãã°ãµãŒããŒã«ã¡ãã»ãŒãžãéä¿¡ãããŸãã
However, the StdoutLogger
given below logs all messages, regardless of verbosity. Your task is to write a VerbosityFilter
type that will ignore messages above a maximum verbosity.
ããã¯äžè¬çãªãã¿ãŒã³ã§ããã€ãŸãããã¬ã€ãå®è£ ãã©ããããŠåããã¬ã€ããå®è£ ãããã®éçšã§æåãè¿œå ããŠããæ§é äœã§ãããã®ã³ã°ãŠãŒãã£ãªãã£ã§ã¯ä»ã«ã©ã®ãããªçš®é¡ã®ã©ãããŒã圹ç«ã€ã§ããããã
pub trait Logger { /// æå®ããã詳现床ã¬ãã«ã§ã¡ãã»ãŒãžããã°ã«èšé²ããŸãã fn log(&self, verbosity: u8, message: &str); } struct StdoutLogger; impl Logger for StdoutLogger { fn log(&self, verbosity: u8, message: &str) { println!("verbosity={verbosity}: {message}"); } } // TODO: `VerbosityFilter` ãå®çŸ©ããŠå®è£ ããŸãã fn main() { let logger = VerbosityFilter { max_verbosity: 3, inner: StdoutLogger }; logger.log(5, "FYI"); logger.log(2, "Uhoh"); }
解ç
pub trait Logger { /// æå®ããã詳现床ã¬ãã«ã§ã¡ãã»ãŒãžããã°ã«èšé²ããŸãã fn log(&self, verbosity: u8, message: &str); } struct StdoutLogger; impl Logger for StdoutLogger { fn log(&self, verbosity: u8, message: &str) { println!("verbosity={verbosity}: {message}"); } } /// æå®ããã詳现床ã¬ãã«ãŸã§ã®ã¡ãã»ãŒãžã®ã¿ããã°ã«èšé²ã struct VerbosityFilter { max_verbosity: u8, inner: StdoutLogger, } impl Logger for VerbosityFilter { fn log(&self, verbosity: u8, message: &str) { if verbosity <= self.max_verbosity { self.inner.log(verbosity, message); } } } fn main() { let logger = VerbosityFilter { max_verbosity: 3, inner: StdoutLogger }; logger.log(5, "FYI"); logger.log(2, "Uhoh"); }
ãããã
Including 10 minute breaks, this session should take about 3 hours and 15 minutes. It contains:
Segment | Duration |
---|---|
ãžã§ããªã¯ã¹ïŒgenericsïŒ | 45 minutes |
æšæºã©ã€ãã©ãªå ã®å | 1 hour |
æšæºã©ã€ãã©ãªå ã®ãã¬ã€ã | 1 hour and 10 minutes |
ãžã§ããªã¯ã¹ïŒgenericsïŒ
This segment should take about 45 minutes. It contains:
Slide | Duration |
---|---|
ãžã§ããªãã¯é¢æ° | 5 minutes |
ãžã§ããªãã¯ããŒã¿å | 10 minutes |
ãã¬ã€ãå¶çŽ | 10 minutes |
impl Trait | 5 minutes |
dyn Trait | 5 minutes |
æŒç¿: ãžã§ããªãã¯ãª min | 10 minutes |
ãžã§ããªãã¯é¢æ°
Rust supports generics, which lets you abstract algorithms or data structures (such as sorting or a binary tree) over the types used or stored.
/// `n` ã®å€ã«å¿ã㊠`even` ãŸã㯠`odd` ãéžæããŸãã fn pick<T>(n: i32, even: T, odd: T) -> T { if n % 2 == 0 { even } else { odd } } fn main() { println!("picked a number: {:?}", pick(97, 222, 333)); println!("picked a string: {:?}", pick(28, "dog", "cat")); }
-
Rust ã¯åŒæ°ãšæ»ãå€ã®åã«åºã¥ã㊠T ã®åãæšæž¬ããŸãã
-
In this example we only use the primitive types
i32
and&str
forT
, but we can use any type here, including user-defined types:struct Foo { val: u8, } pick(123, Foo { val: 7 }, Foo { val: 456 });
-
ãã㯠C++ ãã³ãã¬ãŒãã«äŒŒãŠããŸãããRust ã¯ãžã§ããªãã¯é¢æ°ãéšåçã«ããã«ã³ã³ãã€ã«ããããããã®é¢æ°ã¯å¶çŽã«äžèŽãããã¹ãŠã®åã«å¯ŸããŠæå¹ã§ããå¿ èŠããããŸããããšãã°ã
n == 0
ã®å Žåã¯even + odd
ãè¿ãããã«pick
ãå€æŽããŠã¿ãŠãã ãããæŽæ°ã䜿çšããpick
ã€ã³ã¹ã¿ã³ã¹åã®ã¿ã䜿çšãããŠããå Žåã§ããRust ã¯ãããç¡å¹ãšã¿ãªããŸããC++ ã§ã¯ãããè¡ãããšãã§ããŸãã -
Generic code is turned into non-generic code based on the call sites. This is a zero-cost abstraction: you get exactly the same result as if you had hand-coded the data structures without the abstraction.
ãžã§ããªãã¯ããŒã¿å
ãžã§ããªã¯ã¹ã䜿ã£ãŠãå ·äœçãªãã£ãŒã«ãã®åãæœè±¡åããããšãã§ããŸãïŒ
#[derive(Debug)] struct Point<T> { x: T, y: T, } impl<T> Point<T> { fn coords(&self) -> (&T, &T) { (&self.x, &self.y) } fn set_x(&mut self, x: T) { self.x = x; } } fn main() { let integer = Point { x: 5, y: 10 }; let float = Point { x: 1.0, y: 4.0 }; println!("{integer:?} and {float:?}"); println!("coords: {:?}", integer.coords()); }
-
Q: ãªã
T
ã¯ïŒåãimpl<T> Point<T> {}
ã«ãããŠæå®ãããã®ã§ããããïŒåé·ã§ã¯ãããŸãããïŒ- ãªããªããããã¯ãžã§ããªã¯ã¹ã«å¯ŸããŠã®ãžã§ããªãã¯ãªå®è£ ã®ç®æã ããã§ãããããã¯ç¬ç«ããŠãžã§ããªãã¯ã§ãã
- ã€ãŸãããã®ãããªã¡ãœããã¯ä»»æã®
T
ã«å¯ŸããŠå®çŸ©ããããšããããšã§ãã - It is possible to write
impl Point<u32> { .. }
.Point
ã¯ããã§ããªããžã§ããªãã¯ã§ãããPoint<f64>
ã䜿ãããšãã§ããŸãããããããã®ãããã¯ã§ã®ã¡ãœããã¯Point<u32>
ã«å¯ŸããŠã®ã¿å©çšå¯èœãšãªããŸãã
-
æ°ããå€æ°
let p = Point { x: 5, y: 10.0 };
ã宣èšããŠã¿ãŠãã ããã2 ã€ã®å€æ°ïŒT
ãšU
ãªã©ïŒã䜿çšããŠãç°ãªãåã®èŠçŽ ãæã€ãã€ã³ããèš±å¯ããããã«ã³ãŒããæŽæ°ããŸãã
ãžã§ããªãã¯ãã¬ã€ã
Traits can also be generic, just like types and functions. A trait's parameters get concrete types when it is used.
#[derive(Debug)] struct Foo(String); impl From<u32> for Foo { fn from(from: u32) -> Foo { Foo(format!("Converted from integer: {from}")) } } impl From<bool> for Foo { fn from(from: bool) -> Foo { Foo(format!("Converted from bool: {from}")) } } fn main() { let from_int = Foo::from(123); let from_bool = Foo::from(true); println!("{from_int:?}, {from_bool:?}"); }
-
The
From
trait will be covered later in the course, but its definition in thestd
docs is simple. -
Implementations of the trait do not need to cover all possible type parameters. Here,
Foo::from("hello")
would not compile because there is noFrom<&str>
implementation forFoo
. -
Generic traits take types as "input", while associated types are a kind of "output" type. A trait can have multiple implementations for different input types.
-
In fact, Rust requires that at most one implementation of a trait match for any type T. Unlike some other languages, Rust has no heuristic for choosing the "most specific" match. There is work on adding this support, called specialization.
ãã¬ã€ãå¶çŽ
ãžã§ããªã¯ã¹ãçšãããšãããããã¬ã€ãã®ã¡ãœãããåŒã³åºããããã«ãåããã®ãã¬ã€ããå®è£ ããŠããããšãèŠæ±ãããããšããããããŸããïŒè泚ïŒæ¬ææã§ã¯"Trait bounds"ãããã¬ã€ãå¶çŽããšç¿»èš³ããŸããããRustã®æ¥æ¬èªç¿»èš³ã³ãã¥ããã£ã§ã¯ããã¬ã€ãå¢çããšåŒã¶æµæŽŸããããã©ã¡ãã®ç¿»èš³ãæ¡çšãããã«ã€ããŠã¯è°è«ããªãããŠããŸããïŒ
You can do this with T: Trait
:
fn duplicate<T: Clone>(a: T) -> (T, T) { (a.clone(), a.clone()) } // struct NotCloneable; fn main() { let foo = String::from("foo"); let pair = duplicate(foo); println!("{pair:?}"); }
-
Try making a
NonCloneable
and passing it toduplicate
. -
è€æ°ã®ãã¬ã€ããå¿ èŠãªå Žåã¯ã
+
ã䜿ã£ãŠçµåããŸãã -
where
ç¯ã®äœ¿ãæ¹ã瀺ããŸããããåè¬çã¯ã³ãŒããèªãã§ãããšãã«ããã®where
ç¯ã«ééããŸããfn duplicate<T>(a: T) -> (T, T) where T: Clone, { (a.clone(), a.clone()) }
- ããããã®ãã©ã¡ã¿ãããå Žåã«ã
where
ç¯ã¯é¢æ°ã®ã·ã°ããã£ãæŽçæŽé ããŠãããŸãã where
ç¯ã«ã¯æŽã«åŒ·åãªæ©èœããããŸãã- 誰ãã«èãããå Žåã§è¯ãã§ããããã®æ©èœãšããã®ã¯ã":" ã®å·ŠåŽã«ã¯
Option<T>
ã®ããã«ä»»æã®åãè¡šçŸã§ãããšãããã®ã§ãã
- 誰ãã«èãããå Žåã§è¯ãã§ããããã®æ©èœãšããã®ã¯ã":" ã®å·ŠåŽã«ã¯
- ããããã®ãã©ã¡ã¿ãããå Žåã«ã
-
ãªããRust ã¯ãŸã ç¹å(specialization)ããµããŒãããŠããŸãããããšãã°ãå ã®
duplicate
ãããå Žåã¯ãç¹åãããduplicate(a: u32)
ãè¿œå ããããšã¯ã§ããŸããã
impl Trait
ãã¬ã€ãå¢çãšäŒŒãããã«ãæ§æ impl Trait
ã¯é¢æ°ã®åŒæ°ãšè¿ãå€ã«ãããŠã®ã¿å©çšå¯èœã§ãïŒ
// 以äžã®ç³è¡£æ§æ: // fn add_42_millions<T: Into<i32>>(x: T) -> i32 { fn add_42_millions(x: impl Into<i32>) -> i32 { x.into() + 42_000_000 } fn pair_of(x: u32) -> impl std::fmt::Debug { (x + 1, x - 1) } fn main() { let many = add_42_millions(42_i8); println!("{many}"); let many_more = add_42_millions(10_000_000); println!("{many_more}"); let debuggable = pair_of(27); println!("debuggable: {debuggable:?}"); }
impl Trait
allows you to work with types which you cannot name. The meaning of impl Trait
is a bit different in the different positions.
-
ãã©ã¡ã¿ã«å¯ŸããŠã¯ã
impl Trait
ã¯ããã¬ã€ãå¢çãæã€å¿åã®ãžã§ããªãã¯ãã©ã¡ã¿ã®ãããªãã®ã§ãã -
è¿ãå€ã®åã«çšããå Žåã¯ãç¹å®ã®ãã¬ã€ããå®è£ ããäœããã®å ·è±¡åãè¿ãããå ·äœçãªååã¯æ瀺ããªããšããããšãæå³ããŸãããã®ããšã¯å ¬éãããAPIã«å ·è±¡åãæããããªãå Žåã«äŸ¿å©ã§ãã
è¿ãå€ã®äœçœ®ã«ãããåæšè«ã¯å°é£ã§ãã
impl Foo
ãè¿ãé¢æ°ã¯ããããè¿ãå ·è±¡åã¯ãœãŒã¹ã³ãŒãã«æžãããããšãªããŸãŸãå ·è±¡åãéžã³ãŸããcollect<B>() -> B
ã®ãããªãžã§ããªãã¯åãè¿ãé¢æ°ã¯ãB
ãæºããã©ã®ãããªåã§ãè¿ãããšããããŸãã ãŸããé¢æ°ã®åŒã³åºãå ã¯ãã®ãããªåãäžã€ãéžã¶å¿ èŠããããããããŸããã ããã¯ãlet x: Vec<_> = foo.collect()
ãšããããturbofishãçšããŠfoo.collect::<Vec<_>>()
ãšããããšã§è¡ããŸãã
debuggable
ã®åã¯äœã§ãããããlet debuggable: () = ..
ãè©ŠããŠããšã©ãŒ ã¡ãã»ãŒãžã®å
容ã確èªããŠãã ããã
dyn Trait
In addition to using traits for static dispatch via generics, Rust also supports using them for type-erased, dynamic dispatch via trait objects:
struct Dog { name: String, age: i8, } struct Cat { lives: i8, } trait Pet { fn talk(&self) -> String; } impl Pet for Dog { fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) } } impl Pet for Cat { fn talk(&self) -> String { String::from("Miau!") } } // Uses generics and static dispatch. fn generic(pet: &impl Pet) { println!("Hello, who are you? {}", pet.talk()); } // Uses type-erasure and dynamic dispatch. fn dynamic(pet: &dyn Pet) { println!("Hello, who are you? {}", pet.talk()); } fn main() { let cat = Cat { lives: 9 }; let dog = Dog { name: String::from("Fido"), age: 5 }; generic(&cat); generic(&dog); dynamic(&cat); dynamic(&dog); }
-
Generics, including
impl Trait
, use monomorphization to create a specialized instance of the function for each different type that the generic is instantiated with. This means that calling a trait method from within a generic function still uses static dispatch, as the compiler has full type information and can resolve which type's trait implementation to use. -
When using
dyn Trait
, it instead uses dynamic dispatch through a virtual method table (vtable). This means that there's a single version offn dynamic
that is used regardless of what type ofPet
is passed in. -
When using
dyn Trait
, the trait object needs to be behind some kind of indirection. In this case it's a reference, though smart pointer types likeBox
can also be used (this will be demonstrated on day 3). -
At runtime, a
&dyn Pet
is represented as a "fat pointer", i.e. a pair of two pointers: One pointer points to the concrete object that implementsPet
, and the other points to the vtable for the trait implementation for that type. When calling thetalk
method on&dyn Pet
the compiler looks up the function pointer fortalk
in the vtable and then invokes the function, passing the pointer to theDog
orCat
into that function. The compiler doesn't need to know the concrete type of thePet
in order to do this. -
A
dyn Trait
is considered to be "type-erased", because we no longer have compile-time knowledge of what the concrete type is.
æŒç¿: ãžã§ããªãã¯ãª min
In this short exercise, you will implement a generic min
function that determines the minimum of two values, using the Ord
trait.
use std::cmp::Ordering; // TODO: `main` ã§äœ¿çšãã `min` é¢æ°ãå®è£ ããŸãã fn main() { assert_eq!(min(0, 10), 0); assert_eq!(min(500, 123), 123); assert_eq!(min('a', 'z'), 'a'); assert_eq!(min('7', '1'), '1'); assert_eq!(min("hello", "goodbye"), "goodbye"); assert_eq!(min("bat", "armadillo"), "armadillo"); }
解ç
use std::cmp::Ordering; fn min<T: Ord>(l: T, r: T) -> T { match l.cmp(&r) { Ordering::Less | Ordering::Equal => l, Ordering::Greater => r, } } fn main() { assert_eq!(min(0, 10), 0); assert_eq!(min(500, 123), 123); assert_eq!(min('a', 'z'), 'a'); assert_eq!(min('7', '1'), '1'); assert_eq!(min("hello", "goodbye"), "goodbye"); assert_eq!(min("bat", "armadillo"), "armadillo"); }
æšæºã©ã€ãã©ãªå ã®å
This segment should take about 1 hour. It contains:
Slide | Duration |
---|---|
æšæºã©ã€ãã©ãª | 3 minutes |
ããã¥ã¡ã³ã | 5 minutes |
Option | 10 minutes |
Result | 5 minutes |
String | 5 minutes |
Vec | 5 minutes |
HashMap | 5 minutes |
æŒç¿: ã«ãŠã³ã¿ãŒ | 20 minutes |
ãã®ã»ã¯ã·ã§ã³ã®åã¹ã©ã€ãã§ã¯ãæéããããŠããã¥ã¡ã³ã ããŒãžã確èªããããäžè¬çãªã¡ãœãããããã€ãåãäžããŠãã ããã
æšæºã©ã€ãã©ãª
Rust ã«ã¯ãRust ã®ã©ã€ãã©ãªãšããã°ã©ã ã§äœ¿çšãããäžè¬çãªåã®ã»ããã確ç«ããã®ã«åœ¹ç«ã€æšæºã©ã€ãã©ãªãä»å±ããŠããŸãã2 ã€ã®ã©ã€ãã©ãªãã¹ã ãŒãºã«é£æºãããããšãã§ããã®ã¯ããã®ããã«äž¡æ¹ãšãåã String
åã䜿çšããŠããããã§ãã
å®éãRust ã«ã¯æšæºã©ã€ãã©ãªïŒcore
ãalloc
ãstd
ïŒã®è€æ°ã®ã¬ã€ã€ãå«ãŸããŠããŸãã
core
ã«ã¯ãlibc
ãã¢ãã±ãŒã¿ãããã«ã¯ãªãã¬ãŒãã£ã³ã° ã·ã¹ãã ã®ååšã«ãäŸåããªããæãåºæ¬çãªåãšé¢æ°ãå«ãŸããŸããalloc
ã«ã¯ãVec
ãBox
ãArc
ãªã©ãã°ããŒãã«ããŒãã¢ãã±ãŒã¿ãå¿ èŠãšããåãå«ãŸããŸãã- å€ãã®å Žåãåã蟌ã¿ã® Rust ã¢ããªã¯
core
ã®ã¿ã䜿çšããå Žåã«ãã£ãŠã¯alloc
ã䜿çšããŸãã
ããã¥ã¡ã³ã
Rust ã«ã¯è©³çŽ°ãªããã¥ã¡ã³ããçšæãããŠããŸãã次ã«äŸã瀺ããŸãã
- All of the details about loops.
u8
ã®ãããªããªããã£ãåãOption
ãBinaryHeap
ãªã©ã®æšæºã©ã€ãã©ãªåã
Use rustup doc --std
or https://std.rs to view the documentation.
å®éãç¬èªã®ã³ãŒãã«ããã¥ã¡ã³ããã€ããããšãã§ããŸãã
/// æåã®åŒæ°ã 2 çªç®ã®åŒæ°ã§å²ãåãããã©ãããå€å®ããŸãã /// /// 2 çªç®ã®åŒæ°ããŒãã®å Žåãçµæ㯠false ã«ãªããŸãã fn is_divisible_by(lhs: u32, rhs: u32) -> bool { if rhs == 0 { return false; } lhs % rhs == 0 }
ã³ã³ãã³ãã¯ããŒã¯ããŠã³ãšããŠæ±ãããŸããå
¬éããããã¹ãŠã® Rust ã©ã€ãã©ãª ã¯ã¬ãŒãã¯ãrustdoc ããŒã«ã䜿çšããŠãdocs.rs
ã§èªåçã«ããã¥ã¡ã³ãããŸãšããããŸãããã®ãã¿ãŒã³ã䜿çšããŠããã¹ãŠã®å
¬éã¢ã€ãã ã API ã§ããã¥ã¡ã³ãåããã®ãæ
£çšçã§ãã
ã¢ã€ãã å
ïŒã¢ãžã¥ãŒã«å
ãªã©ïŒããã¢ã€ãã ãããã¥ã¡ã³ãåããã«ã¯ããå
éšããã¥ã¡ã³ãã®ã³ã¡ã³ãããšåŒã°ãã //!
ãŸã㯠/*! .. */
ã䜿çšããŸãã
//! ãã®ã¢ãžã¥ãŒã«ã«ã¯ãæŽæ°ã®æŽé€ã«é¢é£ããæ©èœãå«ãŸããŠããŸãã
- https://docs.rs/rand ã§
rand
ã¯ã¬ãŒãçšã«çæãããããã¥ã¡ã³ããåè¬è ã«ç€ºããŸãã
Option
Option<T>
ã®äœ¿çšæ¹æ³ã«ã€ããŠã¯ãã§ã«ããã€ãèŠãŠããŸããããããã¯å T
ã®å€ãæ ŒçŽããããäœãæ ŒçŽããŸãããããšãã°ãString::find
㯠Option<usize>
ãè¿ããŸãã
fn main() { let name = "Löwe èè Léopard Gepardi"; let mut position: Option<usize> = name.find('é'); println!("find returned {position:?}"); assert_eq!(position.unwrap(), 14); position = name.find('Z'); println!("find returned {position:?}"); assert_eq!(position.expect("Character not found"), 0); }
-
Option
is widely used, not just in the standard library. -
unwrap
ã¯Option
å ã®å€ãè¿ããããããã¯ã«ãªããŸããexpect
ãåæ§ã§ããããšã©ãŒã¡ãã»ãŒãžãåãåããŸãã- None ã§ãããã¯ã«ãªãå ŽåããããŸãããã誀ã£ãŠãNone ã®ãã§ãã¯ãå¿ããããšã¯ãããŸããã
- äœããäžç·ã«ãããã³ã°ããå Žåã¯ããã¡ãã¡ã§
unwrap
/expect
ãè¡ãã®ãäžè¬çã§ãããæ¬çªç°å¢ã®ã³ãŒãã¯éåžžãNone
ãããé©åã«åŠçããŸãã
-
The "niche optimization" means that
Option<T>
often has the same size in memory asT
, if there is some representation that is not a valid value of T. For example, a reference cannot be NULL, soOption<&T>
automatically uses NULL to represent theNone
variant, and thus can be stored in the same memory as&T
.
Result
Result
is similar to Option
, but indicates the success or failure of an operation, each with a different enum variant. It is generic: Result<T, E>
where T
is used in the Ok
variant and E
appears in the Err
variant.
use std::fs::File; use std::io::Read; fn main() { let file: Result<File, std::io::Error> = File::open("diary.txt"); match file { Ok(mut file) => { let mut contents = String::new(); if let Ok(bytes) = file.read_to_string(&mut contents) { println!("Dear diary: {contents} ({bytes} bytes)"); } else { println!("Could not read file content"); } } Err(err) => { println!("The diary could not be opened: {err}"); } } }
Option
ãšåæ§ã«ãæåããå€ã¯Result
ã®å éšã«ãããããããããŒã¯ãããæ瀺çã«æœåºããå¿ èŠããããŸããããã«ããããšã©ãŒãã§ãã¯ãä¿é²ãããŸãããšã©ãŒãçºçããŠã¯ãªããªãå Žåã¯ãunwrap()
ãŸãã¯expect()
ãåŒã³åºãããšãã§ããŸãããããããããããŒã®ã€ã³ãã³ãã®ã·ã°ãã«ã§ããResult
ã®ããã¥ã¡ã³ããèªãããšãããããŸãããããã®è¬åº§ã§ã¯åãäžããŸããããèšåãã䟡å€ããããŸãããã®ããã¥ã¡ã³ãã«ã¯ãé¢æ°åããã°ã©ãã³ã°ã«åœ¹ç«ã€äŸ¿å©ãªã¡ãœãããé¢æ°ãå€æ°å«ãŸããŠããŸããResult
is the standard type to implement error handling as we will see on Day 4.
String
String
is a growable UTF-8 encoded string:
fn main() { let mut s1 = String::new(); s1.push_str("Hello"); println!("s1: len = {}, capacity = {}", s1.len(), s1.capacity()); let mut s2 = String::with_capacity(s1.len() + 1); s2.push_str(&s1); s2.push('!'); println!("s2: len = {}, capacity = {}", s2.len(), s2.capacity()); let s3 = String::from("ðšð"); println!("s3: len = {}, number of chars = {}", s3.len(), s3.chars().count()); }
String
㯠Deref<Target = str>
ãå®è£
ããŸããã€ãŸããString
ã®ãã¹ãŠã® str
ã¡ãœãããåŒã³åºãããšãã§ããŸãã
String::new
ã¯æ°ãã空ã®æååãè¿ããŸããæååã«ããã·ã¥ããããŒã¿ã®éãããã£ãŠããå Žåã¯String::with_capacity
ã䜿çšããŸããString::len
ã¯ãString
ã®ãµã€ãºããã€ãåäœã§è¿ããŸãïŒæåæ°ãšã¯ç°ãªãå ŽåããããŸãïŒãString::chars
ã¯ãå®éã®æåã®ã€ãã¬ãŒã¿ãè¿ããŸããæžèšçŽ ã¯ã©ã¹ã¿ã«ãããchar
ã¯äººéããæåããšèŠãªããã®ãšã¯ç°ãªãå ŽåããããŸãã- 人ã
ãæååã«ã€ããŠèšåããå Žåãåã«
&str
ãŸãã¯String
ã®ããšã話ããŠããå¯èœæ§ããããŸãã - åã
Deref<Target = T>
ãå®è£ ããŠããå Žåãã³ã³ãã€ã©ã«ããT
ããã¡ãœãããééçã«åŒã³åºããããã«ãªããŸããDeref
ãã¬ã€ãã«ã€ããŠã¯ãŸã 説æããŠããªããããçŸæç¹ã§ã¯äž»ã«ããã¥ã¡ã³ãã®ãµã€ãããŒã®æ§é ã«ã€ããŠèª¬æããŠããŸããString
ã¯Deref<Target = str>
ãå®è£ ããstr
ã®ã¡ãœãããžã®ã¢ã¯ã»ã¹ãééçã«èš±å¯ããŸããlet s3 = s1.deref();
ãšlet s3 = &*s1;
ãèšè¿°ããŠæ¯èŒããŸãã
String
ã¯ãã€ãã®ãã¯ã¿ãŒã®ã©ãããŒãšããŠå®è£ ãããŸãããã¯ã¿ãŒã§ãµããŒããããŠãããªãã¬ãŒã·ã§ã³ã®å€ãã¯String
ã§ããµããŒããããŠããŸãããããã€ãã®ä¿èšŒãè¿œå ãããŠããŸããString
ã«ã€ã³ããã¯ã¹ãä»ããããŸããŸãªæ¹æ³ãæ¯èŒããŸãã- æåã«ã¯
s3.chars().nth(i).unwrap()
ã䜿çšããŸããããã§i
ã¯å¢çå ã®å Žåãå¢çå€ã®å Žåãè¡šããŸãã - éšåæååã«ã¯
s3[0..4]
ã䜿çšããŸãããã®ã¹ã©ã€ã¹ã¯ãæåå¢çã«ããå Žåãšãªãå ŽåããããŸãã
- æåã«ã¯
- Many types can be converted to a string with the
to_string
method. This trait is automatically implemented for all types that implementDisplay
, so anything that can be formatted can also be converted to a string.
Vec
Vec
ã¯ããµã€ãºå€æŽå¯èœãªæšæºã®ããŒãå²ãåœãŠãããã¡ã§ãã
fn main() { let mut v1 = Vec::new(); v1.push(42); println!("v1: len = {}, capacity = {}", v1.len(), v1.capacity()); let mut v2 = Vec::with_capacity(v1.len() + 1); v2.extend(v1.iter()); v2.push(9999); println!("v2: len = {}, capacity = {}", v2.len(), v2.capacity()); // èŠçŽ ã§ãã¯ã¿ãŒãåæåããæ£èŠãã¯ãã let mut v3 = vec![0, 0, 1, 2, 3, 4]; // å¶æ°èŠçŽ ã®ã¿ãä¿æããŸãã v3.retain(|x| x % 2 == 0); println!("{v3:?}"); // é£ç¶ããéè€ãåé€ããŸãã v3.dedup(); println!("{v3:?}"); }
Vec
㯠Deref<Target = [T]>
ãå®è£
ããŠãããããVec
ã§ã¹ã©ã€ã¹ ã¡ãœãããåŒã³åºãããšãã§ããŸãã
Vec
ã¯ãString
ããã³HashMap
ãšãšãã«ã³ã¬ã¯ã·ã§ã³ã®äžçš®ã§ããå«ãŸããŠããããŒã¿ã¯ããŒãã«æ ŒçŽããããããã³ã³ãã€ã«æã«ããŒã¿éãææ¡ããå¿ èŠã¯ãããŸãããããŒã¿éã¯å®è¡æã«å¢å ãŸãã¯æžå°ããå ŽåããããŸããVec<T>
ããžã§ããªãã¯åã§ãããT
ãæ瀺çã«æå®ããå¿ èŠã¯ãããŸãããRust ã®åæšè«ã§ãã€ãè¡ãããããã«ãæåã®push
åŒã³åºãã§T
ã確ç«ãããŠããŸããvec![...]
ã¯Vec::new()
ã®ä»£ããã«äœ¿çšããæ£èŠã®ãã¯ãã§ããã¯ã¿ãŒãžã®åæèŠçŽ ã®è¿œå ããµããŒãããŠããŸãã- ãã¯ã¿ãŒã«ã€ã³ããã¯ã¹ãä»ããã«ã¯
[
]
ã䜿çšããŸãããå¢çå€ã®å Žåã¯ãããã¯ãçºçããŸãããŸãã¯ãget
ã䜿çšãããšOption
ãè¿ãããŸããpop
é¢æ°ã¯æåŸã®èŠçŽ ãåé€ããŸãã - ã¹ã©ã€ã¹ã«ã€ããŠã¯ 3 æ¥ç®ã«èª¬æããŸããåè¬è
ã¯çŸæç¹ã§ã¯ãå
Vec
ã®å€ã«ãããããã¥ã¡ã³ãã«èšããããã¹ãŠã®ã¹ã©ã€ã¹ã¡ãœããã«ã¢ã¯ã»ã¹ã§ããããšã ããç¥ã£ãŠããã°ååã§ãã
HashMap
HashDoS æ»æããä¿è·ããæšæºã®ããã·ã¥ããã:
use std::collections::HashMap; fn main() { let mut page_counts = HashMap::new(); page_counts.insert("Adventures of Huckleberry Finn", 207); page_counts.insert("Grimms' Fairy Tales", 751); page_counts.insert("Pride and Prejudice", 303); if !page_counts.contains_key("Les Misérables") { println!( "We know about {} books, but not Les Misérables.", page_counts.len() ); } for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] { match page_counts.get(book) { Some(count) => println!("{book}: {count} pages"), None => println!("{book} is unknown."), } } // äœãèŠã€ãããªãã£ãå Žåã¯ã.entry() ã¡ãœããã䜿çšããŠå€ãæ¿å ¥ããŸãã for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] { let page_count: &mut i32 = page_counts.entry(book).or_insert(0); *page_count += 1; } println!("{page_counts:#?}"); }
-
HashMap
ã¯ãã¬ãªã¥ãŒãã§å®çŸ©ãããŠããªããããã¹ã³ãŒãã«å«ããå¿ èŠããããŸãã -
次ã®ã³ãŒãè¡ãè©ŠããŸããæåã®è¡ã§ãæžç±ãããã·ã¥ãããã«ãããã©ããã確èªãããªãå Žåã¯ä»£æ¿å€ãè¿ããŸããæžç±ãèŠã€ãããªãã£ãå Žåã2 è¡ç®ã§ããã·ã¥ãããã«ä»£æ¿å€ãæ¿å ¥ããŸãã
let pc1 = page_counts .get("Harry Potter and the Sorcerer's Stone") .unwrap_or(&336); let pc2 = page_counts .entry("The Hunger Games") .or_insert(374);
-
vec!
ãšã¯ç°ãªããæšæºã®hashmap!
ãã¯ãã¯ãããŸããã-
ããããRust 1.56 以éã§ã¯ãHashMap ã¯
From<[(K, V); N]>
ãå®è£ ããŠããŸããããã«ããããªãã©ã«é åããããã·ã¥ããããç°¡åã«åæåã§ããŸããlet page_counts = HashMap::from([ ("Harry Potter and the Sorcerer's Stone".to_string(), 336), ("The Hunger Games".to_string(), 374), ]);
-
-
å¥ã®æ¹æ³ãšããŠãHashMap ã¯ãKey-Value ã¿ãã«ãçæããä»»æã®
Iterator
ããäœæããããšãã§ããŸãã -
ãã®åã«ã¯ã
std::collections::hash_map::Keys
ãªã©ã®ãã¡ãœããåºæã®ãæ»ãå€ã®åãããã€ããããŸãããããã®åã¯ãRust ããã¥ã¡ã³ãã®æ€çŽ¢ã§ãã䜿çšãããŸãããã®åã®ããã¥ã¡ã³ããšãkeys
ã¡ãœããã«æ»ãã®ã«åœ¹ç«ã€ãªã³ã¯ãåè¬è ã«ç€ºããŸãã
æŒç¿: ã«ãŠã³ã¿ãŒ
ãã®æŒç¿ã§ã¯ãéåžžã«ã·ã³ãã«ãªããŒã¿æ§é ãæ±çšçãªãã®ã«ããŸããstd::collections::HashMap
ã䜿çšããŠãã©ã®å€ã確èªãããåå€ãäœååºçŸãããã远跡ããŸãã
Counter
ã®åæããŒãžã§ã³ã¯ãu32
ã®å€ã§ã®ã¿æ©èœããããã«ããŒãã³ãŒããããŠããŸãã远跡ããå€ã®åã«å¯ŸããŠæ§é äœãšãã®ã¡ãœããããžã§ããªãã¯åããŸããããã«ãããCounter
ã§ããããåã®å€ã远跡ã§ããŸãã
æ©ãã«çµãã£ãå Žåã¯ãentry
ã¡ãœããã䜿çšããŠãcount
ã¡ãœããã®å®è£
ã«å¿
èŠãªããã·ã¥ ã«ãã¯ã¢ããã®åæ°ãååã«ããŠã¿ãŸãããã
use std::collections::HashMap; /// ã«ãŠã³ã¿ã¯å T ã®åå€ã確èªãããåæ°ãã«ãŠã³ãããŸãã struct Counter { values: HashMap<u32, u64>, } impl Counter { /// æ°ããã«ãŠã³ã¿ãäœæããŸãã fn new() -> Self { Counter { values: HashMap::new(), } } /// æå®ãããå€ã®çºçãã«ãŠã³ãããŸãã fn count(&mut self, value: u32) { if self.values.contains_key(&value) { *self.values.get_mut(&value).unwrap() += 1; } else { self.values.insert(value, 1); } } /// æå®ãããå€ã確èªãããåæ°ãè¿ããŸãã fn times_seen(&self, value: u32) -> u64 { self.values.get(&value).copied().unwrap_or_default() } } fn main() { let mut ctr = Counter::new(); ctr.count(13); ctr.count(14); ctr.count(16); ctr.count(14); ctr.count(14); ctr.count(11); for i in 10..20 { println!("saw {} values equal to {}", ctr.times_seen(i), i); } let mut strctr = Counter::new(); strctr.count("apple"); strctr.count("orange"); strctr.count("apple"); println!("got {} apples", strctr.times_seen("apple")); }
解ç
use std::collections::HashMap; use std::hash::Hash; /// ã«ãŠã³ã¿ã¯å T ã®åå€ã確èªãããåæ°ãã«ãŠã³ãããŸãã struct Counter<T> { values: HashMap<T, u64>, } impl<T: Eq + Hash> Counter<T> { /// æ°ããã«ãŠã³ã¿ãäœæããŸãã fn new() -> Self { Counter { values: HashMap::new() } } /// æå®ãããå€ã®çºçãã«ãŠã³ãããŸãã fn count(&mut self, value: T) { *self.values.entry(value).or_default() += 1; } /// æå®ãããå€ã確èªãããåæ°ãè¿ããŸãã fn times_seen(&self, value: T) -> u64 { self.values.get(&value).copied().unwrap_or_default() } } fn main() { let mut ctr = Counter::new(); ctr.count(13); ctr.count(14); ctr.count(16); ctr.count(14); ctr.count(14); ctr.count(11); for i in 10..20 { println!("saw {} values equal to {}", ctr.times_seen(i), i); } let mut strctr = Counter::new(); strctr.count("apple"); strctr.count("orange"); strctr.count("apple"); println!("got {} apples", strctr.times_seen("apple")); }
æšæºã©ã€ãã©ãªå ã®ãã¬ã€ã
This segment should take about 1 hour and 10 minutes. It contains:
Slide | Duration |
---|---|
ä»ã®èšèªãšã®æ¯èŒ | 5 minutes |
æŒç®å | 5 minutes |
From ãš Into | 5 minutes |
ãã£ã¹ã | 5 minutes |
Read ãš Write | 5 minutes |
Defaultãæ§é äœæŽæ°èšæ³ | 5 minutes |
ã¯ããŒãžã£ | 10 minutes |
æŒç¿: ROT13æå· | 30 minutes |
æšæºã©ã€ãã©ãªåãšåæ§ã«ãæéããããŠåãã¬ã€ãã®ããã¥ã¡ã³ãã確èªããŸãã
ãã®ã»ã¯ã·ã§ã³ã¯é·ããããéäžã§äŒæ©ãåã£ãŠãã ããã
ä»ã®èšèªãšã®æ¯èŒ
ãããã®ãã¬ã€ãã¯å€ã®æ¯èŒããµããŒãããŸãããã¹ãŠã®ãã¬ã€ãã¯ããããã®ãã¬ã€ããå®è£ ãããã£ãŒã«ããå«ãåçšã«å°åºã§ããŸãã
PartialEq
ãš Eq
PartialEq
ã¯ãå¿
é ã®ã¡ãœãã eq
ãšæå®ãããã¡ãœãã ne
ãæã€éšåçãªç䟡é¢ä¿ã§ãã==
æŒç®åãš !=
æŒç®åã¯ããããã®ã¡ãœãããåŒã³åºããŸãã
struct Key { id: u32, metadata: Option<String>, } impl PartialEq for Key { fn eq(&self, other: &Self) -> bool { self.id == other.id } }
Eq
ã¯å®å
šãªç䟡é¢ä¿ïŒåå°çã察称çãæšç§»çïŒã§ãããPartialEq
ãæå³ããŸããå®å
šãªç䟡é¢ä¿ãå¿
èŠãšããé¢æ°ã¯ããã¬ã€ãå¢çãšã㊠Eq
ã䜿çšããŸãã
PartialOrd
ãš Ord
PartialOrd
㯠partial_cmp
ã¡ãœããã䜿ã£ãŠéšåçãªé åºãå®çŸ©ããŸããããã¯ã<
ã<=
ã>=
ã>
æŒç®åãå®è£
ããããã«äœ¿çšãããŸãã
use std::cmp::Ordering; #[derive(Eq, PartialEq)] struct Citation { author: String, year: u32, } impl PartialOrd for Citation { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { match self.author.partial_cmp(&other.author) { Some(Ordering::Equal) => self.year.partial_cmp(&other.year), author_ord => author_ord, } } }
Ord
ã¯å
šé åºã瀺ããcmp
㯠Ordering
ãè¿ããŸãã
PartialEq
ã¯ç°ãªãåã®éã§å®è£
ã§ããŸãããEq
ã¯åå°çã§ãããããå®è£
ã§ããŸããã
struct Key { id: u32, metadata: Option<String>, } impl PartialEq<u32> for Key { fn eq(&self, other: &u32) -> bool { self.id == *other } }
å®éã«ã¯ããããã®ãã¬ã€ããå°åºããããšã¯äžè¬çã§ãããå®è£ ããã®ã¯äžè¬çã§ã¯ãããŸããã
æŒç®å
æŒç®åã®ãªãŒããŒããŒãã¯ãstd::ops
å
ã®ãã¬ã€ããä»ããŠå®è£
ãããŸãã
#[derive(Debug, Copy, Clone)] struct Point { x: i32, y: i32, } impl std::ops::Add for Point { type Output = Self; fn add(self, other: Self) -> Self { Self { x: self.x + other.x, y: self.y + other.y } } } fn main() { let p1 = Point { x: 10, y: 20 }; let p2 = Point { x: 100, y: 200 }; println!("{p1:?} + {p2:?} = {:?}", p1 + p2); }
è°è«ã®ãã€ã³ã:
&Point
ã«Add
ãå®è£ ã§ããŸããããã¯ã©ã®ãããªç¶æ³ã§åœ¹ã«ç«ã¡ãŸããïŒ- åç:
Add:add
ã¯self
ã䜿çšããŸããæŒç®åããªãŒããŒããŒãããåT
ãCopy
ã§ãªãå Žåã¯ã&T
ã®æŒç®åããªãŒããŒããŒãããããšãæ€èšããå¿ èŠããããŸããããã«ãããåŒã³åºãç®æã§ã®äžèŠãªã¯ããŒã³äœæãåé¿ã§ããŸãã
- åç:
Output
ãé¢é£åã§ããã®ã¯ãªãã§ããïŒãããã¡ãœããã®åãã©ã¡ãŒã¿ã«ã§ããã§ããããïŒ- çãåç: é¢æ°åã®ãã©ã¡ãŒã¿ã¯åŒã³åºãå
ã«ãã£ãŠå¶åŸ¡ãããŸãããé¢é£åïŒ
Output
ãªã©ïŒã¯ãã¬ã€ãã®å®è£ è ã«ãã£ãŠå¶åŸ¡ãããŸãã
- çãåç: é¢æ°åã®ãã©ã¡ãŒã¿ã¯åŒã³åºãå
ã«ãã£ãŠå¶åŸ¡ãããŸãããé¢é£åïŒ
- 2 çš®é¡ã®åã«å¯ŸããŠ
Add
ãå®è£ ã§ããŸããããšãã°ãimpl Add<(i32, i32)> for Point
ã¯Point
ã«ã¿ãã«ãè¿œå ããŸãã
The Not
trait (!
operator) is notable because it does not "boolify" like the same operator in C-family languages; instead, for integer types it negates each bit of the number, which arithmetically is equivalent to subtracting it from -1: !5 == -6
.
From
ãš Into
Types implement From
and Into
to facilitate type conversions. Unlike as
, these traits correspond to lossless, infallible conversions.
fn main() { let s = String::from("hello"); let addr = std::net::Ipv4Addr::from([127, 0, 0, 1]); let one = i16::from(true); let bigger = i32::from(123_i16); println!("{s}, {addr}, {one}, {bigger}"); }
From
ãå®è£
ããããšãInto
ãèªåçã«å®è£
ãããŸãã
fn main() { let s: String = "hello".into(); let addr: std::net::Ipv4Addr = [127, 0, 0, 1].into(); let one: i16 = true.into(); let bigger: i32 = 123_i16.into(); println!("{s}, {addr}, {one}, {bigger}"); }
- ãã®ããã«
Into
ãå®è£ ããããããåã«ã¯From
ã®ã¿ãå®è£ ããã®ãäžè¬çã§ãã - ã
String
ã«å€æã§ãããã¹ãŠãã®ãããªé¢æ°åŒæ°ã®å ¥ååã宣èšããå Žåããã®ã«ãŒã«ã¯éãšãªããInto
ã䜿çšããå¿ èŠããããŸããé¢æ°ã¯ãFrom
ãå®è£ ããåãšãInto
ã®ã¿ ãå®è£ ããåãåãå ¥ããŸãã
ãã£ã¹ã
Rust ã«ã¯ æé»ç ãªåå€æã¯ãããŸããããas
ã«ããæ瀺çãªãã£ã¹ãã¯ãµããŒããããŠããŸãããããã®ãã£ã¹ãã¯éåžžãããããå®çŸ©ãããŠãã C ã»ãã³ãã£ã¯ã¹ã«åŸããŸãã
fn main() { let value: i64 = 1000; println!("as u16: {}", value as u16); println!("as i16: {}", value as i16); println!("as u8: {}", value as u8); }
as
ã®çµæ㯠Rust 㧠垞㫠å®çŸ©ããããã©ãããã©ãŒã éã§äžè²«ããŠããŸããããã¯ãæ£è² ã®ç¬Šå·ãå€ããããããå°ããªåã«ãã£ã¹ããããããéã«åŸãããçŽæã«åããŠãããããããŸãããããã¥ã¡ã³ãã確èªããæ確ã«ããããã«ã³ã¡ã³ããèšè¿°ããŠãã ããã
as
ã䜿çšãããã£ã¹ãã¯æ¯èŒçæ±ãã«ããã誀ã£ãŠäœ¿çšããããšãå°ãªããããŸããããŸããå°æ¥ã®ã¡ã³ããã³ã¹äœæ¥ã§ã䜿çšãããåãåã®å€ã®ç¯å²ãå€æŽãããéã«ããããã«ãããã°ãçºçããå¯èœæ§ããããŸãããã£ã¹ãã¯ãç¡æ¡ä»¶ã®åãæšãŠã瀺ãããšãç®çãšããŠããå Žåã«ã®ã¿ãæé©ã«äœ¿çšãããŸãïŒããšãã°ãäžäœãããã®å
容ã«é¢ä¿ãªããas u32
㧠u64
ã®äžäœ 32 ããããéžæããå ŽåïŒã
絶察ã«æ£ãããã£ã¹ãïŒäŸ: u32
ãã u64
ãžã®ãã£ã¹ãïŒã§ã¯ããã£ã¹ããå®éã«å®ç§ã§ããããšã確èªããããã«ãas
ã§ã¯ãªã From
ãŸã㯠Into
ã䜿çšããããšãããããããŸããæ£ãããªãå¯èœæ§ããããã£ã¹ãã«ã€ããŠã¯ã絶察ã«æ£ãããã£ã¹ããšã¯ç°ãªãæ¹æ³ã§ããããåŠçãããå Žåã«ãTryFrom
ãš TryInto
ã䜿çšã§ããŸãã
ãã®ã¹ã©ã€ãã®åŸã§äŒæ©ãåãããšãæ€èšããŠãã ããã
as
㯠C++ ã®éçãã£ã¹ãã«äŒŒãŠããŸããããŒã¿ã倱ãããå¯èœæ§ãããç¶æ³ã§ as
ã䜿çšããããšã¯ãäžè¬çã«æšå¥šãããŸããã䜿çšããå Žåã¯ãå°ãªããšã説æã®ã³ã¡ã³ããèšè¿°ããããšãããããããŸãã
ããã¯ãæŽæ°ãusize
ã«ãã£ã¹ãããŠã€ã³ããã¯ã¹ãšããŠäœ¿çšããå Žåã«äžè¬çã§ãã
Read
ãš Write
Read
ãš BufRead
ã䜿çšããããšã§ãu8
ãœãŒã¹ãæœè±¡åã§ããŸãã
use std::io::{BufRead, BufReader, Read, Result}; fn count_lines<R: Read>(reader: R) -> usize { let buf_reader = BufReader::new(reader); buf_reader.lines().count() } fn main() -> Result<()> { let slice: &[u8] = b"foo\nbar\nbaz\n"; println!("lines in slice: {}", count_lines(slice)); let file = std::fs::File::open(std::env::current_exe()?)?; println!("lines in file: {}", count_lines(file)); Ok(()) }
åæ§ã«ãWrite
ã䜿çšãããšãu8
ã·ã³ã¯ãæœè±¡åã§ããŸãã
use std::io::{Result, Write}; fn log<W: Write>(writer: &mut W, msg: &str) -> Result<()> { writer.write_all(msg.as_bytes())?; writer.write_all("\n".as_bytes()) } fn main() -> Result<()> { let mut buffer = Vec::new(); log(&mut buffer, "Hello")?; log(&mut buffer, "World")?; println!("Logged: {buffer:?}"); Ok(()) }
Default
ãã¬ã€ã
Default
ãã¬ã€ãã¯ãåã®ããã©ã«ãå€ãçæããŸãã
#[derive(Debug, Default)] struct Derived { x: u32, y: String, z: Implemented, } #[derive(Debug)] struct Implemented(String); impl Default for Implemented { fn default() -> Self { Self("John Smith".into()) } } fn main() { let default_struct = Derived::default(); println!("{default_struct:#?}"); let almost_default_struct = Derived { y: "Y is set!".into(), ..Derived::default() }; println!("{almost_default_struct:#?}"); let nothing: Option<Derived> = None; println!("{:#?}", nothing.unwrap_or_default()); }
- çŽæ¥å®è£
ããããšãã
#[derive(Default)]
ã§å°åºããããšãã§ããŸãã - å°åºã«ããå®è£
ã§ã¯ããã¹ãŠã®ãã£ãŒã«ããããã©ã«ãå€ã«èšå®ãããå€ãçæãããŸãã
- ã€ãŸããæ§é äœå
ã®ãã¹ãŠã®åã«ã
Default
ãå®è£ ããå¿ èŠããããŸãã
- ã€ãŸããæ§é äœå
ã®ãã¹ãŠã®åã«ã
- æšæºã® Rust åã¯å€ãã®å Žåã劥åœãªå€ïŒ
0
ã""
ãªã©ïŒã®Default
ãå®è£ ããŸãã - éšåçãªæ§é äœã®åæåã¯ãããã©ã«ãã§é©åã«æ©èœããŸãã
- Rust æšæºã©ã€ãã©ãªã¯ãåã
Default
ãå®è£ ã§ããããšãèªèããŠãããããã䜿çšããã³ã³ãããšã³ã¹ ã¡ãœãããæäŸããŠããŸãã ..
æ§æã¯ãæ§é äœæŽæ°èšæ³ãšåŒã°ããŠããŸãã
ã¯ããŒãžã£
ã¯ããŒãžã£ãã©ã ãåŒã«ã¯ãååãä»ããããšãã§ããªãåããããŸãããã ãããããã¯ç¹å¥ãª Fn
ãFnMut
ãFnOnce
ãã¬ã€ããåããŠããŸãã
fn apply_and_log(func: impl FnOnce(i32) -> i32, func_name: &str, input: i32) { println!("Calling {func_name}({input}): {}", func(input)) } fn main() { let n = 3; let add_3 = |x| x + n; apply_and_log(&add_3, "add_3", 10); apply_and_log(&add_3, "add_3", 20); let mut v = Vec::new(); let mut accumulate = |x: i32| { v.push(x); v.iter().sum::<i32>() }; apply_and_log(&mut accumulate, "accumulate", 4); apply_and_log(&mut accumulate, "accumulate", 5); let multiply_sum = |x| x * v.into_iter().sum::<i32>(); apply_and_log(multiply_sum, "multiply_sum", 3); }
An Fn
(e.g. add_3
) neither consumes nor mutates captured values. It can be called needing only a shared reference to the closure, which means the closure can be executed repeatedly and even concurrently.
An FnMut
(e.g. accumulate
) might mutate captured values. The closure object is accessed via exclusive reference, so it can be called repeatedly but not concurrently.
If you have an FnOnce
(e.g. multiply_sum
), you may only call it once. Doing so consumes the closure and any values captured by move.
FnMut
㯠FnOnce
ã®ãµãã¿ã€ãã§ãFn
㯠FnMut
ãš FnOnce
ã®ãµãã¿ã€ãã§ããã€ãŸããFnOnce
ãåŒã³åºãããå Žåã¯åžžã« FnMut
ã䜿çšã§ããFnMut
ãŸã㯠FnOnce
ãåŒã³åºãããå Žåã¯åžžã« Fn
ã䜿çšã§ããŸãã
ã¯ããŒãžã£ãåãåãé¢æ°ãå®çŸ©ããå Žåãå¯èœã§ããã°ïŒ1 åã ãåŒã³åºãïŒFnOnce
ã䜿çšãã次㫠FnMut
ãæåŸã« Fn
ã䜿çšããããã«ããŸããããã«ãããåŒã³åºãå
ã«æãæè»ã«å¯Ÿå¿ã§ããŸãã
In contrast, when you have a closure, the most flexible you can have is Fn
(which can be passed to a consumer of any of the 3 closure traits), then FnMut
, and lastly FnOnce
.
The compiler also infers Copy
(e.g. for add_3
) and Clone
(e.g. multiply_sum
), depending on what the closure captures. Function pointers (references to fn
items) implement Copy
and Fn
.
By default, closures will capture each variable from an outer scope by the least demanding form of access they can (by shared reference if possible, then exclusive reference, then by move). The move
keyword forces capture by value.
fn make_greeter(prefix: String) -> impl Fn(&str) { return move |name| println!("{} {}", prefix, name); } fn main() { let hi = make_greeter("Hi".to_string()); hi("Greg"); }
æŒç¿: ROT13æå·
ãã®äŸã§ã¯ãå€å žç㪠ãROT13ãæå·ãå®è£ ããŸãããã®ã³ãŒãããã¬ã€ã°ã©ãŠã³ãã«ã³ããŒããæ¬ èœããŠããããããå®è£ ããŠãã ãããçµæãæå¹ãª UTF-8 ã®ãŸãŸã«ãªãããã«ãASCII ã¢ã«ãã¡ãããæåã®ã¿ãããŒããŒã·ã§ã³ããŸãã
use std::io::Read; struct RotDecoder<R: Read> { input: R, rot: u8, } // `RotDecoder` ã® `Read` ãã¬ã€ããå®è£ ããŸãã fn main() { let mut rot = RotDecoder { input: "Gb trg gb gur bgure fvqr!".as_bytes(), rot: 13 }; let mut result = String::new(); rot.read_to_string(&mut result).unwrap(); println!("{}", result); } #[cfg(test)] mod test { use super::*; #[test] fn joke() { let mut rot = RotDecoder { input: "Gb trg gb gur bgure fvqr!".as_bytes(), rot: 13 }; let mut result = String::new(); rot.read_to_string(&mut result).unwrap(); assert_eq!(&result, "To get to the other side!"); } #[test] fn binary() { let input: Vec<u8> = (0..=255u8).collect(); let mut rot = RotDecoder::<&[u8]> { input: input.as_ref(), rot: 13 }; let mut buf = [0u8; 256]; assert_eq!(rot.read(&mut buf).unwrap(), 256); for i in 0..=255 { if input[i] != buf[i] { assert!(input[i].is_ascii_alphabetic()); assert!(buf[i].is_ascii_alphabetic()); } } } }
ããããã 13 æåãã€ããŒããŒã·ã§ã³ããã 2 ã€ã® RotDecoder
ã€ã³ã¹ã¿ã³ã¹ãé£çµãããšã©ããªãã§ããããã
解ç
use std::io::Read; struct RotDecoder<R: Read> { input: R, rot: u8, } impl<R: Read> Read for RotDecoder<R> { fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { let size = self.input.read(buf)?; for b in &mut buf[..size] { if b.is_ascii_alphabetic() { let base = if b.is_ascii_uppercase() { 'A' } else { 'a' } as u8; *b = (*b - base + self.rot) % 26 + base; } } Ok(size) } } fn main() { let mut rot = RotDecoder { input: "Gb trg gb gur bgure fvqr!".as_bytes(), rot: 13 }; let mut result = String::new(); rot.read_to_string(&mut result).unwrap(); println!("{}", result); } #[cfg(test)] mod test { use super::*; #[test] fn joke() { let mut rot = RotDecoder { input: "Gb trg gb gur bgure fvqr!".as_bytes(), rot: 13 }; let mut result = String::new(); rot.read_to_string(&mut result).unwrap(); assert_eq!(&result, "To get to the other side!"); } #[test] fn binary() { let input: Vec<u8> = (0..=255u8).collect(); let mut rot = RotDecoder::<&[u8]> { input: input.as_ref(), rot: 13 }; let mut buf = [0u8; 256]; assert_eq!(rot.read(&mut buf).unwrap(), 256); for i in 0..=255 { if input[i] != buf[i] { assert!(input[i].is_ascii_alphabetic()); assert!(buf[i].is_ascii_alphabetic()); } } } }
3 æ¥ç®ã®ãã¬ãŒãã³ã°ã«ãããã
æ¬æ¥ã®å 容:
- ã¡ã¢ãªç®¡çãã©ã€ãã¿ã€ã ãåçšãã§ãã«ãŒ: Rust ãã¡ã¢ãªã®å®å šæ§ã確ä¿ããä»çµã¿ã
- ã¹ããŒããã€ã³ã¿: æšæºã©ã€ãã©ãªã®ãã€ã³ã¿åã
ã¹ã±ãžã¥ãŒã«
Including 10 minute breaks, this session should take about 2 hours and 20 minutes. It contains:
Segment | Duration |
---|---|
ãããã | 3 minutes |
ã¡ã¢ãªç®¡ç | 1 hour |
ã¹ããŒããã€ã³ã¿ | 55 minutes |
ã¡ã¢ãªç®¡ç
This segment should take about 1 hour. It contains:
Slide | Duration |
---|---|
ããã°ã©ã ã¡ã¢ãªã®èŠçŽã | 5 minutes |
ã¡ã¢ãªç®¡çã®ã¢ãããŒã | 10 minutes |
æææš© | 5 minutes |
ã ãŒãã»ãã³ãã£ã¯ã¹ | 5 minutes |
Clone | 2 minutes |
Copy å | 5 minutes |
Drop | 10 minutes |
æŒç¿: ãã«ããŒå | 20 minutes |
ããã°ã©ã ã¡ã¢ãªã®èŠçŽã
ããã°ã©ã ã¯ã次㮠2 ã€ã®æ¹æ³ã§ã¡ã¢ãªãå²ãåœãŠãŸãã
-
ã¹ã¿ãã¯: ããŒã«ã«å€æ°çšã®é£ç¶ããã¡ã¢ãªé åã
- å€ã®ãµã€ãºã¯åºå®ãããŠãããã³ã³ãã€ã«æã«å€æããŠããŸãã
- éåžžã«é«é: ã¹ã¿ã㯠ãã€ã³ã¿ã移åããã ãã§ãã
- é¢æ°åŒã³åºãã«ãã£ãŠè¡ãããããã管çã容æã§ãã
- ã¡ã¢ãªå±ææ§ã«åªããŠããŸãã
-
ããŒã: é¢æ°åŒã³åºãã«äŸåããªãå€ã®ä¿æé åã
- å€ã®ãµã€ãºã¯åçã§ãå®è¡æã«æ±ºå®ãããŸãã
- ã¹ã¿ãã¯ããããäœéã§ãäœããã®ã®ããã¯ããŒãã³ã°ãå¿ èŠã§ãã
- ã¡ã¢ãªã®å±ææ§ãä¿èšŒãããŸããã
äŸ
String
ãäœæãããšãã¹ã¿ãã¯ã«ã¯åºå®ãµã€ãºã®ã¡ã¿ããŒã¿ãé
眮ãããããŒãã«ã¯ãµã€ãºãåçã«æ±ºå®ãããããŒã¿ïŒå®éã®æååïŒãé
眮ãããŸãã
fn main() { let s1 = String::from("Hello"); }
-
String
ã¯Vec
ã«ããå®çŸãããŠããããã容éãšé·ãããããå¯å€ã§ããã°ããŒãäžã®åå²ãåœãŠã«ãã£ãŠæ¡åŒµã§ããããšã説æããŸãã -
åè¬è ããå°ããããå Žåã¯ãã·ã¹ãã ã¢ãã±ãŒã¿ã䜿çšããŠã¡ã¢ãªé åãããŒãããå²ãåœãŠãããããšãAllocator API ã䜿çšããŠã«ã¹ã¿ã ã¢ãã±ãŒã¿ãå®è£ ã§ããããšã説æããŠãã ããã
ãã®ä»
unsafe
Rust ã䜿çšããŠã¡ã¢ãª ã¬ã€ã¢ãŠãã調ã¹ãããšãåºæ¥ãŸãããã ããããã¯åœç¶ãªããå®å
šã§ãªãããšãææããå¿
èŠããããŸãã
fn main() { let mut s1 = String::from("Hello"); s1.push(' '); s1.push_str("world"); // èªå® ã§ã¯è¡ããªãã§ãã ãããããã¯èª¬æã®ã¿ãç®çãšããŠããŸãã // String ã¯ãã®ã¬ã€ã¢ãŠããä¿èšŒããªããããæªå®çŸ©ã®åäœã // çºçããå¯èœæ§ããããŸãã unsafe { let (capacity, ptr, len): (usize, usize, usize) = std::mem::transmute(s1); println!("capacity = {capacity}, ptr = {ptr:#x}, len = {len}"); } }
ã¡ã¢ãªç®¡çã®ã¢ãããŒã
äŒçµ±çã«ãèšèªã¯å€§ãã 2 ã€ã®ã«ããŽãªã«åé¡ãããŸãã
- æåã§ã®ã¡ã¢ãªç®¡çã«ããå®å
šãªå¶åŸ¡: CãC++ãPascal ãªã©
- ããã°ã©ããŒãããŒãã¡ã¢ãªãå²ãåœãŠãŸãã¯è§£æŸããã¿ã€ãã³ã°ã決å®ããŸãã
- ããã°ã©ããŒã¯ããã€ã³ã¿ããŸã æå¹ãªã¡ã¢ãªãæããŠãããã©ãããå€æããå¿ èŠããããŸãã
- 調æ»ã«ãããšãããã°ã©ããŒã¯å€æã誀ãããšããããŸãã
- å®è¡æã®èªåã¡ã¢ãªç®¡çã«ããå®å
šãªå®å
šæ§: JavaãPythonãGoãHaskell ãªã©
- ã©ã³ã¿ã€ã ã·ã¹ãã ã«ãããã¡ã¢ãªã¯åç §ã§ããªããªããŸã§è§£æŸãããŸããã
- Typically implemented with reference counting or garbage collection.
Rust ã§ã¯ãã® 2 ã€ãèåããããšã§ãæ°ãã«ä»¥äžã®ç¹åŸŽãæäŸããŸãã
ã³ã³ãã€ã«æã®é©åãªã¡ã¢ãªç®¡çã®é©çšã«ãããå®å šãªå¶åŸ¡ãšå®å šæ§ã
ããã¯ãæ瀺çãªæææš©ã®æŠå¿µã«ãã£ãŠå®çŸãããŸãã
ãã®ã¹ã©ã€ãã¯ãä»ã®èšèªãç¿åŸæžã¿ã®åè¬è ã«ããã®æèã®äžã§ Rust ãç解ããŠãããããšãç®çãšããŠããŸãã
-
C ã§ã¯ã
malloc
ãšfree
ã䜿çšããŠããŒããæåã§ç®¡çããå¿ èŠããããŸãããããããšã©ãŒãšããŠã¯ãfree
ã®åŒã³åºããå¿ãããåããã€ã³ã¿ã«å¯ŸããŠè€æ°ååŒã³åºãããã€ã³ãããŠããã¡ã¢ãªã解æŸãããåŸã«ãã€ã³ã¿ãéåç §ããããªã©ããããŸãã -
C++ ã«ã¯ã¹ããŒã ãã€ã³ã¿ïŒ
unique_ptr
ãshared_ptr
ãªã©ïŒã®ããŒã«ãããããã¹ãã©ã¯ã¿ã®åŒã³åºãã«é¢ããèšèªä¿èšŒãå©çšããŠãé¢æ°ãæ»ã£ããšãã«ã¡ã¢ãªã解æŸãããããã«ããŸãããããã®ããŒã«ã誀çšã㊠C ãšåæ§ã®ãã°ãäœæããããšããããããŸãã -
JavaãGoãPythonã§ã¯ãã¢ã¯ã»ã¹ã§ããªããªã£ãã¡ã¢ãªã®ç¹å®ãšç Žæ£ãã¬ãŒããžã³ã¬ã¯ã¿ã«äŸåããŸããããã«ããããããããã€ã³ã¿ã®éåç §ãå¯èœã«ãªãã解æŸåŸã®äœ¿çšãªã©ã®ãã°ããªããªããŸãããã ããGC (ã¬ãŒããžã³ã¬ã¯ã·ã§ã³) ã«ã¯ã©ã³ã¿ã€ã ã³ã¹ããããããé©åãªãã¥ãŒãã³ã°ãå°é£ã§ãã
Rust ã®æææš©ãšåçšã¢ãã«ã¯ãå€ãã®å Žåãå²ãåœãŠãªãã¬ãŒã·ã§ã³ãšè§£æŸãªãã¬ãŒã·ã§ã³ãæ£ç¢ºã«å¿ èŠãªå Žæã§è¡ãããšã«ããããŒãã³ã¹ã㧠C ã®ããã©ãŒãã³ã¹ãå®çŸã§ããŸãããŸããC++ ã®ã¹ããŒããã€ã³ã¿ã«äŒŒãããŒã«ãçšæãããŠããŸããå¿ èŠã«å¿ããŠãåç §ã«ãŠã³ããªã©ã®ä»ã®ãªãã·ã§ã³ãå©çšã§ããŸãããŸããã©ã³ã¿ã€ã ã¬ããŒãžã³ã¬ã¯ã·ã§ã³ããµããŒãããããã®ãµãŒãããŒãã£ã®ã¯ã¬ãŒãã䜿çšã§ããŸãïŒãã®ã¯ã©ã¹ã§ã¯æ±ããŸããïŒã
æææš©
ãã¹ãŠã®å€æ°ãã€ã³ãã£ã³ã°ã«ã¯æå¹ãªã¹ã³ãŒãããããã¹ã³ãŒãå€ã§å€æ°ã䜿çšãããšãšã©ãŒã«ãªããŸãã
struct Point(i32, i32); fn main() { { let p = Point(3, 4); println!("x: {}", p.0); } println!("y: {}", p.1); }
ããããå€æ°ãå€ã ææ ããŠãããšè¡šçŸããŸãããã¹ãŠã® Rustã®å€ææè ã¯åžžã« 1 人ã§ãã
ã¹ã³ãŒãããå€ãããšå€æ°ãç Žæ£ (drop) ãããããŒã¿ã解æŸãããŸããããã§ãã¹ãã©ã¯ã¿ãå®è¡ããŠãªãœãŒã¹ã解æŸã§ããŸãã
ã¬ããŒãž ã³ã¬ã¯ã·ã§ã³ã®å®è£ ã«ç²ŸéããŠããåè¬è ã¯ãã¬ããŒãž ã³ã¬ã¯ã¿ãäžé£ã®ãã«ãŒããããéå§ããŠå°éå¯èœãªãã¹ãŠã®ã¡ã¢ãªãèŠã€ããããšãç¥ã£ãŠããŸããRust ã®ãåäžãªãŒããŒãã®ååããåæ§ã®èãæ¹ã«åºã¥ããŠããŸãã
ã ãŒãã»ãã³ãã£ã¯ã¹
ä»£å ¥ãããšãå€æ°é㧠æææš© ã移åããŸãã
fn main() { let s1: String = String::from("Hello!"); let s2: String = s1; println!("s2: {s2}"); // println!("s1: {s1}"); }
s1
ãs2
ã«ä»£å ¥ãããšãæææš©ã移åããŸããs1
ãã¹ã³ãŒãå€ã«ãªããšãäœãææããŠãªãããã§ãïŒäœãææããŸããïŒãs2
ãã¹ã³ãŒãå€ã«ãªããšãæååããŒã¿ã¯è§£æŸãããŸãã
s2
ã«ç§»åããå:
s2
ã«ç§»åããåŸ:
次ã®äŸã®ããã«ãé¢æ°ã«å€ãæž¡ããšããã®å€ã¯é¢æ°ãã©ã¡ãŒã¿ã«ä»£å ¥ãããŸããããã«ãããæææš©ã移åããŸãã
fn say_hello(name: String) { println!("Hello {name}") } fn main() { let name = String::from("Alice"); say_hello(name); // say_hello(name); }
-
ããã¯ã
std::move
ã䜿çšããªãéãïŒãã€ã ãŒã ã³ã³ã¹ãã©ã¯ã¿ãå®çŸ©ãããŠããªãéãïŒå€ãã³ããŒãããC++ ã®ããã©ã«ããšã¯éã§ããããšã説æããŸãã -
移åããã®ã¯æææš©ã®ã¿ã§ããããŒã¿èªäœãæäœããããã«ãã·ã³ã³ãŒããçæããããã©ããã¯æé©åã®åé¡ã§ããããã®ãããªã³ããŒã®ããã®ãã·ã³ã³ãŒãã¯ç©æ¥µçã«æé©åãããŠãªããªããŸãã
-
åçŽãªå€ïŒæŽæ°ãªã©ïŒã«ã¯
Copy
ã®ããŒã¯ãä»ããããšãã§ããŸãïŒåŸã®ã¹ã©ã€ããåç §ïŒã -
Rust ã§ã¯ãã¯ããŒã³ã¯æ瀺çã«
clone
ã䜿çšããŠè¡ãããŸãã
say_hello
ã®äŸã®å
容ã¯æ¬¡ã®ãšããã§ãã
say_hello
ã®æåã®åŒã³åºãã§ãmain
ã¯name
ã®æææš©ãæŸæ£ããŸãããã®åŸã¯main
å ã§name
ã䜿çšã§ããªããªããŸããname
ã«å²ãåœãŠãããããŒãã¡ã¢ãªã¯ãsay_hello
é¢æ°ã®æåŸã§è§£æŸãããŸããmain
ãname
ãåç §ãšããŠæž¡ãïŒ&name
ïŒãsay_hello
ããã©ã¡ãŒã¿ãšããŠåç §ãåãå ¥ããå Žåãmain
ã¯æææš©ãä¿æã§ããŸãã- ãŸãã¯ã
main
ãæåã®åŒã³åºãã§name
ã®ã¯ããŒã³ïŒname.clone()
ïŒãæž¡ãããšãã§ããŸãã - Rust ã§ã¯ãã ãŒã ã»ãã³ãã£ã¯ã¹ãããã©ã«ãã«ããã¯ããŒã³ãããã°ã©ãã«æ瀺çã«è¡ãããŠããŸããããã«ãããC++ ã«æ¯ã¹ãŠæå³ããã³ããŒãäœæãããªã¹ã¯ãäœæžãããŠããŸãã
ãã®ä»
Defensive Copies in Modern C++
ææ°ã® C++ ã§ã¯ããã®åé¡ãå¥ã®æ¹æ³ã§è§£æ±ºããŸãã
std::string s1 = "Cpp";
std::string s2 = s1; // s1 ã«ããŒã¿ãè€è£œããŸãã
s1
ããã®ããŒãããŒã¿ãè€è£œãããs2
ã¯èªèº«ã®ç¬ç«ããã³ããŒãååŸããŸããs1
ãšs2
ãã¹ã³ãŒãå€ã«ãªããšãããããèªèº«ã®ã¡ã¢ãªã解æŸããŸãã
ã³ããŒä»£å ¥å:
ã³ããŒä»£å ¥åŸ:
èŠç¹ïŒ
-
C++ ã®ã¢ãããŒãã¯ãRust ãšã¯è¥å¹²ç°ãªããŸãã
=
ã䜿çšãããšããŒã¿ãã³ããŒããããããæååããŒã¿ã®ã¯ããŒã³ãäœæããå¿ èŠãããããã§ããããããªããšãããããã®æååãã¹ã³ãŒãå€ã«ãªã£ããšãã«äºé解æŸãçºçããŸãã -
C++ ã«ã¯
std::move
ããããŸãããããã¯å€ãã ãŒãã§ããã¿ã€ãã³ã°ã瀺ãããã«äœ¿çšãããŸãããã®äŸã§s2 = std::move(s1)
ãšãªã£ãŠããå Žåã¯ãããŒãå²ãåœãŠã¯è¡ãããŸãããã ãŒãåŸãs1
ã¯æå¹ã§ãããã®ã®ãæªæå®ã®ç¶æ ã«ãªããŸããRust ãšã¯ç°ãªããããã°ã©ããŒã¯s1
ãåŒãç¶ã䜿çšã§ããŸãã -
Rust ãšã¯ç°ãªããC++ ã®
=
ã¯ãã³ããŒãŸãã¯ç§»åãããåã«ãã£ãŠæ±ºå®ãããä»»æã®ã³ãŒããå®è¡ã§ããŸãã
Clone
å€ã®ã³ããŒãäœæãããå Žåã¯ãClone
ãã¬ã€ãã䜿çšã§ããŸãã
fn say_hello(name: String) { println!("Hello {name}") } fn main() { let name = String::from("Alice"); say_hello(name.clone()); say_hello(name); }
-
The idea of
Clone
is to make it easy to spot where heap allocations are occurring. Look for.clone()
and a few others likevec!
orBox::new
. -
åçšãã§ãã«ãŒãéããªãå Žåã«ããšããããã¯ããŒã³ãäœæããŠåãæããŠãããŠããããšããã¯ããŒã³ã®ãªãã³ãŒããžã®æé©åãè©Šã¿ãã®ãããããããšã§ãã
-
clone
generally performs a deep copy of the value, meaning that if you e.g. clone an array, all of the elements of the array are cloned as well. -
The behavior for
clone
is user-defined, so it can perform custom cloning logic if needed.
Copy å
èšèªãšããŠã®ããã©ã«ãã¯ã ãŒãã»ãã³ãã£ã¯ã¹ã§ãããç¹å®ã®åã§ã¯ããã©ã«ãã§ã³ããŒãè¡ãããŸãã
fn main() { let x = 42; let y = x; println!("x: {x}"); // would not be accessible if not Copy println!("y: {y}"); }
ãããã®å㯠Copy
ãã¬ã€ããå®è£
ããŠããããã§ãã
ããªããå®çŸ©ããç¬èªã®åã®ããã©ã«ããã³ããŒã»ãã³ãã£ã¯ã¹ã«ããããšãåºæ¥ãŸãã
#[derive(Copy, Clone, Debug)] struct Point(i32, i32); fn main() { let p1 = Point(3, 4); let p2 = p1; println!("p1: {p1:?}"); println!("p2: {p2:?}"); }
- 代å
¥åŸã¯ã
p1
ãšp2
ã®äž¡æ¹ãç¬èªã®ããŒã¿ãææããŸãã p1.clone()
ã䜿çšããŠããŒã¿ãæ瀺çã«ã³ããŒããããšãã§ããŸãã
ã³ããŒãšã¯ããŒã³äœæã¯åãã§ã¯ãããŸããã
- ã³ããŒãšã¯ãã¡ã¢ãªé åã®ãããåäœã³ããŒã®ããšã§ãããä»»æã®ãªããžã§ã¯ãã§ã¯æ©èœããŸããã
- ã³ããŒã§ã¯ã«ã¹ã¿ã ããžãã¯ã¯äœ¿çšã§ããŸããïŒC++ ã®ã³ããŒã³ã³ã¹ãã©ã¯ã¿ãšã¯ç°ãªããŸãïŒã
- ã¯ããŒã³äœæã¯ããäžè¬çãªãªãã¬ãŒã·ã§ã³ã§ããã
Clone
ãã¬ã€ããå®è£ ããããšã§ã«ã¹ã¿ã åäœãå¯èœã«ãªããŸãã Drop
ãã¬ã€ããå®è£ ããŠããåã§ã¯ãã³ããŒã¯åºæ¥ãŸããã
äžèšã®äŸã§ã次ã®æ¹æ³ãè©ŠããŠãã ããã
String
ãã£ãŒã«ããstruct Point
ã«è¿œå ããŸããString
ãCopy
åã§ã¯ãªããããã³ã³ãã€ã«ã§ããªããªããŸããderive
å±æ§ããCopy
ãåé€ããŸããp1
ã®println!
ã§ã³ã³ãã€ã© ãšã©ãŒãçºçããŸãã- 代ããã«
p1
ã®ã¯ããŒã³ãäœæããã°è§£æ±ºã§ããããšã瀺ããŸãã
ãã®ä»
- Shared references are
Copy
/Clone
, mutable references are not. This is because Rust requires that mutable references be exclusive, so while it's valid to make a copy of a shared reference, creating a copy of a mutable reference would violate Rust's borrowing rules.
Drop
ãã¬ã€ã
Drop
ãå®è£
ããŠããå€ã§ã¯ãã¹ã³ãŒãããå€ãããšãã«å®è¡ããã³ãŒããæå®ã§ããŸãã
struct Droppable { name: &'static str, } impl Drop for Droppable { fn drop(&mut self) { println!("Dropping {}", self.name); } } fn main() { let a = Droppable { name: "a" }; { let b = Droppable { name: "b" }; { let c = Droppable { name: "c" }; let d = Droppable { name: "d" }; println!("Exiting block B"); } println!("Exiting block A"); } drop(a); println!("Exiting main"); }
std::mem::drop
ã¯std::ops::Drop::drop
ãšåãã§ã¯ãããŸããã- ã¹ã³ãŒãå€ã«ãªããšãå€ã¯èªåçã«ãããããããŸãã
- å€ããããããããéã
std::ops::Drop
ãå®è£ ããŠããå Žåã¯ããã®Drop::drop
å®è£ ãåŒã³åºãããŸãã - ãã®åŸã
Drop
ãå®è£ ããŠãããã©ããã«ãããããããã¹ãŠã®ãã£ãŒã«ãããããããããŸãã std::mem::drop
ã¯ãä»»æã®å€ãåãåã空ã®é¢æ°ã«ãããŸãããéèŠãªã®ã¯ããã®é¢æ°ãå€ã®æææš©ãååŸããããšã§ãã¹ã³ãŒãã®æåŸã§å€ããããããããããšã§ããããã¯ãã¹ã³ãŒãå€ã«ãªãåã«å€ãæ瀺çã«ããããããããã®äŸ¿å©ãªæ¹æ³ã§ãã- ãã®æ¹æ³ã¯ã
drop
ã§äœããã®åŠçïŒããã¯ã®è§£æŸããã¡ã€ã«ã®ã¯ããŒãºãªã©ïŒãè¡ããªããžã§ã¯ãã«äœ¿çšãããšäŸ¿å©ã§ãã
- ãã®æ¹æ³ã¯ã
è°è«ã®ãã€ã³ã:
Drop::drop
ãself
ããã©ã¡ãŒã¿ãšããŠåããªãã®ã¯ãªãã§ããïŒ- çãåç: ãã®å Žåããããã¯ã®æåŸã«
std::mem::drop
ãåŒã³åºããããããå¥ã®Drop::drop
ãåŒã³åºãããã¹ã¿ã㯠ãªãŒããŒãããŒãçºçããŸãã
- çãåç: ãã®å Žåããããã¯ã®æåŸã«
drop(a)
ãa.drop()
ã«çœ®ãæããŠã¿ãŠãã ããã
æŒç¿: ãã«ããŒå
ãã®äŸã§ã¯ããã¹ãŠã®ããŒã¿ãæã€è€éãªããŒã¿åãå®è£ ããŸããããã«ã㌠ãã¿ãŒã³ãã§äŸ¿å©ãªé¢æ°ã䜿çšããŠãæ°ããå€ã 1 ã€ãã€æ§ç¯ã§ããããã«ããŸãã
æããŠããéšåãèšå ¥ããŠãã ããã
#[derive(Debug)] enum Language { Rust, Java, Perl, } #[derive(Clone, Debug)] struct Dependency { name: String, version_expression: String, } /// ãœãããŠã§ã¢ ããã±ãŒãžã®è¡šçŸã #[derive(Debug)] struct Package { name: String, version: String, authors: Vec<String>, dependencies: Vec<Dependency>, language: Option<Language>, } impl Package { /// ãã®ããã±ãŒãžã®è¡šçŸãäŸåé¢ä¿ãšããŠè¿ãã /// ä»ã®ããã±ãŒãžã®ãã«ãã«äœ¿çšããŸãã fn as_dependency(&self) -> Dependency { todo!("1") } } /// ããã±ãŒãžã®ãã«ããŒã`build()` ã䜿çšã㊠`Package` èªäœãäœæããŸãã struct PackageBuilder(Package); impl PackageBuilder { fn new(name: impl Into<String>) -> Self { todo!("2") } /// ããã±ãŒãžã®ããŒãžã§ã³ãèšå®ããŸãã fn version(mut self, version: impl Into<String>) -> Self { self.0.version = version.into(); self } /// ããã±ãŒãžäœæè ãèšå®ããŸãã fn authors(mut self, authors: Vec<String>) -> Self { todo!("3") } /// äŸåé¢ä¿ãè¿œå ããŸãã fn dependency(mut self, dependency: Dependency) -> Self { todo!("4") } /// èšèªãèšå®ããŸããèšå®ããªãå Žåãèšèªã¯ããã©ã«ã㧠None ã«ãªããŸãã fn language(mut self, language: Language) -> Self { todo!("5") } fn build(self) -> Package { self.0 } } fn main() { let base64 = PackageBuilder::new("base64").version("0.13").build(); println!("base64: {base64:?}"); let log = PackageBuilder::new("log").version("0.4").language(Language::Rust).build(); println!("log: {log:?}"); let serde = PackageBuilder::new("serde") .authors(vec!["djmitche".into()]) .version(String::from("4.0")) .dependency(base64.as_dependency()) .dependency(log.as_dependency()) .build(); println!("serde: {serde:?}"); }
解ç
#[derive(Debug)] enum Language { Rust, Java, Perl, } #[derive(Clone, Debug)] struct Dependency { name: String, version_expression: String, } /// ãœãããŠã§ã¢ ããã±ãŒãžã®è¡šçŸã #[derive(Debug)] struct Package { name: String, version: String, authors: Vec<String>, dependencies: Vec<Dependency>, language: Option<Language>, } impl Package { /// ãã®ããã±ãŒãžã®è¡šçŸãäŸåé¢ä¿ãšããŠè¿ãã /// ä»ã®ããã±ãŒãžã®ãã«ãã«äœ¿çšããŸãã fn as_dependency(&self) -> Dependency { Dependency { name: self.name.clone(), version_expression: self.version.clone(), } } } /// ããã±ãŒãžã®ãã«ããŒã`build()` ã䜿çšã㊠`Package` èªäœãäœæããŸãã struct PackageBuilder(Package); impl PackageBuilder { fn new(name: impl Into<String>) -> Self { Self(Package { name: name.into(), version: "0.1".into(), authors: vec![], dependencies: vec![], language: None, }) } /// ããã±ãŒãžã®ããŒãžã§ã³ãèšå®ããŸãã fn version(mut self, version: impl Into<String>) -> Self { self.0.version = version.into(); self } /// ããã±ãŒãžäœæè ãèšå®ããŸãã fn authors(mut self, authors: Vec<String>) -> Self { self.0.authors = authors; self } /// äŸåé¢ä¿ãè¿œå ããŸãã fn dependency(mut self, dependency: Dependency) -> Self { self.0.dependencies.push(dependency); self } /// èšèªãèšå®ããŸããèšå®ããªãå Žåãèšèªã¯ããã©ã«ã㧠None ã«ãªããŸãã fn language(mut self, language: Language) -> Self { self.0.language = Some(language); self } fn build(self) -> Package { self.0 } } fn main() { let base64 = PackageBuilder::new("base64").version("0.13").build(); println!("base64: {base64:?}"); let log = PackageBuilder::new("log").version("0.4").language(Language::Rust).build(); println!("log: {log:?}"); let serde = PackageBuilder::new("serde") .authors(vec!["djmitche".into()]) .version(String::from("4.0")) .dependency(base64.as_dependency()) .dependency(log.as_dependency()) .build(); println!("serde: {serde:?}"); }
ã¹ããŒããã€ã³ã¿
This segment should take about 55 minutes. It contains:
Slide | Duration |
---|---|
Box | 10 minutes |
Rc | 5 minutes |
ææããããã¬ã€ããªããžã§ã¯ã | 10 minutes |
æŒç¿: ãã€ããªããªãŒ | 30 minutes |
Box<T>
Box
ã¯ãããŒãäžã®ããŒã¿ãžã®ææãã€ã³ã¿ã§ãã
fn main() { let five = Box::new(5); println!("five: {}", *five); }
Box<T>
㯠Deref<Target = T>
ãå®è£
ããŠãããããBox<T>
ã«å¯Ÿã㊠T ã®ã¡ãœãããçŽæ¥åŒã³åºãããšãã§ããŸãã
Recursive data types or data types with dynamic sizes cannot be stored inline without a pointer indirection. Box
accomplishes that indirection:
#[derive(Debug)] enum List<T> { /// 空ã§ãªããªã¹ã: æåã®èŠçŽ ãšãªã¹ãã®æ®ãã Element(T, Box<List<T>>), /// 空ã®ãªã¹ãã Nil, } fn main() { let list: List<i32> = List::Element(1, Box::new(List::Element(2, Box::new(List::Nil)))); println!("{list:?}"); }
-
Box
㯠C++ ã®std::unique_ptr
ãšäŒŒãŠããŸãããnull ã§ã¯ãªãããšãä¿èšŒãããŠããç¹ãç°ãªããŸãã -
Box
ã¯æ¬¡ã®ãããªå Žåã«åœ¹ç«ã¡ãŸãã- have a type whose size can't be known at compile time, but the Rust compiler wants to know an exact size.
- 倧éã®ããŒã¿ã®æææš©ãã ãŒããããå Žåãã¹ã¿ãã¯äžã®å€§éã®ããŒã¿ãã³ããŒãããªãããã«ããã«ã¯ã代ããã«
Box
ã«ããããŒãäžã«ããŒã¿ãæ ŒçŽãããã€ã³ã¿ã®ã¿ã移åãããããã«ããŸãã
-
ä»®ã«
Box
ã䜿çšããã«List
ãList
ã«çŽæ¥åã蟌ãããšãããšãã³ã³ãã€ã©ã¯ã¡ã¢ãªå ã®æ§é äœã®åºå®ãµã€ãºãèšç®ããããšããŸããïŒList
ã¯ç¡éãµã€ãºã«ãªããŸãïŒã -
Box
ããã®åé¡ã解決ã§ããã®ã¯ããã®ãµã€ãºãéåžžã®ãã€ã³ã¿ãšåãã§ãããåã«ããŒãå ã® List ã®æ¬¡ã®èŠçŽ ãæãã ãã ãã ããã§ãã -
Remove the
Box
in the List definition and show the compiler error. We get the message "recursive without indirection", because for data recursion, we have to use indirection, aBox
or reference of some kind, instead of storing the value directly. -
Though
Box
looks likestd::unique_ptr
in C++, it cannot be empty/null. This makesBox
one of the types that allow the compiler to optimize storage of some enums (the "niche optimization").
Rc
Rc
ã¯ãåç
§ã«ãŠã³ããããå
±æãã€ã³ã¿ã§ããè€æ°ã®å ŽæããåãããŒã¿ãåç
§ããå¿
èŠãããå Žåã«äœ¿çšããŸãã
use std::rc::Rc; fn main() { let a = Rc::new(10); let b = Rc::clone(&a); println!("a: {a}"); println!("b: {b}"); }
- See
Arc
andMutex
if you are in a multi-threaded context. - å
±æãã€ã³ã¿ã
Weak
ãã€ã³ã¿ã«ããŠã³ã°ã¬ãŒã (downgrade) ãããšãããããããããµã€ã¯ã«ãäœæã§ããŸãã
Rc
ã®ã«ãŠã³ãã¯ãåç §ãããéãæå¹ã§ããããšãä¿èšŒããŸãã- Rust ã®
Rc
㯠C++ ã®std::shared_ptr
ã«äŒŒãŠããŸãã Rc::clone
ã®åäœã¯è»œéã§ããåãå²ãåœãŠé åãžã®ãã€ã³ã¿ãäœæããåç §ã«ãŠã³ããå¢ããã ãã§ããããŒãã¯ããŒã³ãäœæããªãã®ã§ãæ§èœäžã®åé¡ç®æãã³ãŒãããæ¢ãå Žåã«ã¯éåžžç¡èŠããããšãåºæ¥ãŸããmake_mut
ã¯ãå¿ èŠã«å¿ããŠå éšã®å€ã®ã¯ããŒã³ãäœæãïŒãclone-on-writeãïŒãå¯å€åç §ãè¿ããŸããRc::strong_count
ã䜿çšããŠåç §ã«ãŠã³ãã確èªããŸããRc::downgrade
ã¯ãïŒå€ãã®å ŽåãRefCell
ãšçµã¿åãããŠïŒé©åã«ããããããããµã€ã¯ã«ãäœæããããã®åŒ±åç §ã«ãŠã³ã (weakly reference-counted) ãªããžã§ã¯ããæäŸããŸãã
ææããããã¬ã€ããªããžã§ã¯ã
We previously saw how trait objects can be used with references, e.g &dyn Pet
. However, we can also use trait objects with smart pointers like Box
to create an owned trait object: Box<dyn Pet>
.
struct Dog { name: String, age: i8, } struct Cat { lives: i8, } trait Pet { fn talk(&self) -> String; } impl Pet for Dog { fn talk(&self) -> String { format!("Woof, my name is {}!", self.name) } } impl Pet for Cat { fn talk(&self) -> String { String::from("Miau!") } } fn main() { let pets: Vec<Box<dyn Pet>> = vec![ Box::new(Cat { lives: 9 }), Box::new(Dog { name: String::from("Fido"), age: 5 }), ]; for pet in pets { println!("Hello, who are you? {}", pet.talk()); } }
pets
ãå²ãåœãŠãåŸã®ã¡ã¢ãªã¬ã€ã¢ãŠãïŒ
- åããã¬ã€ããå®è£
ããåã§ãã£ãŠããã®ãµã€ãºã¯ç°ãªãããšããããŸãããã®ãããäžã®äŸã§Vec
ãšæžãããšã¯ã§ããŸããã dyn Pet
ã¯ã³ã³ãã€ã©ã«ããã®åãPet
ãã¬ã€ããå®è£ ããåçãªãµã€ãºã®åã§ããããšãäŒããŸãã- äžã®äŸã§ã¯
pets
ã¯ã¹ã¿ãã¯ã«ç¢ºä¿ããããã¯ã¿ãŒã®ããŒã¿ã¯ããŒãäžã«ãããŸããäºã€ã®ãã¯ã¿ãŒã®èŠçŽ 㯠ãã¡ãããã€ã³ã¿ ã§ãïŒ- ãã¡ãããã€ã³ã¿ã¯double-widthãã€ã³ã¿ã§ããããã¯äºã€ã®èŠçŽ ãããªããŸãïŒå®éã®ãªããžã§ã¯ããžã®ãã€ã³ã¿ãšããã®ãªããžã§ã¯ãã®
Pet
ã®å®è£ ã®ããã®ä»®æ³é¢æ°ããŒãã« (vtable)ã§ãã - "Fido"ãšåä»ãããã
Dog
ã®ããŒã¿ã¯name
ãšage
ã®ãã£ãŒã«ãã«å¯Ÿå¿ããŸããïŒèš³æ³š: "Fido"ãšã¯ããããç¬ã®æ称ã§ãæ¥æ¬èªã§ããããããã®ãããªååã§ããïŒäŸã®Cat
ã«ã¯lives
ãã£ãŒã«ãããããŸããïŒèš³æ³š: ããã§Cat
ãlives
ãšãããã£ãŒã«ããæã¡ã9ã§åæåããŠããã®ã¯"A cat has nine lives" âç«ã¯ïŒã€ã®åœãæã€âãšããããšããã«ç±æ¥ããŸããïŒ
- ãã¡ãããã€ã³ã¿ã¯double-widthãã€ã³ã¿ã§ããããã¯äºã€ã®èŠçŽ ãããªããŸãïŒå®éã®ãªããžã§ã¯ããžã®ãã€ã³ã¿ãšããã®ãªããžã§ã¯ãã®
- äžã®äŸã«ãããŠãäžã®ã³ãŒãã«ããåºåçµæãæ¯ã¹ãŠã¿ãŸãããïŒ
println!("{} {}", std::mem::size_of::<Dog>(), std::mem::size_of::<Cat>()); println!("{} {}", std::mem::size_of::<&Dog>(), std::mem::size_of::<&Cat>()); println!("{}", std::mem::size_of::<&dyn Pet>()); println!("{}", std::mem::size_of::<Box<dyn Pet>>());
æŒç¿: ãã€ããªããªãŒ
ãã€ããªããªãŒã¯ããã¹ãŠã®ããŒãã« 2 ã€ã®åïŒå·Šãšå³ïŒãããããªãŒåã®ããŒã¿æ§é ã§ããããã§ã¯ãåããŒããå€ãæ ŒçŽããããªãŒãäœæããŸããããç¹å®ã®ããŒã N ã«ã€ããŠãN ã®å·ŠåŽã®ãµãããªãŒå ã®ãã¹ãŠã®ããŒãã«ã¯ããå°ããå€ãå«ãŸããN ã®å³åŽã®ãµãããªãŒå ã®ãã¹ãŠã®ããŒãã«ã¯ãã倧ããå€ãå«ãŸããŸãã
次ã®åãå®è£ ããŠãæå®ããããã¹ããéãããã«ããŸãã
è¿œå ã®å®ç¿: ãã€ããªããªãŒã«å€ãé çªã«è¿ãã€ãã¬ãŒã¿ãå®è£ ããŸãã
/// ãã€ããªããªãŒã®ããŒãã
#[derive(Debug)]
struct Node<T: Ord> {
value: T,
left: Subtree<T>,
right: Subtree<T>,
}
/// 空ã®å¯èœæ§ã®ãããµãããªãŒã
#[derive(Debug)]
struct Subtree<T: Ord>(Option<Box<Node<T>>>);
/// ãã€ããªããªãŒã䜿çšããŠäžé£ã®å€ãæ ŒçŽããã³ã³ããã
///
/// åãå€ãè€æ°åè¿œå ãããå Žåããã®å€ã¯ 1 åã ãæ ŒçŽãããã
#[derive(Debug)]
pub struct BinaryTree<T: Ord> {
root: Subtree<T>,
}
impl<T: Ord> BinaryTree<T> {
fn new() -> Self {
Self { root: Subtree::new() }
}
fn insert(&mut self, value: T) {
self.root.insert(value);
}
fn has(&self, value: &T) -> bool {
self.root.has(value)
}
fn len(&self) -> usize {
self.root.len()
}
}
// Implement `new`, `insert`, `len`, and `has` for `Subtree`.
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn len() {
let mut tree = BinaryTree::new();
assert_eq!(tree.len(), 0);
tree.insert(2);
assert_eq!(tree.len(), 1);
tree.insert(1);
assert_eq!(tree.len(), 2);
tree.insert(2); // åºæã®ã¢ã€ãã ã§ã¯ãªã
assert_eq!(tree.len(), 2);
}
#[test]
fn has() {
let mut tree = BinaryTree::new();
fn check_has(tree: &BinaryTree<i32>, exp: &[bool]) {
let got: Vec<bool> =
(0..exp.len()).map(|i| tree.has(&(i as i32))).collect();
assert_eq!(&got, exp);
}
check_has(&tree, &[false, false, false, false, false]);
tree.insert(0);
check_has(&tree, &[true, false, false, false, false]);
tree.insert(4);
check_has(&tree, &[true, false, false, false, true]);
tree.insert(4);
check_has(&tree, &[true, false, false, false, true]);
tree.insert(3);
check_has(&tree, &[true, false, false, true, true]);
}
#[test]
fn unbalanced() {
let mut tree = BinaryTree::new();
for i in 0..100 {
tree.insert(i);
}
assert_eq!(tree.len(), 100);
assert!(tree.has(&50));
}
}
解ç
use std::cmp::Ordering; /// ãã€ããªããªãŒã®ããŒãã #[derive(Debug)] struct Node<T: Ord> { value: T, left: Subtree<T>, right: Subtree<T>, } /// 空ã®å¯èœæ§ã®ãããµãããªãŒã #[derive(Debug)] struct Subtree<T: Ord>(Option<Box<Node<T>>>); /// ãã€ããªããªãŒã䜿çšããŠäžé£ã®å€ãæ ŒçŽããã³ã³ããã /// /// åãå€ãè€æ°åè¿œå ãããå Žåããã®å€ã¯ 1 åã ãæ ŒçŽãããã #[derive(Debug)] pub struct BinaryTree<T: Ord> { root: Subtree<T>, } impl<T: Ord> BinaryTree<T> { fn new() -> Self { Self { root: Subtree::new() } } fn insert(&mut self, value: T) { self.root.insert(value); } fn has(&self, value: &T) -> bool { self.root.has(value) } fn len(&self) -> usize { self.root.len() } } impl<T: Ord> Subtree<T> { fn new() -> Self { Self(None) } fn insert(&mut self, value: T) { match &mut self.0 { None => self.0 = Some(Box::new(Node::new(value))), Some(n) => match value.cmp(&n.value) { Ordering::Less => n.left.insert(value), Ordering::Equal => {} Ordering::Greater => n.right.insert(value), }, } } fn has(&self, value: &T) -> bool { match &self.0 { None => false, Some(n) => match value.cmp(&n.value) { Ordering::Less => n.left.has(value), Ordering::Equal => true, Ordering::Greater => n.right.has(value), }, } } fn len(&self) -> usize { match &self.0 { None => 0, Some(n) => 1 + n.left.len() + n.right.len(), } } } impl<T: Ord> Node<T> { fn new(value: T) -> Self { Self { value, left: Subtree::new(), right: Subtree::new() } } } fn main() { let mut tree = BinaryTree::new(); tree.insert("foo"); assert_eq!(tree.len(), 1); tree.insert("bar"); assert!(tree.has(&"foo")); } #[cfg(test)] mod tests { use super::*; #[test] fn len() { let mut tree = BinaryTree::new(); assert_eq!(tree.len(), 0); tree.insert(2); assert_eq!(tree.len(), 1); tree.insert(1); assert_eq!(tree.len(), 2); tree.insert(2); // åºæã®ã¢ã€ãã ã§ã¯ãªã assert_eq!(tree.len(), 2); } #[test] fn has() { let mut tree = BinaryTree::new(); fn check_has(tree: &BinaryTree<i32>, exp: &[bool]) { let got: Vec<bool> = (0..exp.len()).map(|i| tree.has(&(i as i32))).collect(); assert_eq!(&got, exp); } check_has(&tree, &[false, false, false, false, false]); tree.insert(0); check_has(&tree, &[true, false, false, false, false]); tree.insert(4); check_has(&tree, &[true, false, false, false, true]); tree.insert(4); check_has(&tree, &[true, false, false, false, true]); tree.insert(3); check_has(&tree, &[true, false, false, true, true]); } #[test] fn unbalanced() { let mut tree = BinaryTree::new(); for i in 0..100 { tree.insert(i); } assert_eq!(tree.len(), 100); assert!(tree.has(&50)); } }
ãããã
Including 10 minute breaks, this session should take about 1 hour and 55 minutes. It contains:
Segment | Duration |
---|---|
åçš | 55 minutes |
ã©ã€ãã¿ã€ã | 50 minutes |
åçš
This segment should take about 55 minutes. It contains:
Slide | Duration |
---|---|
å€ã®åçš | 10 minutes |
åçšãã§ã㯠| 10 minutes |
Borrow Errors | 3 minutes |
å éšå¯å€æ§ | 10 minutes |
æŒç¿: å¥åº·ã«é¢ããçµ±èš | 20 minutes |
å€ã®åçš
åã«èª¬æããããã«ãé¢æ°ãåŒã³åºããšãã«æææš©ã移åãã代ããã«ãé¢æ°ã§å€ãåçšã§ããŸãã
#[derive(Debug)] struct Point(i32, i32); fn add(p1: &Point, p2: &Point) -> Point { Point(p1.0 + p2.0, p1.1 + p2.1) } fn main() { let p1 = Point(3, 4); let p2 = Point(10, 20); let p3 = add(&p1, &p2); println!("{p1:?} + {p2:?} = {p3:?}"); }
add
é¢æ°ã¯ 2 ã€ã®ãã€ã³ãã åçš ããæ°ãããã€ã³ããè¿ããŸãã- åŒã³åºãå ã¯å ¥åã®æææš©ãä¿æããŸãã
ãã®ã¹ã©ã€ãã§ã¯ã1 æ¥ç®ã®åç §ã«é¢ããè³æãæ¯ãè¿ãã§ãããå°ã察象ãåºããé¢æ°ã®åŒæ°ãšæ»ãå€ãå«ããŠããŸãã
ãã®ä»
Notes on stack returns and inlining:
-
Demonstrate that the return from
add
is cheap because the compiler can eliminate the copy operation, by inlining the call to add into main. Change the above code to print stack addresses and run it on the Playground or look at the assembly in Godbolt. In the "DEBUG" optimization level, the addresses should change, while they stay the same when changing to the "RELEASE" setting:#[derive(Debug)] struct Point(i32, i32); fn add(p1: &Point, p2: &Point) -> Point { let p = Point(p1.0 + p2.0, p1.1 + p2.1); println!("&p.0: {:p}", &p.0); p } pub fn main() { let p1 = Point(3, 4); let p2 = Point(10, 20); let p3 = add(&p1, &p2); println!("&p3.0: {:p}", &p3.0); println!("{p1:?} + {p2:?} = {p3:?}"); }
-
The Rust compiler can do automatic inlining, that can be disabled on a function level with
#[inline(never)]
. -
Once disabled, the printed address will change on all optimization levels. Looking at Godbolt or Playground, one can see that in this case, the return of the value depends on the ABI, e.g. on amd64 the two i32 that is making up the point will be returned in 2 registers (eax and edx).
åçšãã§ãã¯
Rust ã® åçšãã§ãã«ãŒ ã¯ãå€ãåçšããæ¹æ³ã«å¶éãèšããŸããä»»æã®å€ã«å¯ŸããŠãåžžã«æ¬¡ã®å¶éã課ãããŸãã
- å€ãžã®å ±æåç §ã 1 ã€ä»¥äžæã€ããšãåºæ¥ãŸãããŸãã¯ã
- å€ãžã®æä»åç §ã 1 ã€ã ãæã€ããšãåºæ¥ãŸãã
fn main() { let mut a: i32 = 10; let b: &i32 = &a; { let c: &mut i32 = &mut a; *c = 20; } println!("a: {a}"); println!("b: {b}"); }
- èŠä»¶ã¯ã競åããåç §ãåãæç¹ã«ååšããªãããšã§ããåç §ãã©ãã§å€ãããŠããŠãæ§ããŸããã
- äžèšã®ã³ãŒãã¯ã
a
ãc
ãéããŠå¯å€ãšããŠåçšãããŠãããšåæã«ãb
ãéããŠäžå€ãšããŠåçšãããŠãããããã³ã³ãã€ã«ã§ããŸããã b
ã®println!
ã¹ããŒãã¡ã³ããc
ãå°å ¥ããã¹ã³ãŒãã®åã«ç§»åããŠãã³ãŒããã³ã³ãã€ã«åºæ¥ãããã«ããŸãã- ãã®å€æŽåŸãã³ã³ãã€ã©ã¯ c ãéããa ã®å¯å€åç §ãããåã«ããbã䜿ãããŠããªãããšãèªèããŸããããã¯ããã³ã¬ãã·ã«ã« ã©ã€ãã¿ã€ã ("non-lexical lifetimes")ããšåŒã°ããåçšãã§ãã«ãŒã®æ©èœã§ãã
- æä»åç §å¶çŽã¯éåžžã«åŒ·åã§ããRust ã¯ãã®å¶çŽã䜿çšããŠãããŒã¿ãžã®ç«¶åãçºçããªãããã«ãããšãšãã«ãã³ãŒããæé©åããŠããŸããããšãã°ãå ±æåç §ãéããŠåŸãããå€ã¯ããã®åç §ãåç¶ããéãå®å šã«ã¬ãžã¹ã¿ã«ãã£ãã·ã¥ããããšãåºæ¥ãŸã
- åçšãã§ãã«ãŒã¯ãæ§é äœå ã®ç°ãªããã£ãŒã«ããžã®æä»åç §ãåæã«ååŸãããªã©ãå€ãã®äžè¬çãªãã¿ãŒã³ã«å¯Ÿå¿ããããã«èšèšãããŠããŸããããããç¶æ³ã«ãã£ãŠã¯åçšãã§ãã«ãŒãã³ãŒããæ£ããç解ã§ããããåçšãã§ãã«ãŒãšã®æŠããã«çºå±ããããšãå€ããããŸãã
Borrow Errors
As a concrete example of how these borrowing rules prevent memory errors, consider the case of modifying a collection while there are references to its elements:
fn main() { let mut vec = vec![1, 2, 3, 4, 5]; let elem = &vec[2]; vec.push(6); println!("{elem}"); }
Similarly, consider the case of iterator invalidation:
fn main() { let mut vec = vec![1, 2, 3, 4, 5]; for elem in &vec { vec.push(elem * 2); } }
- In both of these cases, modifying the collection by pushing new elements into it can potentially invalidate existing references to the collection's elements if the collection has to reallocate.
å éšå¯å€æ§
å Žåã«ãã£ãŠã¯ãå ±æïŒèªã¿åãå°çšïŒåç §ã®èåŸã«ããããŒã¿ãå€æŽããå¿ èŠããããŸããããšãã°ãå ±æããŒã¿æ§é ã«å éšãã£ãã·ã¥ãããããã®ãã£ãã·ã¥ãèªã¿åãå°çšã¡ãœããããæŽæ°ããå¿ èŠãããå ŽåããããŸãã
ãå éšå¯å€æ§ããã¿ãŒã³ã¯ãå ±æåç §ãéããæä»çïŒå¯å€ïŒã¢ã¯ã»ã¹ãå¯èœã«ããŸããæšæºã©ã€ãã©ãªã«ã¯ããããå®å šã«è¡ãããã®æ¹æ³ãããã€ãçšæãããŠãããéåžžã¯ã©ã³ã¿ã€ã ãã§ãã¯ãå®è¡ããããšã§å®å šæ§ã確ä¿ããŸãã
Cell
Cell
wraps a value and allows getting or setting the value using only a shared reference to the Cell
. However, it does not allow any references to the inner value. Since there are no references, borrowing rules cannot be broken.
use std::cell::Cell; fn main() { // Note that `cell` is NOT declared as mutable. let cell = Cell::new(5); cell.set(123); println!("{}", cell.get()); }
RefCell
RefCell
allows accessing and mutating a wrapped value by providing alternative types Ref
and RefMut
that emulate &T
/&mut T
without actually being Rust references.
These types perform dynamic checks using a counter in the RefCell
to prevent existence of a RefMut
alongside another Ref
/RefMut
.
By implementing Deref
(and DerefMut
for RefMut
), these types allow calling methods on the inner value without allowing references to escape.
use std::cell::RefCell; fn main() { // Note that `cell` is NOT declared as mutable. let cell = RefCell::new(5); { let mut cell_ref = cell.borrow_mut(); *cell_ref = 123; // This triggers an error at runtime. // let other = cell.borrow(); // println!("{}", *other); } println!("{cell:?}"); }
ãã®ã¹ã©ã€ãã§éèŠãªã®ã¯ãRust ã«ã¯ãå
񑆇
§ã®èåŸã«ããããŒã¿ãå€æŽããå®å
šãªæ¹æ³ãçšæãããŠãããšããããšã§ããå®å
šæ§ã確ä¿ããã«ã¯ããŸããŸãªæ¹æ³ããããŸãããããã§ã¯ RefCell
ãš Cell
ãåãäžããŸãã
-
RefCell
ã¯ãã©ã³ã¿ã€ã ãã§ãã¯ãšãšãã« Rust ã®éåžžã®åçšã«ãŒã«ïŒè€æ°ã®å ±æåç §ãŸãã¯åäžã®æä»åç §ïŒãé©çšããŸãããã®å Žåããã¹ãŠã®åçšã¯éåžžã«çããéè€ããªãããããã§ãã¯ã¯åžžã«æåããŸãã- The extra block in the
RefCell
example is to end the borrow created by the call toborrow_mut
before we print the cell. Trying to print a borrowedRefCell
just shows the message"{borrowed}"
.
- The extra block in the
-
Cell
ã¯å®å šæ§ã確ä¿ããããã®ããã·ã³ãã«ãªæ段ã§ããã&self
ãåãåãset
ã¡ãœãããåããŠããŸããã©ã³ã¿ã€ã ãã§ãã¯ã¯å¿ èŠãããŸããããå€ã移åããå¿ èŠããããããã«ãã£ãŠã³ã¹ããçºçããããšããããŸãã -
Both
RefCell
andCell
are!Sync
, which means&RefCell
and&Cell
can't be passed between threads. This prevents two threads trying to access the cell at once.
æŒç¿: å¥åº·ã«é¢ããçµ±èš
å¥åº·ç®¡çã·ã¹ãã ã®å®è£ ã®äžç°ãšããŠããŠãŒã¶ãŒã®å¥åº·ã«é¢ããçµ±èšæ å ±ã远跡ããå¿ èŠããããŸãã
impl
ãããã¯ã®ã¹ã¿ãé¢æ°ãšãUser
æ§é äœã®å®çŸ©ãããç¶æ
ããéå§ããŸããUser
æ§é äœã® impl
ãããã¯ã«ãããŠã¹ã¿ãåãããé¢æ°ãå®è£
ããããšã§ãã
以äžã®ã³ãŒãã https://play.rust-lang.org/ ã«ã³ããŒããå®äœããªãã¡ãœããã®äžèº«ãå®è£ ããŸãã
// TODO: å®è£ ãå®äºãããããããåé€ããŸãã #![allow(unused_variables, dead_code)] #![allow(dead_code)] pub struct User { name: String, age: u32, height: f32, visit_count: usize, last_blood_pressure: Option<(u32, u32)>, } pub struct Measurements { height: f32, blood_pressure: (u32, u32), } pub struct HealthReport<'a> { patient_name: &'a str, visit_count: u32, height_change: f32, blood_pressure_change: Option<(i32, i32)>, } impl User { pub fn new(name: String, age: u32, height: f32) -> Self { Self { name, age, height, visit_count: 0, last_blood_pressure: None } } pub fn visit_doctor(&mut self, measurements: Measurements) -> HealthReport { todo!("Update a user's statistics based on measurements from a visit to the doctor") } } fn main() { let bob = User::new(String::from("Bob"), 32, 155.2); println!("I'm {} and my age is {}", bob.name, bob.age); } #[test] fn test_visit() { let mut bob = User::new(String::from("Bob"), 32, 155.2); assert_eq!(bob.visit_count, 0); let report = bob.visit_doctor(Measurements { height: 156.1, blood_pressure: (120, 80) }); assert_eq!(report.patient_name, "Bob"); assert_eq!(report.visit_count, 1); assert_eq!(report.blood_pressure_change, None); assert!((report.height_change - 0.9).abs() < 0.00001); let report = bob.visit_doctor(Measurements { height: 156.1, blood_pressure: (115, 76) }); assert_eq!(report.visit_count, 2); assert_eq!(report.blood_pressure_change, Some((-5, -4))); assert_eq!(report.height_change, 0.0); }
解ç
#![allow(dead_code)] pub struct User { name: String, age: u32, height: f32, visit_count: usize, last_blood_pressure: Option<(u32, u32)>, } pub struct Measurements { height: f32, blood_pressure: (u32, u32), } pub struct HealthReport<'a> { patient_name: &'a str, visit_count: u32, height_change: f32, blood_pressure_change: Option<(i32, i32)>, } impl User { pub fn new(name: String, age: u32, height: f32) -> Self { Self { name, age, height, visit_count: 0, last_blood_pressure: None } } pub fn visit_doctor(&mut self, measurements: Measurements) -> HealthReport { self.visit_count += 1; let bp = measurements.blood_pressure; let report = HealthReport { patient_name: &self.name, visit_count: self.visit_count as u32, height_change: measurements.height - self.height, blood_pressure_change: match self.last_blood_pressure { Some(lbp) => { Some((bp.0 as i32 - lbp.0 as i32, bp.1 as i32 - lbp.1 as i32)) } None => None, }, }; self.height = measurements.height; self.last_blood_pressure = Some(bp); report } } fn main() { let bob = User::new(String::from("Bob"), 32, 155.2); println!("I'm {} and my age is {}", bob.name, bob.age); } #[test] fn test_visit() { let mut bob = User::new(String::from("Bob"), 32, 155.2); assert_eq!(bob.visit_count, 0); let report = bob.visit_doctor(Measurements { height: 156.1, blood_pressure: (120, 80) }); assert_eq!(report.patient_name, "Bob"); assert_eq!(report.visit_count, 1); assert_eq!(report.blood_pressure_change, None); assert!((report.height_change - 0.9).abs() < 0.00001); let report = bob.visit_doctor(Measurements { height: 156.1, blood_pressure: (115, 76) }); assert_eq!(report.visit_count, 2); assert_eq!(report.blood_pressure_change, Some((-5, -4))); assert_eq!(report.height_change, 0.0); }
ã©ã€ãã¿ã€ã
This segment should take about 50 minutes. It contains:
Slide | Duration |
---|---|
é¢æ°ãšã©ã€ãã¿ã€ã | 10 minutes |
ã©ã€ãã¿ã€ã ã®çç¥ | 5 minutes |
æ§é äœã®ã©ã€ãã¿ã€ã | 5 minutes |
æŒç¿: Protobufã®è§£æ | 30 minutes |
é¢æ°ãšã©ã€ãã¿ã€ã
åç §ã«ã¯ã©ã€ãã¿ã€ã ããããããã¯åç §ããå€ããããé·ãåç¶ãããŠã¯ãªããŸãããããã¯åçšãã§ãã«ãŒã«ãã£ãŠæ€èšŒãããŸãã
ãããŸã§èŠãŠãããšãããã©ã€ãã¿ã€ã ã¯æé»ã«æ±ããŸããã&'a Point
ã&'document str
ã®ããã«æ瀺çã«æå®ããããšãã§ããŸããã©ã€ãã¿ã€ã 㯠'
ã§å§ãŸãã'a
ãäžè¬çãªããã©ã«ãåã§ãã&'a Point
ã¯ããå°ãªããšãã©ã€ãã¿ã€ã a
ã®éã¯æå¹ãªãåçšãã Point
ããšãšè§£éããŸãã
ã©ã€ãã¿ã€ã ã¯åžžã«ã³ã³ãã€ã©ã«ãã£ãŠæšæž¬ãããŸããèªåã§ã©ã€ãã¿ã€ã ãå²ãåœãŠãããšã¯ã§ããŸãããæ瀺çãªã©ã€ãã¿ã€ã ã¢ãããŒã·ã§ã³ã䜿çšãããšããããŸããªãšããã«å¶çŽã課ãããšãã§ããŸããããã«å¯Ÿããã³ã³ãã€ã©ã¯ãã®å¶çŽãæºããã©ã€ãã¿ã€ã ãèšå®ã§ããããšãæ€èšŒããŸãã
é¢æ°ã«å€ãæž¡ããé¢æ°ããå€ãè¿ãããšãèæ ®ããå Žåãã©ã€ãã¿ã€ã ã¯ããè€éã«ãªããŸãã
#[derive(Debug)] struct Point(i32, i32); fn left_most(p1: &Point, p2: &Point) -> &Point { if p1.0 < p2.0 { p1 } else { p2 } } fn main() { let p1: Point = Point(10, 10); let p2: Point = Point(20, 20); let p3 = left_most(&p1, &p2); // p3 ã®ã©ã€ãã¿ã€ã ã¯ïŒ println!("p3: {p3:?}"); }
ãã®äŸã§ã¯ãã³ã³ãã€ã©ã¯ p3
ã®ã©ã€ãã©ã€ã ãæšæž¬ããããåºæ¥ãŸããããé¢æ°æ¬äœã®å
éšãèŠããšãp3
ã®ã©ã€ãã¿ã€ã 㯠p1
ãš p2
ã®ããããçããããæ³å®ã§ããããšãããããŸãããã ããåãšåæ§ã«ãRust ã§ã¯é¢æ°ã®åŒæ°ãæ»ãå€ã«ã©ã€ãã¿ã€ã ã®æ瀺çãªã¢ãããŒã·ã§ã³ãå¿
èŠã§ãã
left_most
ã« 'a
ãé©åã«è¿œå ããŸãã
fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {
ããã¯ããp1 ãš p2 ã®äž¡æ¹ã 'a
ããé·ãåç¶ãããšãæ»ãå€ã¯å°ãªããšã 'a
ã®éåç¶ããããšããæå³ã«ãªããŸãã
äžè¬çãªã±ãŒã¹ã§ã¯ã次ã®ã¹ã©ã€ãã§èª¬æããããã«ã©ã€ãã¿ã€ã ãçç¥ã§ããŸãã
é¢æ°ãšã©ã€ãã¿ã€ã
é¢æ°ã®åŒæ°ãæ»ãå€ã®ã©ã€ãã¿ã€ã ã¯å®å šã«æå®ããå¿ èŠããããŸãããRust ã§ã¯ã»ãšãã©ã®å Žåãããã€ãã®ç°¡åãªã«ãŒã«ã«ãããã©ã€ãã¿ã€ã ãçç¥ã§ããŸããããã¯æšè«ã§ã¯ãªããæ§æã®çç¥åœ¢ã«ãããŸããã
- ã©ã€ãã¿ã€ã ã¢ãããŒã·ã§ã³ãä»ããŠããªãååŒæ°ã«ã¯ã1 ã€ã®ã©ã€ãã¿ã€ã ãäžããããŸãã
- åŒæ°ã®ã©ã€ãã¿ã€ã ã 1 ã€ãããªãå Žåãã¢ãããŒã·ã§ã³ã®ãªãæ»ãå€ãã¹ãŠã«ãã®ã©ã€ãã¿ã€ã ãäžããããŸãã
- åŒæ°ã®ã©ã€ãã¿ã€ã ãè€æ°ãããæåã®ã©ã€ãã¿ã€ã ã
self
ã§ããå Žåãã¢ãããŒã·ã§ã³ã®ãªãæ»ãå€ãã¹ãŠã«ãã®ã©ã€ãã¿ã€ã ãäžããããŸãã
#[derive(Debug)] struct Point(i32, i32); fn cab_distance(p1: &Point, p2: &Point) -> i32 { (p1.0 - p2.0).abs() + (p1.1 - p2.1).abs() } fn nearest<'a>(points: &'a [Point], query: &Point) -> Option<&'a Point> { let mut nearest = None; for p in points { if let Some((_, nearest_dist)) = nearest { let dist = cab_distance(p, query); if dist < nearest_dist { nearest = Some((p, dist)); } } else { nearest = Some((p, cab_distance(p, query))); }; } nearest.map(|(p, _)| p) } fn main() { let points = &[Point(1, 0), Point(1, 0), Point(-1, 0), Point(0, -1)]; println!("{:?}", nearest(points, &Point(0, 2))); }
ãã®äŸã§ã¯ãcab_distance
ã«é¢ããã©ã€ãã¿ã€ã ã®èšè¿°ã¯çç¥ãããŠããŸãã
nearest
é¢æ°ã¯ãæ瀺çãªã¢ãããŒã·ã§ã³ãå¿
èŠãšããè€æ°ã®åç
§ãåŒæ°ã«å«ãé¢æ°ã®ããäžã€ã®äŸã§ãã
è¿ãããã©ã€ãã¿ã€ã ã«ã€ããŠåã®ã¢ãããŒã·ã§ã³ãä»ããããã«ã·ã°ããã£ã調æŽããŠã¿ãŸãããã
fn nearest<'a, 'q>(points: &'a [Point], query: &'q Point) -> Option<&'q Point> {
ãããããšã³ã³ãã€ã«ãéããªããªããŸããããã¯ãããªãã¡ãã³ã³ãã€ã©ãã¢ãããŒã·ã§ã³ã®åŠ¥åœæ§ããã§ãã¯ããŠãããšããããšã瀺ããã®ã§ãããã ããããã¯çã®ãã€ã³ã¿ïŒå®å šã§ã¯ãªãïŒã«ã¯åœãŠã¯ãŸããŸãããã¢ã³ã»ãŒãRustã䜿çšããå Žåã«ãããã¯ãããããšã©ãŒã®åå ãšãªã£ãŠããŸãã
ã©ã€ãã¿ã€ã ãã©ã®ãããªå Žåã«äœ¿ãã¹ãããåè¬è ãã質åãåãããããããŸãããRust ã®åçšã§ã¯åžžã«ã©ã€ãã¿ã€ã ã䜿çšããŸããã»ãšãã©ã®å Žåãçç¥ãåæšè« ã«ãããã©ã€ãã¿ã€ã ãèšè¿°ããå¿ èŠã¯ãããŸãããããè€éãªã±ãŒã¹ã§ã¯ãã©ã€ãã¿ã€ã ã¢ãããŒã·ã§ã³ã䜿çšããããšã§ãããŸããã解決ã§ããŸããå€ãã®å Žåãç¹ã«ãããã¿ã€ãã³ã°ã§ã¯ãå¿ èŠã«å¿ããŠå€ãã¯ããŒãã³ã°ããŠææããŒã¿ãåŠçããæ¹ãç°¡åã§ãã
ããŒã¿æ§é ãšã©ã€ãã¿ã€ã
ããŒã¿åãåçšããŒã¿ãå éšã«ä¿æããå Žåãã©ã€ãã¿ã€ã ã¢ãããŒã·ã§ã³ãä»ããå¿ èŠããããŸãã
#[derive(Debug)] struct Highlight<'doc>(&'doc str); fn erase(text: String) { println!("Bye {text}!"); } fn main() { let text = String::from("The quick brown fox jumps over the lazy dog."); let fox = Highlight(&text[4..19]); let dog = Highlight(&text[35..43]); // æ¶å»ïŒããã¹ãïŒ; println!("{fox:?}"); println!("{dog:?}"); }
- äžèšã®äŸã§ã¯ã
Highlight
ã®ã¢ãããŒã·ã§ã³ã«ãããå å ããã&str
ã®åç §å ã®ããŒã¿ã¯ãå°ãªããšããã®ããŒã¿ã䜿çšããHighlight
ã®ã€ã³ã¹ã¿ã³ã¹ãååšããéãåç¶ããªããã°ãªããªããªããŸãã fox
ïŒãŸãã¯dog
ïŒã®ã©ã€ãã¿ã€ã ãçµäºããåã«text
ã䜿çšããããšãåçšãã§ãã«ãŒã¯ãšã©ãŒãã¹ããŒããŸãã- æ¶è²»ããããŒã¿ãå«ãŸããåã§ã¯ããŠãŒã¶ãŒã¯å ã®ããŒã¿ãä¿æããããåŸãªããªããŸããããã¯è»œéã®ãã¥ãŒãäœæããå Žåã«äŸ¿å©ã§ãããäžè¬çã«ã¯äœ¿ãã«ãããªããŸãã
- å¯èœã§ããã°ãããŒã¿æ§é ãããŒã¿ãçŽæ¥ææã§ããããã«ããŸãã
- å éšã«è€æ°ã®åç §ãããæ§é äœã«ã¯ãè€æ°ã®ã©ã€ãã¿ã€ã ã¢ãããŒã·ã§ã³ãå«ãŸããå ŽåããããŸãããããå¿ èŠã«ãªãã®ã¯ãæ§é äœèªäœã®ã©ã€ãã¿ã€ã ã ãã§ãªããåç §å士ã®ã©ã€ãã¿ã€ã ã®é¢ä¿ãèšè¿°ããå¿ èŠãããå Žåã§ããããã¯éåžžã«é«åºŠãªãŠãŒã¹ã±ãŒã¹ã§ãã
æŒç¿: Protobufã®è§£æ
ãã®æŒç¿ã§ã¯ãprotobuf ãã€ã㪠ãšã³ã³ãŒãçšã®ããŒãµãŒãäœæããŸããèŠãããããç°¡åã§ãã®ã§ãå¿é ã¯ãããŸãããããã¯ãããŒã¿ã®ã¹ã©ã€ã¹ãæž¡ãäžè¬çãªè§£æãã¿ãŒã³ã瀺ããŠããŸããåºã«ãªãããŒã¿èªäœãã³ããŒãããããšã¯ãããŸããã
protobuf ã¡ãã»ãŒãžãå®å
šã«è§£æããã«ã¯ããã£ãŒã«ãçªå·ã§ã€ã³ããã¯ã¹ä»ãããããã£ãŒã«ãã®åãç¥ãå¿
èŠããããŸããããã¯éåžžãproto
ãã¡ã€ã«ã§æäŸãããŸãããã®æŒç¿ã§ã¯ããã£ãŒã«ãããšã«åŒã³åºãããé¢æ°ã® match
ã¹ããŒãã¡ã³ãã«ããã®æ
å ±ããšã³ã³ãŒãããŸãã
次㮠proto ã䜿çšããŸãã
message PhoneNumber {
optional string number = 1;
optional string type = 2;
}
message Person {
optional string name = 1;
optional int32 id = 2;
repeated PhoneNumber phones = 3;
}
proto ã¡ãã»ãŒãžã¯ãé£ç¶ãããã£ãŒã«ããšããŠãšã³ã³ãŒããããŸãããããããåŸãã«å€ã䌎ããã¿ã°ããšããŠå®è£
ãããŸããã¿ã°ã«ã¯ãã£ãŒã«ãçªå·ïŒäŸ: Person
ã¡ãã»ãŒãžã® id
ãã£ãŒã«ãã«ã¯ 2
ïŒãšããã€ã ã¹ããªãŒã ãããã€ããŒããã©ã®ããã«æ±ºå®ãããããå®çŸ©ããã¯ã€ã€ãŒã¿ã€ããå«ãŸããŸãã
ã¿ã°ãå«ãæŽæ°ã¯ãVARINT ãšåŒã°ããå¯å€é·ãšã³ã³ãŒãã§è¡šãããŸãã幞ãã«ããparse_varint
ã¯ä»¥äžã§ãã§ã«å®çŸ©ãããŠããŸãããŸãããã®ã³ãŒãã§ã¯ãPerson
ãã£ãŒã«ããš PhoneNumber
ãã£ãŒã«ããåŠçããã¡ãã»ãŒãžã解æããŠãããã®ã³ãŒã«ããã¯ã«å¯Ÿããäžé£ã®åŒã³åºãã«å€æããã³ãŒã«ããã¯ãå®çŸ©ããŠããŸãã
æ®ãäœæ¥ã¯ãparse_field
é¢æ°ãšãPerson
ããã³ PhoneNumber
ã® ProtoMessage
ãã¬ã€ããå®è£
ããã ãã§ãã
/// ã¯ã€ã€ãŒäžã§èŠããã¯ã€ã€ãŒã¿ã€ãã enum WireType { /// Varint WireType ã¯ãå€ãåäžã® VARINT ã§ããããšã瀺ããŸãã Varint, /// The I64 WireType indicates that the value is precisely 8 bytes in /// little-endian order containing a 64-bit signed integer or double type. //I64, -- not needed for this exercise /// The Len WireType indicates that the value is a length represented as a /// VARINT followed by exactly that number of bytes. Len, // The I32 WireType indicates that the value is precisely 4 bytes in // little-endian order containing a 32-bit signed integer or float type. //I32, -- not needed for this exercise } #[derive(Debug)] /// ã¯ã€ã€ãŒã¿ã€ãã«åºã¥ããŠåæå®ããããã£ãŒã«ãã®å€ã enum FieldValue<'a> { Varint(u64), //I64ïŒi64ïŒã -- ãã®æŒç¿ã§ã¯äžèŠ Len(&'a [u8]), //I32(i32), -- not needed for this exercise } #[derive(Debug)] /// ãã£ãŒã«ãçªå·ãšãã®å€ãå«ããã£ãŒã«ãã struct Field<'a> { field_num: u64, value: FieldValue<'a>, } trait ProtoMessage<'a>: Default { fn add_field(&mut self, field: Field<'a>); } impl From<u64> for WireType { fn from(value: u64) -> Self { match value { 0 => WireType::Varint, //1 => WireType::I64ã -- ãã®æŒç¿ã§ã¯äžèŠ 2 => WireType::Len, //5 => WireType::I32, -- not needed for this exercise _ => panic!("Invalid wire type: {value}"), } } } impl<'a> FieldValue<'a> { fn as_str(&self) -> &'a str { let FieldValue::Len(data) = self else { panic!("Expected string to be a `Len` field"); }; std::str::from_utf8(data).expect("Invalid string") } fn as_bytes(&self) -> &'a [u8] { let FieldValue::Len(data) = self else { panic!("Expected bytes to be a `Len` field"); }; data } fn as_u64(&self) -> u64 { let FieldValue::Varint(value) = self else { panic!("Expected `u64` to be a `Varint` field"); }; *value } } /// VARINT ã解æãã解æããå€ãšæ®ãã®ãã€ããè¿ããŸãã fn parse_varint(data: &[u8]) -> (u64, &[u8]) { for i in 0..7 { let Some(b) = data.get(i) else { panic!("Not enough bytes for varint"); }; if b & 0x80 == 0 { // ãã㯠VARINT ã®æåŸã®ãã€ãã§ããããã // u64 ã«å€æããŠè¿ããŸãã let mut value = 0u64; for b in data[..=i].iter().rev() { value = (value << 7) | (b & 0x7f) as u64; } return (value, &data[i + 1..]); } } // 7 ãã€ããè¶ ããå€ã¯ç¡å¹ã§ãã panic!("Too many bytes for varint"); } /// ã¿ã°ããã£ãŒã«ãçªå·ãš WireType ã«å€æããŸãã fn unpack_tag(tag: u64) -> (u64, WireType) { let field_num = tag >> 3; let wire_type = WireType::from(tag & 0x7); (field_num, wire_type) } /// ãã£ãŒã«ãã解æããŠæ®ãã®ãã€ããè¿ããŸãã fn parse_field(data: &[u8]) -> (Field, &[u8]) { let (tag, remainder) = parse_varint(data); let (field_num, wire_type) = unpack_tag(tag); let (fieldvalue, remainder) = match wire_type { _ => todo!("ã¯ã€ã€ãŒã¿ã€ãã«å¿ããŠããã£ãŒã«ããæ§ç¯ããå¿ èŠãªéã®ãã€ããæ¶è²»ããŸãã") }; todo!("ãã£ãŒã«ããšãæªæ¶è²»ã®ãã€ããè¿ããŸãã") } /// æå®ãããããŒã¿å ã®ã¡ãã»ãŒãžã解æããã¡ãã»ãŒãžã®ãã£ãŒã«ãããšã« /// `T::add_field` ãåŒã³åºããŸãã /// /// å ¥åå šäœãæ¶è²»ãããŸãã fn parse_message<'a, T: ProtoMessage<'a>>(mut data: &'a [u8]) -> T { let mut result = T::default(); while !data.is_empty() { let parsed = parse_field(data); result.add_field(parsed.0); data = parsed.1; } result } #[derive(Debug, Default)] struct PhoneNumber<'a> { number: &'a str, type_: &'a str, } #[derive(Debug, Default)] struct Person<'a> { name: &'a str, id: u64, phone: Vec<PhoneNumber<'a>>, } // TODO: Person ãš PhoneNumber ã® ProtoMessage ãå®è£ ããŸãã fn main() { let person: Person = parse_message(&[ 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x77, 0x65, 0x6c, 0x6c, 0x10, 0x2a, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x30, 0x32, 0x2d, 0x35, 0x35, 0x35, 0x2d, 0x31, 0x32, 0x31, 0x32, 0x12, 0x04, 0x68, 0x6f, 0x6d, 0x65, 0x1a, 0x18, 0x0a, 0x0e, 0x2b, 0x31, 0x38, 0x30, 0x30, 0x2d, 0x38, 0x36, 0x37, 0x2d, 0x35, 0x33, 0x30, 0x38, 0x12, 0x06, 0x6d, 0x6f, 0x62, 0x69, 0x6c, 0x65, ]); println!("{:#?}", person); }
- In this exercise there are various cases where protobuf parsing might fail, e.g. if you try to parse an
i32
when there are fewer than 4 bytes left in the data buffer. In normal Rust code we'd handle this with theResult
enum, but for simplicity in this exercise we panic if any errors are encountered. On day 4 we'll cover error handling in Rust in more detail.
解ç
/// ã¯ã€ã€ãŒäžã§èŠããã¯ã€ã€ãŒã¿ã€ãã enum WireType { /// Varint WireType ã¯ãå€ãåäžã® VARINT ã§ããããšã瀺ããŸãã Varint, /// The I64 WireType indicates that the value is precisely 8 bytes in /// little-endian order containing a 64-bit signed integer or double type. //I64, -- not needed for this exercise /// The Len WireType indicates that the value is a length represented as a /// VARINT followed by exactly that number of bytes. Len, // The I32 WireType indicates that the value is precisely 4 bytes in // little-endian order containing a 32-bit signed integer or float type. //I32, -- not needed for this exercise } #[derive(Debug)] /// ã¯ã€ã€ãŒã¿ã€ãã«åºã¥ããŠåæå®ããããã£ãŒã«ãã®å€ã enum FieldValue<'a> { Varint(u64), //I64ïŒi64ïŒã -- ãã®æŒç¿ã§ã¯äžèŠ Len(&'a [u8]), //I32(i32), -- not needed for this exercise } #[derive(Debug)] /// ãã£ãŒã«ãçªå·ãšãã®å€ãå«ããã£ãŒã«ãã struct Field<'a> { field_num: u64, value: FieldValue<'a>, } trait ProtoMessage<'a>: Default { fn add_field(&mut self, field: Field<'a>); } impl From<u64> for WireType { fn from(value: u64) -> Self { match value { 0 => WireType::Varint, //1 => WireType::I64ã -- ãã®æŒç¿ã§ã¯äžèŠ 2 => WireType::Len, //5 => WireType::I32, -- not needed for this exercise _ => panic!("Invalid wire type: {value}"), } } } impl<'a> FieldValue<'a> { fn as_str(&self) -> &'a str { let FieldValue::Len(data) = self else { panic!("Expected string to be a `Len` field"); }; std::str::from_utf8(data).expect("Invalid string") } fn as_bytes(&self) -> &'a [u8] { let FieldValue::Len(data) = self else { panic!("Expected bytes to be a `Len` field"); }; data } fn as_u64(&self) -> u64 { let FieldValue::Varint(value) = self else { panic!("Expected `u64` to be a `Varint` field"); }; *value } } /// VARINT ã解æãã解æããå€ãšæ®ãã®ãã€ããè¿ããŸãã fn parse_varint(data: &[u8]) -> (u64, &[u8]) { for i in 0..7 { let Some(b) = data.get(i) else { panic!("Not enough bytes for varint"); }; if b & 0x80 == 0 { // ãã㯠VARINT ã®æåŸã®ãã€ãã§ããããã // u64 ã«å€æããŠè¿ããŸãã let mut value = 0u64; for b in data[..=i].iter().rev() { value = (value << 7) | (b & 0x7f) as u64; } return (value, &data[i + 1..]); } } // 7 ãã€ããè¶ ããå€ã¯ç¡å¹ã§ãã panic!("Too many bytes for varint"); } /// ã¿ã°ããã£ãŒã«ãçªå·ãš WireType ã«å€æããŸãã fn unpack_tag(tag: u64) -> (u64, WireType) { let field_num = tag >> 3; let wire_type = WireType::from(tag & 0x7); (field_num, wire_type) } /// ãã£ãŒã«ãã解æããŠæ®ãã®ãã€ããè¿ããŸãã fn parse_field(data: &[u8]) -> (Field, &[u8]) { let (tag, remainder) = parse_varint(data); let (field_num, wire_type) = unpack_tag(tag); let (fieldvalue, remainder) = match wire_type { WireType::Varint => { let (value, remainder) = parse_varint(remainder); (FieldValue::Varint(value), remainder) } WireType::Len => { let (len, remainder) = parse_varint(remainder); let len: usize = len.try_into().expect("len not a valid `usize`"); if remainder.len() < len { panic!("Unexpected EOF"); } let (value, remainder) = remainder.split_at(len); (FieldValue::Len(value), remainder) } }; (Field { field_num, value: fieldvalue }, remainder) } /// æå®ãããããŒã¿å ã®ã¡ãã»ãŒãžã解æããã¡ãã»ãŒãžã®ãã£ãŒã«ãããšã« /// `T::add_field` ãåŒã³åºããŸãã /// /// å ¥åå šäœãæ¶è²»ãããŸãã fn parse_message<'a, T: ProtoMessage<'a>>(mut data: &'a [u8]) -> T { let mut result = T::default(); while !data.is_empty() { let parsed = parse_field(data); result.add_field(parsed.0); data = parsed.1; } result } #[derive(PartialEq)] #[derive(Debug, Default)] struct PhoneNumber<'a> { number: &'a str, type_: &'a str, } #[derive(PartialEq)] #[derive(Debug, Default)] struct Person<'a> { name: &'a str, id: u64, phone: Vec<PhoneNumber<'a>>, } impl<'a> ProtoMessage<'a> for Person<'a> { fn add_field(&mut self, field: Field<'a>) { match field.field_num { 1 => self.name = field.value.as_str(), 2 => self.id = field.value.as_u64(), 3 => self.phone.push(parse_message(field.value.as_bytes())), _ => {} // ãã以å€ããã¹ãŠã¹ããã } } } impl<'a> ProtoMessage<'a> for PhoneNumber<'a> { fn add_field(&mut self, field: Field<'a>) { match field.field_num { 1 => self.number = field.value.as_str(), 2 => self.type_ = field.value.as_str(), _ => {} // ãã以å€ããã¹ãŠã¹ããã } } } fn main() { let person: Person = parse_message(&[ 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x77, 0x65, 0x6c, 0x6c, 0x10, 0x2a, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x30, 0x32, 0x2d, 0x35, 0x35, 0x35, 0x2d, 0x31, 0x32, 0x31, 0x32, 0x12, 0x04, 0x68, 0x6f, 0x6d, 0x65, 0x1a, 0x18, 0x0a, 0x0e, 0x2b, 0x31, 0x38, 0x30, 0x30, 0x2d, 0x38, 0x36, 0x37, 0x2d, 0x35, 0x33, 0x30, 0x38, 0x12, 0x06, 0x6d, 0x6f, 0x62, 0x69, 0x6c, 0x65, ]); println!("{:#?}", person); } #[cfg(test)] mod tests { use super::*; #[test] fn test_id() { let person_id: Person = parse_message(&[0x10, 0x2a]); assert_eq!(person_id, Person { name: "", id: 42, phone: vec![] }); } #[test] fn test_name() { let person_name: Person = parse_message(&[ 0x0a, 0x0e, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6c, 0x20, 0x6e, 0x61, 0x6d, 0x65, ]); assert_eq!( person_name, Person { name: "beautiful name", id: 0, phone: vec![] } ); } #[test] fn test_just_person() { let person_name_id: Person = parse_message(&[0x0a, 0x04, 0x45, 0x76, 0x61, 0x6e, 0x10, 0x16]); assert_eq!(person_name_id, Person { name: "Evan", id: 22, phone: vec![] }); } #[test] fn test_phone() { let phone: Person = parse_message(&[ 0x0a, 0x00, 0x10, 0x00, 0x1a, 0x16, 0x0a, 0x0e, 0x2b, 0x31, 0x32, 0x33, 0x34, 0x2d, 0x37, 0x37, 0x37, 0x2d, 0x39, 0x30, 0x39, 0x30, 0x12, 0x04, 0x68, 0x6f, 0x6d, 0x65, ]); assert_eq!( phone, Person { name: "", id: 0, phone: vec![PhoneNumber { number: "+1234-777-9090", type_: "home" },], } ); } }
4 æ¥ç®ã®ãã¬ãŒãã³ã°ã«ãããã
æ¬æ¥ã¯ãRust ã§ã®å€§èŠæš¡ãªãœãããŠã§ã¢ã®ãã«ãã«é¢é£ãããããã¯ãåãäžããŸãã
- ã€ãã¬ãŒã¿:
Iterator
ãã¬ã€ãã®è©³çŽ°ã - ã¢ãžã¥ãŒã«ãšå¯èŠæ§ã
- Testing.
- ãšã©ãŒåŠç: ãããã¯ã
Result
ãtry æŒç®å?
ã - ã¢ã³ã»ãŒãRust: å®å šãª Rust ã§ã¯èšè¿°ã§ããªãå Žåã®åé¿çã
ã¹ã±ãžã¥ãŒã«
Including 10 minute breaks, this session should take about 2 hours and 40 minutes. It contains:
Segment | Duration |
---|---|
ãããã | 3 minutes |
ã€ãã¬ãŒã¿ | 45 minutes |
ã¢ãžã¥ãŒã« | 40 minutes |
ãã¹ã | 45 minutes |
ã€ãã¬ãŒã¿
This segment should take about 45 minutes. It contains:
Slide | Duration |
---|---|
Iterator | 5 minutes |
IntoIterator | 5 minutes |
FromIterator | 5 minutes |
æŒç¿: ã€ãã¬ãŒã¿ã®ã¡ãœãããã§ãŒã³ | 30 minutes |
Iterator
Iterator
ãã¬ã€ãã¯ãã³ã¬ã¯ã·ã§ã³å
ã®äžé£ã®èŠçŽ ã«å¯ŸããŠé çªã«åŠçãé©çšããããšãå¯èœã«ããŸãããã®ãã¬ã€ãã¯nextã¡ãœãããå¿
èŠãšããããã«ããå€ãã®ã¡ãœãããæäŸããŸããå€ãã®æšæºã©ã€ãã©ãªå㧠Iterator
ãå®è£
ãããŠããŸãããèªåã§å®è£
ããããšãã§ããŸãã
struct Fibonacci { curr: u32, next: u32, } impl Iterator for Fibonacci { type Item = u32; fn next(&mut self) -> Option<Self::Item> { let new_next = self.curr + self.next; self.curr = self.next; self.next = new_next; Some(self.curr) } } fn main() { let fib = Fibonacci { curr: 0, next: 1 }; for (i, n) in fib.enumerate().take(5) { println!("fib({i}): {n}"); } }
-
Iterator
ãã¬ã€ãã¯ãã³ã¬ã¯ã·ã§ã³ã«å¯Ÿããå€ãã®äžè¬çãªé¢æ°åããã°ã©ãã³ã° ãªãã¬ãŒã·ã§ã³ïŒäŸ:map
ãfilter
ãreduce
ãªã©ïŒãå®è£ ããŸãããã®ãã¬ã€ãã®ããã¥ã¡ã³ãã«ãããŠããããã®ãã¹ãŠã®ãªãã¬ãŒã·ã§ã³ã«é¢ãã説æã確èªã§ããŸããRust ã§ã¯ããããã®é¢æ°ã«ãããåçã®åœä»€åå®è£ ãšåããããå¹ççãªã³ãŒããçæãããŸãã -
IntoIterator
ã¯ãforã«ãŒããå®çŸããããã®ãã¬ã€ãã§ããã³ã¬ã¯ã·ã§ã³åïŒVec<T>
ãªã©ïŒãšããããã«å¯Ÿããåç §ïŒ&Vec<T>
ã&[T]
ãªã©ïŒã«ãããŠå®è£ ãããŠããŸãããŸããç¯å²ãè¡šãåã«ãããŠãå®è£ ãããŠããŸããfor i in some_vec { .. }
ã䜿çšããŠãã¯ã¿ãŒãå埩åŠçã§ããã®ã«ãsome_vec.next()
ãååšããªãã®ã¯ãã®ããã§ãã
IntoIterator
Iterator
ãã¬ã€ãã¯ãã€ãã¬ãŒã¿ãäœæããåŸã«å埩åŠçãè¡ãæ¹æ³ã瀺ããŸããé¢é£ãããã¬ã€ã IntoIterator
ã¯ãããåã«å¯Ÿããã€ãã¬ãŒã¿ãäœæããæ¹æ³ãå®çŸ©ããŸãããã㯠for
ã«ãŒãã«ãã£ãŠèªåçã«äœ¿çšãããŸãã
struct Grid { x_coords: Vec<u32>, y_coords: Vec<u32>, } impl IntoIterator for Grid { type Item = (u32, u32); type IntoIter = GridIter; fn into_iter(self) -> GridIter { GridIter { grid: self, i: 0, j: 0 } } } struct GridIter { grid: Grid, i: usize, j: usize, } impl Iterator for GridIter { type Item = (u32, u32); fn next(&mut self) -> Option<(u32, u32)> { if self.i >= self.grid.x_coords.len() { self.i = 0; self.j += 1; if self.j >= self.grid.y_coords.len() { return None; } } let res = Some((self.grid.x_coords[self.i], self.grid.y_coords[self.j])); self.i += 1; res } } fn main() { let grid = Grid { x_coords: vec![3, 5, 7, 9], y_coords: vec![10, 20, 30, 40] }; for (x, y) in grid { println!("point = {x}, {y}"); } }
IntoIterator
ã®ããã¥ã¡ã³ããã¯ãªãã¯ããŠã芧ãã ãããIntoIterator
ã®ãã¹ãŠã®å®è£
ã§ã次㮠2 ã€ã®åã宣èšããå¿
èŠããããŸãã
Item
: å埩åŠçããåïŒi8
ãªã©ïŒãIntoIter
:into_iter
ã¡ãœããã«ãã£ãŠè¿ãããIterator
åã
IntoIter
ãšItem
ã¯é¢é£ããããã€ãã¬ãŒã¿ã¯Item
ãšåãåãæã€å¿
èŠããããŸããããªãã¡ãOption<Item>
ãè¿ããŸãã
ãã®äŸã¯ãx 座æšãš y 座æšã®ãã¹ãŠã®çµã¿åãããå埩åŠçããŠããŸãã
main
ã§ã°ãªããã 2 åå埩åŠçããŠã¿ãŸããããããã¯ãªã倱æããã®ã§ãããããIntoIterator::into_iter
㯠self
ã®æææš©ãååŸããããšã«çç®ããŠãã ããã
ãã®åé¡ãä¿®æ£ããã«ã¯ã&Grid
ã« IntoIterator
ãå®è£
ããGrid
ãžã®åç
§ã GridIter
ã«ä¿åããŸãã
æšæºã©ã€ãã©ãªåã§ãåãåé¡ãçºçããå¯èœæ§ããããŸããfor e in some_vector
ã¯ãsome_vector
ã®æææš©ãååŸãããã®ãã¯ã¿ãŒã®ææèŠçŽ ãå埩åŠçããŸããsome_vector
ã®èŠçŽ ãžã®åç
§ãå埩åŠçããã«ã¯ã代ããã« for e in &some_vector
ã䜿çšããŸãã
FromIterator
FromIterator
ã䜿çšãããšãIterator
ããã³ã¬ã¯ã·ã§ã³ãäœæã§ããŸãã
fn main() { let primes = vec![2, 3, 5, 7]; let prime_squares = primes.into_iter().map(|p| p * p).collect::<Vec<_>>(); println!("prime_squares: {prime_squares:?}"); }
Iterator
ã®å®è£
fn collect<B>(self) -> B
where
B: FromIterator<Self::Item>,
Self: Sized
ãã®ã¡ãœãã㧠B
ãæå®ããã«ã¯ã次㮠2 ã€ã®æ¹æ³ããããŸãã
- ãturbofishãã䜿çšããå Žå: äŸãã°ãäžèšã«ãããã
some_iterator.collect::<COLLECTION_TYPE>()
ãããã§äœ¿çšãããŠãã_ ã¯Rustã«Vecã®
èŠçŽ ã®æ¹ãæšæž¬ãããããã®ãã®ã§ãã - åæšè«ã䜿çšããå Žå:
let prime_squares: Vec<_> = some_iterator.collect()
ããã®åœ¢åŒã䜿çšããããã«äŸãæžãæããŠãã ããã
Vec
ã HashMap
ãªã©ã« FromIterator
ã®åºæ¬çãªå®è£
ãçšæãããŠããŸãããŸããIterator<Item = Result<V, E>>
ã Result<Vec<V>, E>
ã«å€æã§ãããã®ãªã©ãããç¹åããå®è£
ããããŸãã
æŒç¿: ã€ãã¬ãŒã¿ã®ã¡ãœãããã§ãŒã³
ãã®æŒç¿ã§ã¯ãè€éãªèšç®ãå®è£
ããããã«Iterator
ãã¬ã€ãã§æäŸãããŠããã¡ãœãããããã€ããæ¢ããŠäœ¿çšããå¿
èŠããããŸãã
次ã®ã³ãŒãã https://play.rust-lang.org/ ã«ã³ããŒãããã¹ããéãããã«ããŠãã ãããã€ãã¬ãŒã¿åŒã䜿çšãããã®çµæãcollect
ããããšã§æ»ãå€ãçæããŸãã
#![allow(unused)] fn main() { /// `values`ã«ãããŠã`offset`ã ãé¢ããèŠçŽ éã®å·®ãèšç®ããŸãã /// ãªãã`values`ã®æ«å°ŸèŠçŽ ã®æ¬¡ã¯å é ãžæ»ãããšãšããŸãã /// /// çµæã®èŠçŽ `n` 㯠`values[(n+offset)%len] - values[n]` ã§ãã fn offset_differences(offset: usize, values: Vec<i32>) -> Vec<i32> { unimplemented!() } #[test] fn test_offset_one() { assert_eq!(offset_differences(1, vec![1, 3, 5, 7]), vec![2, 2, 2, -6]); assert_eq!(offset_differences(1, vec![1, 3, 5]), vec![2, 2, -4]); assert_eq!(offset_differences(1, vec![1, 3]), vec![2, -2]); } #[test] fn test_larger_offsets() { assert_eq!(offset_differences(2, vec![1, 3, 5, 7]), vec![4, 4, -4, -4]); assert_eq!(offset_differences(3, vec![1, 3, 5, 7]), vec![6, -2, -2, -2]); assert_eq!(offset_differences(4, vec![1, 3, 5, 7]), vec![0, 0, 0, 0]); assert_eq!(offset_differences(5, vec![1, 3, 5, 7]), vec![2, 2, 2, -6]); } #[test] fn test_degenerate_cases() { assert_eq!(offset_differences(1, vec![0]), vec![0]); assert_eq!(offset_differences(1, vec![1]), vec![0]); let empty: Vec<i32> = vec![]; assert_eq!(offset_differences(1, empty), vec![]); } }
解ç
/// `values`ã«ãããŠã`offset`ã ãé¢ããèŠçŽ éã®å·®ãèšç®ããŸãã /// ãªãã`values`ã®æ«å°ŸèŠçŽ ã®æ¬¡ã¯å é ãžæ»ãããšãšããŸãã /// /// çµæã®èŠçŽ `n` 㯠`values[(n+offset)%len] - values[n]` ã§ãã fn offset_differences(offset: usize, values: Vec<i32>) -> Vec<i32> { let a = (&values).into_iter(); let b = (&values).into_iter().cycle().skip(offset); a.zip(b).map(|(a, b)| *b - *a).collect() } #[test] fn test_offset_one() { assert_eq!(offset_differences(1, vec![1, 3, 5, 7]), vec![2, 2, 2, -6]); assert_eq!(offset_differences(1, vec![1, 3, 5]), vec![2, 2, -4]); assert_eq!(offset_differences(1, vec![1, 3]), vec![2, -2]); } #[test] fn test_larger_offsets() { assert_eq!(offset_differences(2, vec![1, 3, 5, 7]), vec![4, 4, -4, -4]); assert_eq!(offset_differences(3, vec![1, 3, 5, 7]), vec![6, -2, -2, -2]); assert_eq!(offset_differences(4, vec![1, 3, 5, 7]), vec![0, 0, 0, 0]); assert_eq!(offset_differences(5, vec![1, 3, 5, 7]), vec![2, 2, 2, -6]); } #[test] fn test_degenerate_cases() { assert_eq!(offset_differences(1, vec![0]), vec![0]); assert_eq!(offset_differences(1, vec![1]), vec![0]); let empty: Vec<i32> = vec![]; assert_eq!(offset_differences(1, empty), vec![]); } fn main() {}
ã¢ãžã¥ãŒã«
This segment should take about 40 minutes. It contains:
Slide | Duration |
---|---|
ã¢ãžã¥ãŒã« | 3 minutes |
ãã¡ã€ã«ã·ã¹ãã éå±€ | 5 minutes |
å¯èŠæ§ | 5 minutes |
useãsuperãself | 10 minutes |
æŒç¿: GUI ã©ã€ãã©ãªã®ã¢ãžã¥ãŒã« | 15 minutes |
ã¢ãžã¥ãŒã«
impl
ãããã¯ã§é¢æ°ãåã®åå空éã«æå±ãããæ¹æ³ã¯ãã§ã«èŠãŠããŸããã
åæ§ã«ãmod
ã䜿çšããŠåãšé¢æ°ã®åå空éãæå®ã§ããŸãã
mod foo { pub fn do_something() { println!("In the foo module"); } } mod bar { pub fn do_something() { println!("In the bar module"); } } fn main() { foo::do_something(); bar::do_something(); }
- ããã±ãŒãž(package)ã¯æ©èœãæäŸãããã®ã§ãããäžã€ä»¥äžã®ã¯ã¬ãŒãããã«ãããæ¹æ³ãèšè¿°ãã
Cargo.toml
ãã¡ã€ã«ãå«ããã®ã§ãã - ãã€ããªã¯ã¬ãŒãã®å Žåã¯å®è¡å¯èœãã¡ã€ã«ãçæããã©ã€ãã©ãªã¯ã¬ãŒãã®å Žåã¯ã©ã€ãã©ãªãçæããŸãã
- ã¢ãžã¥ãŒã«ã«ãã£ãŠæ§æãšã¹ã³ãŒããå®çŸ©ãããŸãããã®ã»ã¯ã·ã§ã³ã§ã¯ã¢ãžã¥ãŒã«ã«çŠç¹ãåœãŠãŸãã
ãã¡ã€ã«ã·ã¹ãã éå±€
ã¢ãžã¥ãŒã«ã®å®çŸ©å 容ãçç¥ãããšãRust ã¯ãããå¥ã®ãã¡ã€ã«ã§æ¢ããŸãã
mod garden;
This tells Rust that the garden
module content is found at src/garden.rs
. Similarly, a garden::vegetables
module can be found at src/garden/vegetables.rs
.
crate
ã«ãŒãã¯ä»¥äžã®å Žæã«ãããŸãã
src/lib.rs
ïŒã©ã€ãã©ãª ã¯ã¬ãŒãã®å ŽåïŒsrc/main.rs
ïŒãã€ã㪠ã¯ã¬ãŒãã®å ŽåïŒ
ãã¡ã€ã«ã§å®çŸ©ãããã¢ãžã¥ãŒã«ã«å¯ŸããŠããå éšããã¥ã¡ã³ãçšã³ã¡ã³ããã䜿çšããŠèª¬æãå ããããšãã§ããŸãããããã®ã³ã¡ã³ãã¯ããããå«ãŸããã¢ã€ãã ïŒãã®å Žåã¯ã¢ãžã¥ãŒã«ïŒã«å¯Ÿãã説æã«ãªããŸãã
//! ãã®ã¢ãžã¥ãŒã«ã¯çãå®è£ ããŸãïŒããã©ãŒãã³ã¹ã®é«ãçºèœã® //! å®è£ ãå«ãïŒã // ãã®ã¢ãžã¥ãŒã«ããåãåãšã¯ã¹ããŒãããŸãã pub use garden::Garden; pub use seeds::SeedPacket; /// æå®ãããçš®ããŸããŸãã pub fn sow(seeds: Vec<SeedPacket>) { todo!() } /// ååã«å®ã£ãŠããçã§äœç©ãåç©«ããŸãã pub fn harvest(garden: &mut Garden) { todo!() }
-
Rust 2018 ããåã§ã¯ãã¢ãžã¥ãŒã«ã
module.rs
ã§ã¯ãªãmodule/mod.rs
ã«é 眮ããå¿ èŠããããŸããããã㯠2018 以éã®ãšãã£ã·ã§ã³ã§ãäŸç¶ãšããŠãµããŒããããŠããŸãã -
filename/mod.rs
ã®ä»£ããã«filename.rs
ãå°å ¥ãããäž»ãªçç±ã¯ãmod.rs
ãšããååã®ãã¡ã€ã«ãå€ããããšãããããIDEã§åºå¥ããã®ãé£ããå Žåãããããã§ãã -
ããæ·±ããã¹ãã§ã¯ãã¡ã€ã³ ã¢ãžã¥ãŒã«ããã¡ã€ã«ã§ãã£ãŠãããã©ã«ãã䜿çšã§ããŸãã
src/ âââ main.rs âââ top_module.rs âââ top_module/ âââ sub_module.rs
-
Rust ãã¢ãžã¥ãŒã«ãæ€çŽ¢ããå Žæã¯ãã³ã³ãã€ã© ãã£ã¬ã¯ãã£ãã§å€æŽã§ããŸãã
#[path = "some/path.rs"] mod some_module;
ããã¯ãããšãã° Go ã§ããè¡ãããŠããããã«ã
some_module_test.rs
ãšããååã®ãã¡ã€ã«ã«ã¢ãžã¥ãŒã«ã®ãã¹ããé 眮ããå Žåã«äŸ¿å©ã§ãã
å¯èŠæ§
ã¢ãžã¥ãŒã«ã¯ãã©ã€ãã·ãŒã®å¢çã§ãã
- ã¢ãžã¥ãŒã« ã¢ã€ãã ã¯ããã©ã«ãã§ãã©ã€ããŒãã§ãïŒå®è£ ã®è©³çŽ°ã¯è¡šç€ºãããŸããïŒã
- 芪ã¢ã€ãã ãšå åŒã¢ã€ãã ã¯åžžã«èŠããŸãã
- èšãæããã°ãããã¢ã€ãã ãã¢ãžã¥ãŒã«
foo
ããèŠããå Žåããã®ã¢ã€ãã ã¯foo
ã®ãã¹ãŠã®åå«ããèŠããŸãã
mod outer { fn private() { println!("outer::private"); } pub fn public() { println!("outer::public"); } mod inner { fn private() { println!("outer::inner::private"); } pub fn public() { println!("outer::inner::public"); super::private(); } } } fn main() { outer::public(); }
- ã¢ãžã¥ãŒã«ãå
¬éããã«ã¯
pub
ããŒã¯ãŒãã䜿çšããŸãã
ãŸããé«åºŠãª pub(...)
æå®åã䜿çšããŠãå
¬éç¯å²ãå¶éããããšãã§ããŸãã
- Rust ãªãã¡ã¬ã³ã¹ ãã芧ãã ããã
pub(crate)
ã®å¯èŠæ§ãèšå®ããã®ã¯äžè¬çãªãã¿ãŒã³ã§ãã- ããã»ã©äžè¬çã§ã¯ãããŸããããç¹å®ã®ãã¹ã«å¯ŸããŠå¯èŠæ§ãæå®ããããšãåºæ¥ãŸãã
- ã©ã®ãããªå Žåããç¥å ã¢ãžã¥ãŒã«ïŒããã³ãã®ãã¹ãŠã®åå«ïŒã«å¯èŠæ§ãäžããå¿ èŠããããŸãã
useãsuperãself
ã¢ãžã¥ãŒã«ã¯ãuse
ã䜿çšããŠå¥ã®ã¢ãžã¥ãŒã«ã®ã·ã³ãã«ãã¹ã³ãŒãã«åã蟌ãããšãã§ããŸãã次ã®ãããªèšè¿°ã¯ã¢ãžã¥ãŒã«ã®å
é ã«ãããŠããèŠãããŸãã
use std::collections::HashSet; use std::process::abort;
ãã¹
ãã¹ã¯æ¬¡ã®ããã«è§£æ±ºãããŸãã
-
çžå¯Ÿãã¹ã®å Žå:
foo
ãŸãã¯self::foo
ã¯ãçŸåšã®ã¢ãžã¥ãŒã«å ã®foo
ãåç §ããŸããsuper::foo
ã¯ã芪ã¢ãžã¥ãŒã«å ã®foo
ãåç §ããŸãã
-
絶察ãã¹ã®å Žå:
crate::foo
ã¯ãçŸåšã®ã¯ã¬ãŒãã®ã«ãŒãå ã®foo
ãåç §ããŸããbar::foo
ã¯ãbar
ã¯ã¬ãŒãå ã®foo
ãåç §ããŸãã
-
ã·ã³ãã«ã¯ãããçããã¹ã§ãåãšã¯ã¹ããŒããããã®ãäžè¬çã§ããããšãã°ãã¯ã¬ãŒãå ã®æäžäœã®
lib.rs
ã«ã以äžã®ããã«èšè¿°ããŸããmod storage; pub use storage::disk::DiskStorage; pub use storage::network::NetworkStorage;
ããã«ãããçã䜿ãããããã¹ã䜿çšããŠã
DiskStorage
ãšNetworkStorage
ãä»ã®ã¯ã¬ãŒãã§äœ¿çšã§ããããã«ãªããŸãã -
ã»ãšãã©ã®å Žåã
use
ãæå®ããå¿ èŠãããã®ã¯ã¢ãžã¥ãŒã«å ã§å®éã«çŽæ¥äœ¿çšãããã¢ã€ãã ã®ã¿ã§ãããã ãããããã¬ã€ããå®è£ ããåããã§ã«ã¹ã³ãŒãã«å«ãŸããŠããå Žåã§ãããã®ãã¬ã€ãã®ã¡ãœãããåŒã³åºãã«ã¯ããã®ãã¬ã€ããã¹ã³ãŒãã«å«ãŸããŠããå¿ èŠããããŸããããšãã°ãRead
ãã¬ã€ããå®è£ ããåã§read_to_string
ã¡ãœããã䜿çšããã«ã¯ãuse std::io::Read
ãšããèšè¿°ãå¿ èŠã«ãªããŸãã -
use
ã¹ããŒãã¡ã³ãã«ã¯use std::io::*
ãšããããã«ã¯ã€ã«ãã«ãŒããå«ããããšãã§ããŸãããã®æ¹æ³ã¯ãã©ã®ã¢ã€ãã ãã€ã³ããŒããããã®ããæ確ã§ã¯ãªããæéã®çµéãšãšãã«å€åããå¯èœæ§ããããããããããããŸããã
æŒç¿: GUI ã©ã€ãã©ãªã®ã¢ãžã¥ãŒã«
ãã®æŒç¿ã§ã¯ãå°èŠæš¡ãª GUI ã©ã€ãã©ãªå®è£
ãåç·šæããŸãããã®ã©ã€ãã©ãªã§ã¯ãWidget
ãã¬ã€ãããã®ãã¬ã€ãã®ããã€ãã®å®è£
ãmain
é¢æ°ãå®çŸ©ããŠããŸãã
éåžžã¯ãååãŸãã¯å¯æ¥ã«é¢é£ããåã®ã»ãããåå¥ã®ã¢ãžã¥ãŒã«ã«é 眮ããã®ã§ããŠã£ãžã§ãã ã¿ã€ãããšã«ç¬èªã®ã¢ãžã¥ãŒã«ãçšæããå¿ èŠããããŸãã
Cargo Setup
Rust ãã¬ã€ã°ã©ãŠã³ã㯠1 ã€ã®ãã¡ã€ã«ãããµããŒãããŠããªããããããŒã«ã« ãã¡ã€ã« ã·ã¹ãã 㧠Cargo ãããžã§ã¯ããäœæããå¿ èŠããããŸãã
cargo init gui-modules
cd gui-modules
cargo run
çæããã src/main.rs
ãç·šéã㊠mod
ã¹ããŒãã¡ã³ããè¿œå ããsrc
ãã£ã¬ã¯ããªã«ãã¡ã€ã«ãè¿œå ããŸãã
ãœãŒã¹
GUI ã©ã€ãã©ãªã®åäžã¢ãžã¥ãŒã«å®è£ ã¯æ¬¡ã®ãšããã§ãã
pub trait Widget { /// `self` ã®èªç¶ãªå¹ ã fn width(&self) -> usize; /// ãŠã£ãžã§ããããããã¡ã«æç»ããŸãã fn draw_into(&self, buffer: &mut dyn std::fmt::Write); /// ãŠã£ãžã§ãããæšæºåºåã«æç»ããŸãã fn draw(&self) { let mut buffer = String::new(); self.draw_into(&mut buffer); println!("{buffer}"); } } pub struct Label { label: String, } impl Label { fn new(label: &str) -> Label { Label { label: label.to_owned() } } } pub struct Button { label: Label, } impl Button { fn new(label: &str) -> Button { Button { label: Label::new(label) } } } pub struct Window { title: String, widgets: Vec<Box<dyn Widget>>, } impl Window { fn new(title: &str) -> Window { Window { title: title.to_owned(), widgets: Vec::new() } } fn add_widget(&mut self, widget: Box<dyn Widget>) { self.widgets.push(widget); } fn inner_width(&self) -> usize { std::cmp::max( self.title.chars().count(), self.widgets.iter().map(|w| w.width()).max().unwrap_or(0), ) } } impl Widget for Window { fn width(&self) -> usize { // æ ç·çšã« 4 ã€ã®ããã£ã³ã°ãè¿œå ããŸãã self.inner_width() + 4 } fn draw_into(&self, buffer: &mut dyn std::fmt::Write) { let mut inner = String::new(); for widget in &self.widgets { widget.draw_into(&mut inner); } let inner_width = self.inner_width(); // TODO: Result<(), std::fmt::Error> ãè¿ãããã« draw_into ãå€æŽããŸãã次ã«ã.unwrap() ã®ä»£ããã« // ? æŒç®åã䜿çšããŸãã writeln!(buffer, "+-{:-<inner_width$}-+", "").unwrap(); writeln!(buffer, "| {:^inner_width$} |", &self.title).unwrap(); writeln!(buffer, "+={:=<inner_width$}=+", "").unwrap(); for line in inner.lines() { writeln!(buffer, "| {:inner_width$} |", line).unwrap(); } writeln!(buffer, "+-{:-<inner_width$}-+", "").unwrap(); } } impl Widget for Button { fn width(&self) -> usize { self.label.width() + 8 // ããã£ã³ã°ãå°ãè¿œå ããŸãã } fn draw_into(&self, buffer: &mut dyn std::fmt::Write) { let width = self.width(); let mut label = String::new(); self.label.draw_into(&mut label); writeln!(buffer, "+{:-<width$}+", "").unwrap(); for line in label.lines() { writeln!(buffer, "|{:^width$}|", &line).unwrap(); } writeln!(buffer, "+{:-<width$}+", "").unwrap(); } } impl Widget for Label { fn width(&self) -> usize { self.label.lines().map(|line| line.chars().count()).max().unwrap_or(0) } fn draw_into(&self, buffer: &mut dyn std::fmt::Write) { writeln!(buffer, "{}", &self.label).unwrap(); } } fn main() { let mut window = Window::new("Rust GUI Demo 1.23"); window.add_widget(Box::new(Label::new("This is a small text GUI demo."))); window.add_widget(Box::new(Button::new("Click me!"))); window.draw(); }
èªåã«ãšã£ãŠèªç¶ãªæ¹æ³ã§ã³ãŒããåå²ããå¿
èŠãª mod
ãuse
ãpub
宣èšã«æ
£ããããåè¬è
ã«ä¿ããŸãããã®åŸãã©ã®æ§æãæãæ
£çšçã§ãããã«ã€ããŠè©±ãåããŸãã
解ç
src
âââ main.rs
âââ widgets
â  âââ button.rs
â  âââ label.rs
â  âââ window.rs
âââ widgets.rs
// ---- src/widgets.rs ----
mod button;
mod label;
mod window;
pub trait Widget {
/// `self` ã®èªç¶ãªå¹
ã
fn width(&self) -> usize;
/// ãŠã£ãžã§ããããããã¡ã«æç»ããŸãã
fn draw_into(&self, buffer: &mut dyn std::fmt::Write);
/// ãŠã£ãžã§ãããæšæºåºåã«æç»ããŸãã
fn draw(&self) {
let mut buffer = String::new();
self.draw_into(&mut buffer);
println!("{buffer}");
}
}
pub use button::Button;
pub use label::Label;
pub use window::Window;
// ---- src/widgets/label.rs ----
use super::Widget;
pub struct Label {
label: String,
}
impl Label {
pub fn new(label: &str) -> Label {
Label { label: label.to_owned() }
}
}
impl Widget for Label {
fn width(&self) -> usize {
// ANCHOR_END: Label-width
self.label.lines().map(|line| line.chars().count()).max().unwrap_or(0)
}
// ANCHOR: Label-draw_into
fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {
// ANCHOR_END: Label-draw_into
writeln!(buffer, "{}", &self.label).unwrap();
}
}
// ---- src/widgets/button.rs ----
use super::{Label, Widget};
pub struct Button {
label: Label,
}
impl Button {
pub fn new(label: &str) -> Button {
Button { label: Label::new(label) }
}
}
impl Widget for Button {
fn width(&self) -> usize {
// ANCHOR_END: Button-width
self.label.width() + 8 // ããã£ã³ã°ãå°ãè¿œå ããŸãã
}
// ANCHOR: Button-draw_into
fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {
// ANCHOR_END: Button-draw_into
let width = self.width();
let mut label = String::new();
self.label.draw_into(&mut label);
writeln!(buffer, "+{:-<width$}+", "").unwrap();
for line in label.lines() {
writeln!(buffer, "|{:^width$}|", &line).unwrap();
}
writeln!(buffer, "+{:-<width$}+", "").unwrap();
}
}
// ---- src/widgets/window.rs ----
use super::Widget;
pub struct Window {
title: String,
widgets: Vec<Box<dyn Widget>>,
}
impl Window {
pub fn new(title: &str) -> Window {
Window { title: title.to_owned(), widgets: Vec::new() }
}
pub fn add_widget(&mut self, widget: Box<dyn Widget>) {
self.widgets.push(widget);
}
fn inner_width(&self) -> usize {
std::cmp::max(
self.title.chars().count(),
self.widgets.iter().map(|w| w.width()).max().unwrap_or(0),
)
}
}
impl Widget for Window {
fn width(&self) -> usize {
// ANCHOR_END: Window-width
// æ ç·ã« 4 ã€ã®ããã£ã³ã°ãè¿œå ããŸãã
self.inner_width() + 4
}
// ANCHOR: Window-draw_into
fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {
// ANCHOR_END: Window-draw_into
let mut inner = String::new();
for widget in &self.widgets {
widget.draw_into(&mut inner);
}
let inner_width = self.inner_width();
// TODO: ãšã©ãŒåŠçã«ã€ããŠåŠç¿ããåŸã§ã
// Result<(), std::fmt::Error> ãè¿ãããã« draw_into ãå€æŽã§ããŸãã次ã«ãããã§
// .unwrap() ã®ä»£ããã« ? æŒç®åã䜿çšããŸãã
writeln!(buffer, "+-{:-<inner_width$}-+", "").unwrap();
writeln!(buffer, "| {:^inner_width$} |", &self.title).unwrap();
writeln!(buffer, "+={:=<inner_width$}=+", "").unwrap();
for line in inner.lines() {
writeln!(buffer, "| {:inner_width$} |", line).unwrap();
}
writeln!(buffer, "+-{:-<inner_width$}-+", "").unwrap();
}
}
// ---- src/main.rs ----
mod widgets;
use widgets::Widget;
fn main() {
let mut window = widgets::Window::new("Rust GUI Demo 1.23");
window
.add_widget(Box::new(widgets::Label::new("This is a small text GUI demo.")));
window.add_widget(Box::new(widgets::Button::new("Click me!")));
window.draw();
}
ãã¹ã
This segment should take about 45 minutes. It contains:
Slide | Duration |
---|---|
ãã¹ãã¢ãžã¥ãŒã« | 5 minutes |
ä»ã®ã¿ã€ãã®ãã¹ã | 5 minutes |
ã³ã³ãã€ã©ã® Lints ãš Clippy | 3 minutes |
æŒç¿: Luhnã¢ã«ãŽãªãºã | 30 minutes |
ãŠããããã¹ã
Rust ãš Cargo ã«ã¯ãã·ã³ãã«ãªåäœãã¹ã ãã¬ãŒã ã¯ãŒã¯ãä»å±ããŠããŸãã
-
åäœãã¹ãã¯ã³ãŒãã®ããããå Žæã«èšè¿°å¯èœã§ãã
-
çµ±åãã¹ãã¯
tests/
ãã£ã¬ã¯ããªå ã«èšè¿°ããŸãã
ãã¹ãã«ã¯ #[test]
ã®ããŒã¯ãä»ããŸããå€ãã®å Žåãåäœãã¹ãã¯éåžžãã¹ããããtests
ã¢ãžã¥ãŒã«ã«é
眮ããã#[cfg(test)]
ã«ãããã¹ãã®ãã«ãæã«ã®ã¿ã³ã³ãã€ã«ãããããã«ãªããŸãã
fn first_word(text: &str) -> &str {
match text.find(' ') {
Some(idx) => &text[..idx],
None => &text,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty() {
assert_eq!(first_word(""), "");
}
#[test]
fn test_single_word() {
assert_eq!(first_word("Hello"), "Hello");
}
#[test]
fn test_multiple_words() {
assert_eq!(first_word("Hello World"), "Hello");
}
}
- ããã«ããããã©ã€ããŒã ãã«ããŒã®åäœãã¹ããè¡ããŸãã
#[cfg(test)]
å±æ§ãä»äžãããã³ãŒãã¯cargo test
ã®å®è¡æã«ã®ã¿æå¹ã«ãªããŸãã
çµæã衚瀺ããã«ã¯ããã¬ã€ã°ã©ãŠã³ãã§ãã¹ããå®è¡ããŸãã
ä»ã®ã¿ã€ãã®ãã¹ã
ã€ã³ãã°ã¬ãŒã·ã§ã³ãã¹ã
ã©ã€ãã©ãªãã¯ã©ã€ã¢ã³ããšããŠãã¹ãããå Žåã¯ãçµ±åãã¹ãã䜿çšããŸãã
tests/
ã®äžã« .rs
ãã¡ã€ã«ãäœæããŸãã
// tests/my_library.rs
use my_library::init;
#[test]
fn test_init() {
assert!(init().is_ok());
}
ãããã®ãã¹ãã§ã¯ãã¯ã¬ãŒãã®å ¬é API ã«ã®ã¿ã¢ã¯ã»ã¹ã§ããŸãã
ããã¥ã¡ã³ããŒã·ã§ã³ãã¹ã
Rust ã«ã¯ãããã¥ã¡ã³ãã®ãã¹ãã«é¢ããæ©èœãçµã¿èŸŒãŸããŠããŸãã
#![allow(unused)] fn main() { /// æå®ããé·ãã«æååãççž®ããŸãã /// /// ``` /// # use playground::shorten_string; /// assert_eq!(shorten_string("Hello World", 5), "Hello"); /// assert_eq!(shorten_string("Hello World", 20), "Hello World"); /// ``` pub fn shorten_string(s: &str, length: usize) -> &str { &s[..std::cmp::min(length, s.len())] } }
///
ã³ã¡ã³ãå ã®ã³ãŒããããã¯ã¯ãèªåçã« Rust ã³ãŒããšã¿ãªãããŸãã- ã³ãŒãã¯
cargo test
ã®äžç°ãšããŠã³ã³ãã€ã«ãããå®è¡ãããŸãã - ã³ãŒãã«
#
ãè¿œå ãããšãããã¥ã¡ã³ãã«ã¯è¡šç€ºãããªããªããŸãããã³ã³ãã€ã«ãšå®è¡ã¯åŒãç¶ãè¡ãããŸãã - Rust ãã¬ã€ã°ã©ãŠã³ã ã§äžèšã®ã³ãŒãããã¹ãããŸãã
ã³ã³ãã€ã©ã® Lints ãš Clippy
Rust ã³ã³ãã€ã©ã¯ãèªã¿ããããšã©ãŒããã³lintã¡ãã»ãŒãžãçæããŸããClippy ã§ã¯ãããã«å€ãã® lint ãã°ã«ãŒãã«ãŸãšããããŠããããããžã§ã¯ãããšã«æå¹ã«ã§ããŸãã
#[deny(clippy::cast_possible_truncation)] fn main() { let x = 3; while (x < 70000) { x *= 2; } println!("X probably fits in a u16, right? {}", x as u16); }
ã³ãŒããµã³ãã«ãå®è¡ããŠãšã©ãŒ ã¡ãã»ãŒãžã確èªããŸããããã«ã lint ã衚瀺ãããŠããŸãããã³ãŒãã®ã³ã³ãã€ã«ãäžåºŠã³ã³ãã€ã«åºæ¥ããšè¡šç€ºãããªããªããŸãããããã® lint ã衚瀺ããã«ã¯ããã¬ã€ã°ã©ãŠã³ã ãµã€ãã«åãæ¿ããŸãã
lint ã解決ããåŸããã¬ã€ã°ã©ãŠã³ã ãµã€ã㧠clippy
ãå®è¡ããŠãClippy ã®èŠåã衚瀺ããŸããClippy ã«ã¯ãlint ã«é¢ããåºç¯ãªããã¥ã¡ã³ãããããæ°ãã lintïŒããã©ã«ãã§ãšã©ãŒã«ãªããªã³ã ãå«ãïŒãåžžã«è¿œå ãããŠããŸãã
help: ...
ãä»ããšã©ãŒãèŠåã¯ãcargo fix
ãŸãã¯ãšãã£ã¿ã䜿çšããŠä¿®æ£ã§ããŸãã
æŒç¿: Luhnã¢ã«ãŽãªãºã
Luhn ã¢ã«ãŽãªãºã ã¯ãã¯ã¬ãžãã ã«ãŒãçªå·ã®æ€èšŒã«äœ¿çšãããŸãããã®ã¢ã«ãŽãªãºã ã¯æååãå ¥åãšããŠåãåãã以äžã®åŠçãè¡ã£ãŠã¯ã¬ãžãã ã«ãŒãçªå·ãæ€èšŒããŸãã
-
Ignore all spaces. Reject numbers with fewer than two digits.
-
å³ããå·Šã«èŠãŠãããªããããããã2æ¡ç®ã®æ°åã 2 åã«ããŸããæ°å€
1234
ã®å Žåã3
ãš1
ã 2 åã«ããŸããæ°å€98765
ã®å Žåã6
ãš8
ã 2 åã«ããŸãã -
æ¡ã 2 åã«ããåŸãçµæã 9 ãã倧ããå Žåã¯ãã®æ¡ãåèšããŸãããããã£ãŠã
7
ã 2 åãããš14
ã«ãªãã1 + 4 = 5
ã«ãªããŸãã -
2 åã«ããŠããªãæ°åãš 2 åã«ããæ°åããã¹ãŠåèšããŸãã
-
ã¯ã¬ãžãã ã«ãŒãçªå·ã¯ãåèšã
0
ã§çµããå Žåã«æå¹ã§ãã
The provided code provides a buggy implementation of the luhn algorithm, along with two basic unit tests that confirm that most of the algorithm is implemented correctly.
以äžã®ã³ãŒãã https://play.rust-lang.org/ ã«ã³ããŒããæäŸãããå®è£ ã®ãã°ãçºèŠããããã®è¿œå ã®ãã¹ããèšè¿°ããèŠã€ãããã°ãä¿®æ£ããŠãã ããã
#![allow(unused)] fn main() { pub fn luhn(cc_number: &str) -> bool { let mut sum = 0; let mut double = false; for c in cc_number.chars().rev() { if let Some(digit) = c.to_digit(10) { if double { let double_digit = digit * 2; sum += if double_digit > 9 { double_digit - 9 } else { double_digit }; } else { sum += digit; } double = !double; } else { continue; } } sum % 10 == 0 } #[cfg(test)] mod test { use super::*; #[test] fn test_valid_cc_number() { assert!(luhn("4263 9826 4026 9299")); assert!(luhn("4539 3195 0343 6467")); assert!(luhn("7992 7398 713")); } #[test] fn test_invalid_cc_number() { assert!(!luhn("4223 9826 4026 9299")); assert!(!luhn("4539 3195 0343 6476")); assert!(!luhn("8273 1232 7352 0569")); } } }
解ç
// ããã¯åé¡ã«èšè¿°ãããŠãããã°ã®ããããŒãžã§ã³ã§ãã #[cfg(never)] pub fn luhn(cc_number: &str) -> bool { let mut sum = 0; let mut double = false; for c in cc_number.chars().rev() { if let Some(digit) = c.to_digit(10) { if double { let double_digit = digit * 2; sum += if double_digit > 9 { double_digit - 9 } else { double_digit }; } else { sum += digit; } double = !double; } else { continue; } } sum % 10 == 0 } // ããã¯è§£çã§ã以äžã®ãã¹ãŠã®ãã¹ãã«åæ ŒããŸãã pub fn luhn(cc_number: &str) -> bool { let mut sum = 0; let mut double = false; let mut digits = 0; for c in cc_number.chars().rev() { if let Some(digit) = c.to_digit(10) { digits += 1; if double { let double_digit = digit * 2; sum += if double_digit > 9 { double_digit - 9 } else { double_digit }; } else { sum += digit; } double = !double; } else if c.is_whitespace() { // New: accept whitespace. continue; } else { // New: reject all other characters. return false; } } // New: check that we have at least two digits digits >= 2 && sum % 10 == 0 } fn main() { let cc_number = "1234 5678 1234 5670"; println!( "Is {cc_number} a valid credit card number? {}", if luhn(cc_number) { "yes" } else { "no" } ); } #[cfg(test)] mod test { use super::*; #[test] fn test_valid_cc_number() { assert!(luhn("4263 9826 4026 9299")); assert!(luhn("4539 3195 0343 6467")); assert!(luhn("7992 7398 713")); } #[test] fn test_invalid_cc_number() { assert!(!luhn("4223 9826 4026 9299")); assert!(!luhn("4539 3195 0343 6476")); assert!(!luhn("8273 1232 7352 0569")); } #[test] fn test_non_digit_cc_number() { assert!(!luhn("foo")); assert!(!luhn("foo 0 0")); } #[test] fn test_empty_cc_number() { assert!(!luhn("")); assert!(!luhn(" ")); assert!(!luhn(" ")); assert!(!luhn(" ")); } #[test] fn test_single_digit_cc_number() { assert!(!luhn("0")); } #[test] fn test_two_digit_cc_number() { assert!(luhn(" 0 0 ")); } }
ãããã
Including 10 minute breaks, this session should take about 2 hours and 20 minutes. It contains:
Segment | Duration |
---|---|
ãšã©ãŒåŠç | 1 hour and 5 minutes |
Unsafe Rust | 1 hour and 5 minutes |
ãšã©ãŒåŠç
This segment should take about 1 hour and 5 minutes. It contains:
Slide | Duration |
---|---|
ãããã¯ïŒpanicïŒ | 3 minutes |
Result | 5 minutes |
TryæŒç®å | 5 minutes |
Tryå€æ | 5 minutes |
Errorãã¬ã€ã | 5 minutes |
thiserror | 5 minutes |
anyhow | 5 minutes |
æŒç¿: Result ã䜿çšããæžãæã | 30 minutes |
ãããã¯ïŒpanicïŒ
Rust ã¯ããããã¯ãã䜿çšããŠèŽåœçãªãšã©ãŒãåŠçããŸãã
å®è¡æã«èŽåœçãªãšã©ãŒãçºçãããšãRust ã¯ãããã¯ãããªã¬ãŒããŸãã
fn main() { let v = vec![10, 20, 30]; println!("v[100]: {}", v[100]); }
- ãããã¯ã¯ãå埩äžèœãªãšã©ãŒãäºæããªããšã©ãŒã«äœ¿çšããããã®ãã®ã§ãã
- ãããã¯ã¯ããã°ã©ã ã«ãã°ãããããšã®å åã§ãã
- ã©ã³ã¿ã€ã ãšã©ãŒïŒå¢çãã§ãã¯ã®å€±æãªã©ïŒã¯ããããã¯ã«ãªãå ŽåããããŸãã
- ã¢ãµãŒã·ã§ã³ïŒ
assert!
ãªã©ïŒã¯å€±ææã«ãããã¯ã«ãªããŸãã - ç¹å®ã®ç®çã§ãããã¯ãããŠããå Žåã«ã¯ã
panic!
ãã¯ãã䜿çšã§ããŸãã
- ãããã¯ãçºçãããšãã¹ã¿ãã¯ããã¢ã³ã¯ã€ã³ãããããé¢æ°ããªã¿ãŒã³ããããã®ããã«å€ããããããããŸãã
- ã¯ã©ãã·ã¥ã蚱容ãããªãå Žåã¯ããããã¯ãçºçããªã APIïŒ
Vec::get
ãªã©ïŒã䜿çšããŸãã
ããã©ã«ãã§ã¯ããããã¯ãçºçãããšã¹ã¿ãã¯ã¯ã¢ã³ã¯ã€ã³ããããŸããã¢ã³ã¯ã€ã³ãã¯ä»¥äžã®ããã«ãã£ããã§ããŸãã
use std::panic; fn main() { let result = panic::catch_unwind(|| "No problem here!"); println!("{result:?}"); let result = panic::catch_unwind(|| { panic!("oh no!"); }); println!("{result:?}"); }
- ãã£ããã¯äžè¬çã§ã¯ãªãããã
catch_unwind
ã䜿çšããŠäŸå€åŠçãå®è£ ããããšããªãã§ãã ããã - ããã¯ã1 ã€ã®ãªã¯ãšã¹ããã¯ã©ãã·ã¥ããå Žåã§ãå®è¡ãç¶ããå¿ èŠããããµãŒããŒã§æçšã§ãã
- ããã¯ã
Cargo.toml
ã§panic = 'abort'
ãèšå®ãããŠããå Žåã¯æ©èœããŸããã
Result
Our primary mechanism for error handling in Rust is the Result
enum, which we briefly saw when discussing standard library types.
use std::fs::File; use std::io::Read; fn main() { let file: Result<File, std::io::Error> = File::open("diary.txt"); match file { Ok(mut file) => { let mut contents = String::new(); if let Ok(bytes) = file.read_to_string(&mut contents) { println!("Dear diary: {contents} ({bytes} bytes)"); } else { println!("Could not read file content"); } } Err(err) => { println!("The diary could not be opened: {err}"); } } }
-
Result
has two variants:Ok
which contains the success value, andErr
which contains an error value of some kind. -
Whether or not a function can produce an error is encoded in the function's type signature by having the function return a
Result
value. -
Like with
Option
, there is no way to forget to handle an error: You cannot access either the success value or the error value without first pattern matching on theResult
to check which variant you have. Methods likeunwrap
make it easier to write quick-and-dirty code that doesn't do robust error handling, but means that you can always see in your source code where proper error handling is being skipped.
ãã®ä»
It may be helpful to compare error handling in Rust to error handling conventions that students may be familiar with from other programming languages.
äŸå€
-
Many languages use exceptions, e.g. C++, Java, Python.
-
In most languages with exceptions, whether or not a function can throw an exception is not visible as part of its type signature. This generally means that you can't tell when calling a function if it may throw an exception or not.
-
Exceptions generally unwind the call stack, propagating upward until a
try
block is reached. An error originating deep in the call stack may impact an unrelated function further up.
Error Numbers
-
Some languages have functions return an error number (or some other error value) separately from the successful return value of the function. Examples include C and Go.
-
Depending on the language it may be possible to forget to check the error value, in which case you may be accessing an uninitialized or otherwise invalid success value.
TryæŒç®å
connection-refused ã file-not-found ãªã©ã®ã©ã³ã¿ã€ã ãšã©ãŒã¯ Result
åã§åŠçãããŸããããã¹ãŠã®åŒã³åºãã§ãã®åãç
§åããã®ã¯é¢åãªå ŽåããããŸããtry æŒç®å ?
ã¯ãåŒã³åºãå
ã«ãšã©ãŒãè¿ãã®ã«äœ¿çšãããŸããããã«ãããäžè¬çãªä»¥äžã®ã³ãŒãããã¯ããã«ã·ã³ãã«ãªã³ãŒãã«å€æã§ããŸãã
match some_expression {
Ok(value) => value,
Err(err) => return Err(err),
}
å€æåŸã®ã³ãŒã:
some_expression?
ãã®æŒç®åã䜿çšããããšã§ããšã©ãŒåŠçã³ãŒããç°¡çŽ åã§ããŸãã
use std::io::Read; use std::{fs, io}; fn read_username(path: &str) -> Result<String, io::Error> { let username_file_result = fs::File::open(path); let mut username_file = match username_file_result { Ok(file) => file, Err(err) => return Err(err), }; let mut username = String::new(); match username_file.read_to_string(&mut username) { Ok(_) => Ok(username), Err(err) => Err(err), } } fn main() { //fs::write("config.dat", "alice").unwrap(); let username = read_username("config.dat"); println!("username or error: {username:?}"); }
? ã䜿çšã㊠read_username
é¢æ°ãç°¡çŽ åããŸãã
èŠç¹ïŒ
username
å€æ°ã¯ãOk(string)
ãŸãã¯Err(error)
ã®ããããã«ãªããŸããfs::write
åŒã³åºãã䜿çšããŠãããŸããŸãªã·ããªãªïŒãã¡ã€ã«ããªãã空ã®ãã¡ã€ã«ããŠãŒã¶ãŒåã®ãããã¡ã€ã«ãªã©ïŒããã¹ãããŸãã- Note that
main
can return aResult<(), E>
as long as it implementsstd::process::Termination
. In practice, this means thatE
implementsDebug
. The executable will print theErr
variant and return a nonzero exit status on error.
Tryå€æ
?
ãå®éã«å±éãããšãåè¿°ã®ã³ãŒããããå°ãè€éãªã³ãŒãã«ãªããŸãã
expression?
äžã®ã³ãŒãã¯ã以äžãšåãããã«åäœããŸãã
match expression {
Ok(value) => value,
Err(err) => return Err(From::from(err)),
}
ããã§ã® From::from
åŒã³åºãã¯ããšã©ãŒåãé¢æ°ãè¿ãåã«å€æããããšããŠããããšãæå³ããŸããããã«ããããšã©ãŒãäžäœã¬ãã«ã®ãšã©ãŒã«ç°¡åã«ã«ãã»ã«åã§ããŸãã
äŸ
use std::error::Error; use std::io::Read; use std::{fmt, fs, io}; #[derive(Debug)] enum ReadUsernameError { IoError(io::Error), EmptyUsername(String), } impl Error for ReadUsernameError {} impl fmt::Display for ReadUsernameError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::IoError(e) => write!(f, "I/O error: {e}"), Self::EmptyUsername(path) => write!(f, "Found no username in {path}"), } } } impl From<io::Error> for ReadUsernameError { fn from(err: io::Error) -> Self { Self::IoError(err) } } fn read_username(path: &str) -> Result<String, ReadUsernameError> { let mut username = String::with_capacity(100); fs::File::open(path)?.read_to_string(&mut username)?; if username.is_empty() { return Err(ReadUsernameError::EmptyUsername(String::from(path))); } Ok(username) } fn main() { //std::fs::write("config.dat", "").unwrap(); let username = read_username("config.dat"); println!("username or error: {username:?}"); }
?
æŒç®åã¯ãé¢æ°ã®æ»ãå€ã®åãšäºææ§ã®ããå€ãè¿ãå¿
èŠããããŸããã€ãŸããResult
ã®å Žåããšã©ãŒåã«äºææ§ããªããã°ãªããŸãããResult<T, ErrorOuter>
ãè¿ãé¢æ°ã¯ãErrorOuter
ãš ErrorInner
ãåãåã§ããããErrorOuter
ã From<ErrorInner>
ãå®è£
ããŠããå Žåã«ã®ã¿ãå Result<U, ErrorInner>
ã®å€ã« ?
ã䜿çšã§ããŸãã
ç¹ã«å€æã 1 ãæã§ã®ã¿çºçããå Žåã¯ãFrom
ãå®è£
ãã代ããã« Result::map_err ã䜿çšããã®ãäžè¬çã§ãã
Option
ã«ã¯äºææ§ã®èŠä»¶ã¯ãããŸãããOption<T>
ãè¿ãé¢æ°ã¯ãä»»æã®TåãšUåã«å¯ŸããŠã?æŒç®åãOption
Result
ãè¿ãé¢æ°ã§ã¯ Option
ã« ?
ã䜿çšã§ããŸããããã®éãåæ§ã§ãããã ããOption::ok_or
㯠Option
ã Result
ã«å€æã§ããResult::ok
㯠Result
ã Option
ã«å€æã§ããŸãã
åçãªãšã©ãŒå
ããŸããŸãªå¯èœæ§ãã«ããŒããç¬èªã®åæåãèšè¿°ããããšãªããããããçš®é¡ã®ãšã©ãŒãè¿ããããã«ãããå ŽåããããŸããstd::error::Error
ãã¬ã€ãã䜿çšãããšããããããšã©ãŒãå«ããããšãã§ãããã¬ã€ã ãªããžã§ã¯ããç°¡åã«äœæã§ããŸãã
use std::error::Error; use std::fs; use std::io::Read; fn read_count(path: &str) -> Result<i32, Box<dyn Error>> { let mut count_str = String::new(); fs::File::open(path)?.read_to_string(&mut count_str)?; let count: i32 = count_str.parse()?; Ok(count) } fn main() { fs::write("count.dat", "1i3").unwrap(); match read_count("count.dat") { Ok(count) => println!("Count: {count}"), Err(err) => println!("Error: {err}"), } }
read_count
é¢æ°ã¯ãstd::io::Error
ïŒãã¡ã€ã« ãªãã¬ãŒã·ã§ã³ããïŒãŸã㯠std::num::ParseIntError
ïŒString::parse
ããïŒãè¿ãããšãã§ããŸãã
ãšã©ãŒãããã¯ã¹åããããšã§ã³ãŒããç¯çŽã§ããŸãããããã°ã©ã ã§ç°ãªããšã©ãŒã±ãŒã¹ãç°ãªãæ¹æ³ã§é©åã«åŠçããæ©èœã倱ãããŸãããã®ãããã©ã€ãã©ãªã®å
¬é API 㧠Box<dyn Error>
ã䜿çšããããšã¯éåžžããããããŸãããããšã©ãŒ ã¡ãã»ãŒãžãã©ããã«è¡šç€ºãããã ãã®ããã°ã©ã ã§ã¯é©åãªéžæè¢ãšãªãããŸãã
Make sure to implement the std::error::Error
trait when defining a custom error type so it can be boxed.
thiserror
The thiserror
crate provides macros to help avoid boilerplate when defining error types. It provides derive macros that assist in implementing From<T>
, Display
, and the Error
trait.
use std::io::Read; use std::{fs, io}; use thiserror::Error; #[derive(Debug, Error)] enum ReadUsernameError { #[error("I/O error: {0}")] IoError(#[from] io::Error), #[error("Found no username in {0}")] EmptyUsername(String), } fn read_username(path: &str) -> Result<String, ReadUsernameError> { let mut username = String::with_capacity(100); fs::File::open(path)?.read_to_string(&mut username)?; if username.is_empty() { return Err(ReadUsernameError::EmptyUsername(String::from(path))); } Ok(username) } fn main() { //fs::write("config.dat", "").unwrap(); match read_username("config.dat") { Ok(username) => println!("Username: {username}"), Err(err) => println!("Error: {err:?}"), } }
Error
å°åºãã¯ãã¯thiserror
ã«ãã£ãŠæäŸãããŸãããã®ãã¯ãã«ã¯ããšã©ãŒåãç°¡æœã«å®çŸ©ããã®ã«åœ¹ç«ã€å±æ§ãæ°å€ãçšæãããŠããŸãã#[error]
ããã®ã¡ãã»ãŒãžã¯ãDisplay
ãã¬ã€ããå°åºããããã«äœ¿çšãããŸãã- Note that the (
thiserror::
)Error
derive macro, while it has the effect of implementing the (std::error::
)Error
trait, is not the same this; traits and macros do not share a namespace.
anyhow
The anyhow
crate provides a rich error type with support for carrying additional contextual information, which can be used to provide a semantic trace of what the program was doing leading up to the error.
This can be combined with the convenience macros from thiserror
to avoid writing out trait impls explicitly for custom error types.
use anyhow::{bail, Context, Result}; use std::fs; use std::io::Read; use thiserror::Error; #[derive(Clone, Debug, Eq, Error, PartialEq)] #[error("Found no username in {0}")] struct EmptyUsernameError(String); fn read_username(path: &str) -> Result<String> { let mut username = String::with_capacity(100); fs::File::open(path) .with_context(|| format!("Failed to open {path}"))? .read_to_string(&mut username) .context("Failed to read")?; if username.is_empty() { bail!(EmptyUsernameError(path.to_string())); } Ok(username) } fn main() { //fs::write("config.dat", "").unwrap(); match read_username("config.dat") { Ok(username) => println!("Username: {username}"), Err(err) => println!("Error: {err:?}"), } }
anyhow::Error
ã¯åºæ¬çã«Box<dyn Error>
ã®ã©ãããŒãšãªã£ãŠããŸãããã®ãããã©ã€ãã©ãªã®å ¬é API ãšããŠã¯äžè¬çã«ã¯é©ããŠããŸããããã¢ããªã§ã¯åºã䜿çšãããŠããŸããanyhow::Result<V>
ã¯Result<V, anyhow::Error>
ã®åãšã€ãªã¢ã¹ã§ãã- Functionality provided by
anyhow::Error
may be familiar to Go developers, as it provides similar behavior to the Goerror
type andResult<T, anyhow::Error>
is much like a Go(T, error)
(with the convention that only one element of the pair is meaningful). anyhow::Context
ã¯ãæšæºã®Result
åãšOption
åã«å®è£ ããããã¬ã€ãã§ãããããã®åã§.context()
ãš.with_context()
ãæå¹ã«ããã«ã¯ãuse anyhow::Context
ãå¿ èŠã§ãã
ãã®ä»
anyhow::Error
has support for downcasting, much likestd::any::Any
; the specific error type stored inside can be extracted for examination if desired withError::downcast
.
æŒç¿: Result ã䜿çšããæžãæã
The following implements a very simple parser for an expression language. However, it handles errors by panicking. Rewrite it to instead use idiomatic error handling and propagate errors to a return from main
. Feel free to use thiserror
and anyhow
.
Hint: start by fixing error handling in the
parse
function. Once that is working correctly, updateTokenizer
to implementIterator<Item=Result<Token, TokenizerError>>
and handle that in the parser.
use std::iter::Peekable; use std::str::Chars; /// ç®è¡æŒç®åã #[derive(Debug, PartialEq, Clone, Copy)] enum Op { Add, Sub, } /// åŒèšèªã®ããŒã¯ã³ #[derive(Debug, PartialEq)] enum Token { Number(String), Identifier(String), Operator(Op), } /// åŒèšèªã®åŒ #[derive(Debug, PartialEq)] enum Expression { /// å€æ°ãžã®åç §ã Var(String), /// ãªãã©ã«æ°å€ã Number(u32), /// ãã€ããªæŒç®ã Operation(Box<Expression>, Op, Box<Expression>), } fn tokenize(input: &str) -> Tokenizer { return Tokenizer(input.chars().peekable()); } struct Tokenizer<'a>(Peekable<Chars<'a>>); impl<'a> Tokenizer<'a> { fn collect_number(&mut self, first_char: char) -> Token { let mut num = String::from(first_char); while let Some(&c @ '0'..='9') = self.0.peek() { num.push(c); self.0.next(); } Token::Number(num) } fn collect_identifier(&mut self, first_char: char) -> Token { let mut ident = String::from(first_char); while let Some(&c @ ('a'..='z' | '_' | '0'..='9')) = self.0.peek() { ident.push(c); self.0.next(); } Token::Identifier(ident) } } impl<'a> Iterator for Tokenizer<'a> { type Item = Token; fn next(&mut self) -> Option<Token> { let c = self.0.next()?; match c { '0'..='9' => Some(self.collect_number(c)), 'a'..='z' => Some(self.collect_identifier(c)), '+' => Some(Token::Operator(Op::Add)), '-' => Some(Token::Operator(Op::Sub)), _ => panic!("Unexpected character {c}"), } } } fn parse(input: &str) -> Expression { let mut tokens = tokenize(input); fn parse_expr<'a>(tokens: &mut Tokenizer<'a>) -> Expression { let Some(tok) = tokens.next() else { panic!("Unexpected end of input"); }; let expr = match tok { Token::Number(num) => { let v = num.parse().expect("Invalid 32-bit integer"); Expression::Number(v) } Token::Identifier(ident) => Expression::Var(ident), Token::Operator(_) => panic!("Unexpected token {tok:?}"), }; // ãã€ããªæŒç®ãååšããå Žåã¯ããŒã¹ããŸãã match tokens.next() { None => expr, Some(Token::Operator(op)) => Expression::Operation( Box::new(expr), op, Box::new(parse_expr(tokens)), ), Some(tok) => panic!("Unexpected token {tok:?}"), } } parse_expr(&mut tokens) } fn main() { let expr = parse("10+foo+20-30"); println!("{expr:?}"); }
解ç
use thiserror::Error; use std::iter::Peekable; use std::str::Chars; /// ç®è¡æŒç®åã #[derive(Debug, PartialEq, Clone, Copy)] enum Op { Add, Sub, } /// åŒèšèªã®ããŒã¯ã³ #[derive(Debug, PartialEq)] enum Token { Number(String), Identifier(String), Operator(Op), } /// åŒèšèªã®åŒ #[derive(Debug, PartialEq)] enum Expression { /// å€æ°ãžã®åç §ã Var(String), /// ãªãã©ã«æ°å€ã Number(u32), /// ãã€ããªæŒç®ã Operation(Box<Expression>, Op, Box<Expression>), } fn tokenize(input: &str) -> Tokenizer { return Tokenizer(input.chars().peekable()); } #[derive(Debug, Error)] enum TokenizerError { #[error("Unexpected character '{0}' in input")] UnexpectedCharacter(char), } struct Tokenizer<'a>(Peekable<Chars<'a>>); impl<'a> Tokenizer<'a> { fn collect_number(&mut self, first_char: char) -> Token { let mut num = String::from(first_char); while let Some(&c @ '0'..='9') = self.0.peek() { num.push(c); self.0.next(); } Token::Number(num) } fn collect_identifier(&mut self, first_char: char) -> Token { let mut ident = String::from(first_char); while let Some(&c @ ('a'..='z' | '_' | '0'..='9')) = self.0.peek() { ident.push(c); self.0.next(); } Token::Identifier(ident) } } impl<'a> Iterator for Tokenizer<'a> { type Item = Result<Token, TokenizerError>; fn next(&mut self) -> Option<Result<Token, TokenizerError>> { let c = self.0.next()?; match c { '0'..='9' => Some(Ok(self.collect_number(c))), 'a'..='z' | '_' => Some(Ok(self.collect_identifier(c))), '+' => Some(Ok(Token::Operator(Op::Add))), '-' => Some(Ok(Token::Operator(Op::Sub))), _ => Some(Err(TokenizerError::UnexpectedCharacter(c))), } } } #[derive(Debug, Error)] enum ParserError { #[error("Tokenizer error: {0}")] TokenizerError(#[from] TokenizerError), #[error("Unexpected end of input")] UnexpectedEOF, #[error("Unexpected token {0:?}")] UnexpectedToken(Token), #[error("Invalid number")] InvalidNumber(#[from] std::num::ParseIntError), } fn parse(input: &str) -> Result<Expression, ParserError> { let mut tokens = tokenize(input); fn parse_expr<'a>( tokens: &mut Tokenizer<'a>, ) -> Result<Expression, ParserError> { let tok = tokens.next().ok_or(ParserError::UnexpectedEOF)??; let expr = match tok { Token::Number(num) => { let v = num.parse()?; Expression::Number(v) } Token::Identifier(ident) => Expression::Var(ident), Token::Operator(_) => return Err(ParserError::UnexpectedToken(tok)), }; // ãã€ããªæŒç®ãååšããå Žåã¯ããŒã¹ããŸãã Ok(match tokens.next() { None => expr, Some(Ok(Token::Operator(op))) => Expression::Operation( Box::new(expr), op, Box::new(parse_expr(tokens)?), ), Some(Err(e)) => return Err(e.into()), Some(Ok(tok)) => return Err(ParserError::UnexpectedToken(tok)), }) } parse_expr(&mut tokens) } fn main() -> anyhow::Result<()> { let expr = parse("10+foo+20-30")?; println!("{expr:?}"); Ok(()) }
Unsafe Rust
This segment should take about 1 hour and 5 minutes. It contains:
Slide | Duration |
---|---|
ã¢ã³ã»ãŒã | 5 minutes |
çãã€ã³ã¿ã®åç §å€ã | 10 minutes |
å¯å€ãªstaticå€æ° | 5 minutes |
å ±çšäœ | 5 minutes |
Unsafeé¢æ°ã®åŒã³åºã | 5 minutes |
Unsafeãªãã¬ã€ãã®å®è£ | 5 minutes |
æŒç¿: FFIã©ãã㌠| 30 minutes |
Unsafe Rust
Rust èšèªã¯ 2 ã€ã®éšåã§æ§æãããŠããŸãã
- å®å šãª Rust: ã¡ã¢ãªã»ãŒãã§ãæªå®çŸ©ã®åäœã¯èµ·ããããŸããã
- ã¢ã³ã»ãŒãRust: åææ¡ä»¶ã«éåããå Žåãæªå®çŸ©ã®åäœãããªã¬ãŒãããå¯èœæ§ããããŸãã
ãã®ã³ãŒã¹ã§ã¯äž»ã«å®å šãª Rust ãèŠãŠããŸããããå®å šã§ãªã Rust ãšã¯äœããç解ããŠããããšãéèŠã§ãã
ã¢ã³ã»ãŒããªã³ãŒãã¯éåžžãå°èŠæš¡ã§åé¢ãããŠããããã®æ£ç¢ºæ§ã¯æ éã«ææžåãããŠããå¿ èŠããããŸããéåžžã¯å®å šãªæœè±¡åã¬ã€ã€ã§ã©ãããããŠããŸãã
ã¢ã³ã»ãŒãRustã§ã¯ã次㮠5 ã€ã®æ°æ©èœãå©çšã§ããŸãã
- çãã€ã³ã¿ã®åç §å€ãã
- å¯å€ã®éçå€æ°ãžã®ã¢ã¯ã»ã¹ãŸãã¯å€æŽã
union
ãã£ãŒã«ããžã®ã¢ã¯ã»ã¹ãextern
é¢æ°ãå«ãunsafe
é¢æ°ã®åŒã³åºããunsafe
ãã¬ã€ãã®å®è£ ã
次ã«ãå®å šã§ãªãæ©èœã«ã€ããŠç°¡åã«èª¬æããŸãã詳ããã¯ãRust Book ã®ç¬¬ 19.1 ç« ãšãRustonomicon ãã芧ãã ããã
ã¢ã³ã»ãŒãRustã¯ãã³ãŒããæ£ãããªãããšãæå³ãããã®ã§ã¯ãããŸãããããããããŒãäžéšã®ã³ã³ãã€ã©å®å šæ§æ©èœããªãã«ããèªåã§æ£ããã³ãŒããèšè¿°ããªããã°ãªããªãããšãæå³ããŸãããŸããã³ã³ãã€ã©ãRustã®ã¡ã¢ãªå®å šæ§ã«é¢ããã«ãŒã«ã匷å¶ããªããªããšããããšãæå³ããŸãã
çãã€ã³ã¿ã®åç §å€ã
ãã€ã³ã¿ã®äœæã¯å®å
šã§ãããåç
§å€ãã«ã¯ unsafe
ãå¿
èŠã§ãã
fn main() { let mut s = String::from("careful!"); let r1 = &raw mut s; let r2 = r1 as *const String; // SAFETY: r1 and r2 were obtained from references and so are guaranteed to // be non-null and properly aligned, the objects underlying the references // from which they were obtained are live throughout the whole unsafe // block, and they are not accessed either through the references or // concurrently through any other pointers. unsafe { println!("r1 is: {}", *r1); *r1 = String::from("uhoh"); println!("r2 is: {}", *r2); } // å®å šã§ãªããããNOT SAFEããã®ãããªèšè¿°ãããªãã§ãã ããã /* let r3: &String = unsafe { &*r1 }; drop(s); println!("r3 is: {}", *r3); */ }
unsafe
ãããã¯ããšã«ã³ã¡ã³ããèšè¿°ãããã®ãããã¯å
ã®ã³ãŒããè¡ãã¢ã³ã»ãŒããªæäœãã©ã®ããã«å®å
šæ§èŠä»¶ãæºãããŠããã®ããèšè¿°ããããšãããããããŸãïŒAndroid Rust ã¹ã¿ã€ã«ã¬ã€ãã§ãå¿
é ãšãããŠããŸãïŒã
ãã€ã³ã¿åç §å€ãã®å Žåãããã¯ãã€ã³ã¿ã valid ã§ãªããã°ãªããªãããšãæå³ããŸããã€ãŸãã次ã®ããã«ãªããŸãã
- ãã€ã³ã¿ã¯ null 以å€ã§ãªããã°ãªããªãããšã
- ãã€ã³ã¿ã¯ãïŒå²ãåœãŠãããåäžã®ãªããžã§ã¯ãã®å¢çå ã§ïŒåç §å€ãå¯èœã§ãªããã°ãªããªãã
- ãªããžã§ã¯ãã解æŸãããŠããªãããšã
- åããã±ãŒã·ã§ã³ã«åæã¢ã¯ã»ã¹ããããšããªãããšã
- åç §ããã£ã¹ãããŠãã€ã³ã¿ãååŸããå Žåãåºã«ãªããªããžã§ã¯ããåç¶ããªããã°ãªãããä»ã®ãããªãåç §ãéããŠããã®ã¡ã¢ãªã«ã¢ã¯ã»ã¹ããªãããš
ã»ãšãã©ã®å Žåããã€ã³ã¿ãé©åã«ã¢ã©ã€ã³ãããå¿ èŠããããŸãã
"NOT SAFE"ãšããã³ã¡ã³ãããããšããã¯ãããããUBãã°ã®äŸã瀺ããŠããŸãã*r1
ã®ã©ã€ãã¿ã€ã 㯠'static
ã§ãããããr3
ã®å㯠&'static String
ãšãªããs
ããé·ãåç¶ããŸãããã€ã³ã¿ããã®åç
§ã®äœæã«ã¯çŽ°å¿ã®æ³šæãå¿
èŠã§ãã
å¯å€ãªstaticå€æ°
äžå€ã®éçå€æ°ã¯å®å šã«èªã¿åãããšãã§ããŸãã
static HELLO_WORLD: &str = "Hello, world!"; fn main() { println!("HELLO_WORLD: {HELLO_WORLD}"); }
ãã ããããŒã¿ç«¶åãçºçããå¯èœæ§ããããããå¯å€éçå€æ°ã®èªã¿åããšæžã蟌ã¿ã¯å®å šã§ã¯ãããŸããã
static mut COUNTER: u32 = 0; fn add_to_counter(inc: u32) { // SAFETY: There are no other threads which could be accessing `COUNTER`. unsafe { COUNTER += inc; } } fn main() { add_to_counter(42); // SAFETY: There are no other threads which could be accessing `COUNTER`. unsafe { println!("COUNTER: {COUNTER}"); } }
-
ãã®ããã°ã©ã ã¯ã·ã³ã°ã«ã¹ã¬ãããªã®ã§å®å šã§ããããããRust ã³ã³ãã€ã©ã¯ä¿å®çã§ãææªã®äºæ ãæ³å®ããŸãã
unsafe
ãåé€ãããšãè€æ°ã®ã¹ã¬ããããéçå€æ°ãå€æŽããããšã¯æªå®çŸ©ã®åäœã§ããããšã説æããã¡ãã»ãŒãžãã³ã³ãã€ã©ã«ãã衚瀺ãããã¯ãã§ãã -
äžè¬çã«ãå¯å€éçå€æ°ã䜿çšããããšã¯ããããããŸããããããŒã ã¢ãã±ãŒã¿ã®å®è£ ãäžéšã® C API ã®æäœãªã©ãäœã¬ãã«ã®
no_std
ã³ãŒãã§ã¯é©ããŠããå ŽåããããŸãã
å ±çšäœ
å ±çšäœã¯åæåã«äŒŒãŠããŸãããã¢ã¯ãã£ã ãã£ãŒã«ããèªåã§ãã©ããã³ã°ããå¿ èŠããããŸãã
#[repr(C)] union MyUnion { i: u8, b: bool, } fn main() { let u = MyUnion { i: 42 }; println!("int: {}", unsafe { u.i }); println!("bool: {}", unsafe { u.b }); // æªå®çŸ©ã®åäœ }
Rust ã§ã¯ãéåžžã¯åæåã䜿çšã§ãããããå ±çšäœã¯ã»ãšãã©å¿ èŠãããŸãããå ±çšäœã¯ãC ã©ã€ãã©ãª API ãšã®ããåãã§å¿ èŠã«ãªãããšããããŸãã
ãã€ããå¥ã®åãšããŠå解éãããå Žåã¯ãstd::mem::transmute
ããzerocopy
ã¯ã¬ãŒãã®ãããªå®å
šãªã©ãããŒã䜿çšããããšãããããããŸãã
Unsafeé¢æ°ã®åŒã³åºã
Unsafeé¢æ°ã®åŒã³åºã
æªå®çŸ©ã®åäœãåé¿ããããã«æºããå¿
èŠãããè¿œå ã®åææ¡ä»¶ãããé¢æ°ãŸãã¯ã¡ãœããã¯ãunsafe
ãšããŒã¯ã§ããŸãã
extern "C" { fn abs(input: i32) -> i32; } fn main() { let emojis = "ð»âð"; // SAFETY: The indices are in the correct order, within the bounds of the // string slice, and lie on UTF-8 sequence boundaries. unsafe { println!("emoji: {}", emojis.get_unchecked(0..4)); println!("emoji: {}", emojis.get_unchecked(4..7)); println!("emoji: {}", emojis.get_unchecked(7..11)); } println!("char count: {}", count_chars(unsafe { emojis.get_unchecked(0..7) })); // SAFETY: `abs` doesn't deal with pointers and doesn't have any safety // requirements. unsafe { println!("Absolute value of -3 according to C: {}", abs(-3)); } // UTF-8 ãšã³ã³ãŒãèŠä»¶ãæºãããªãå Žåãã¡ã¢ãªã®å®å šæ§ãæãªãããŸãã // println!("emoji: {}", unsafe { emojis.get_unchecked(0..3) }); // println!("char count: {}", count_chars(unsafe { // emojis.get_unchecked(0..3) })); } fn count_chars(s: &str) -> usize { s.chars().count() }
Unsafeé¢æ°ã®æžãæ¹
æªå®çŸ©ã®åäœãåé¿ããããã«ç¹å®ã®æ¡ä»¶ãå¿
èŠãªå Žåã¯ãç¬èªã®é¢æ°ã unsafe
ãšããŒã¯ã§ããŸãã
/// æå®ããããã€ã³ã¿ãæãå€ãã¹ã¯ããããŸãã /// /// # Safety /// /// ãã€ã³ã¿ãæå¹ã§ãé©åã«ã¢ã©ã€ã³ãããŠããå¿ èŠããããŸãã unsafe fn swap(a: *mut u8, b: *mut u8) { let temp = *a; *a = *b; *b = temp; } fn main() { let mut a = 42; let mut b = 66; // SAFETY: ... unsafe { swap(&mut a, &mut b); } println!("a = {}, b = {}", a, b); }
Unsafeé¢æ°ã®åŒã³åºã
get_unchecked
, like most _unchecked
functions, is unsafe, because it can create UB if the range is incorrect. abs
is unsafe for a different reason: it is an external function (FFI). Calling external functions is usually only a problem when those functions do things with pointers which might violate Rust's memory model, but in general any C function might have undefined behaviour under any arbitrary circumstances.
ãã®äŸã® "C"
㯠ABI ã§ãïŒä»ã® ABI ã䜿çšã§ããŸãïŒã
Unsafeé¢æ°ã®æžãæ¹
å®éã«ã¯ãswap
é¢æ°ã§ã¯ãã€ã³ã¿ã¯äœ¿çšããŸãããããã¯åç
§ã䜿çšããããšã§å®å
šã«å®è¡ã§ããŸãã
ã¢ã³ã»ãŒããªé¢æ°å
ã§ã¯ãã¢ã³ã»ãŒããªã³ãŒããunsafe
ãããã¯ãªãã«èšè¿°ããããšãã§ããŸãããã㯠#[deny(unsafe_op_in_unsafe_fn)]
ã§çŠæ¢ã§ããŸããè¿œå ãããšã©ããªããèŠãŠã¿ãŸããããããã¯ãä»åŸã® Rust ãšãã£ã·ã§ã³ã§å€æŽãããå¯èœæ§ããããŸãã
Unsafeãªãã¬ã€ãã®å®è£
é¢æ°ãšåæ§ã«ãæªå®çŸ©ã®åäœãåé¿ããããã«å®è£
ã§ç¹å®ã®æ¡ä»¶ãä¿èšŒããå¿
èŠãããå Žåã¯ããã¬ã€ãã unsafe
ãšããŠããŒã¯ã§ããŸãã
For example, the zerocopy
crate has an unsafe trait that looks something like this:
use std::{mem, slice}; /// ... /// # Safety /// åã«ã¯å®çŸ©ãããè¡šçŸãå¿ èŠã§ãããã£ã³ã°ããã£ãŠã¯ãªããŸããã pub unsafe trait IntoBytes { fn as_bytes(&self) -> &[u8] { let len = mem::size_of_val(self); unsafe { slice::from_raw_parts((&raw const self).cast::<u8>(), len) } } } // SAFETY: `u32` has a defined representation and no padding. unsafe impl IntoBytes for u32 {}
Rustdoc ã«ã¯ããã¬ã€ããå®å
šã«å®è£
ããããã®èŠä»¶ã«ã€ããŠèª¬æãã # Safety
ã»ã¯ã·ã§ã³ãå¿
èŠã§ãã
The actual safety section for IntoBytes
is rather longer and more complicated.
çµã¿èŸŒã¿ã® Send
ãã¬ã€ããš Sync
ãã¬ã€ãã¯ã¢ã³ã»ãŒãã§ãã
å®å šãªFFIã©ãã
Rust ã¯ãå€éšé¢æ°ã€ã³ã¿ãŒãã§ãŒã¹ïŒFFIïŒãä»ããé¢æ°åŒã³åºãã匷åã«ãµããŒãããŠããŸããããã䜿çšããŠããã£ã¬ã¯ããªå
ã®ãã¡ã€ã«åãèªã¿åãããã« Cããã°ã©ã ã§äœ¿çšãã libc
é¢æ°ã®å®å
šãªã©ãããŒãäœæããŸãã
以äžã®ããã¥ã¢ã« ããŒãžãã芧ãã ããã
std::ffi
ã¢ãžã¥ãŒã«ãåç
§ããŠãã ãããããã«ã¯ããã®æŒç¿ã§å¿
èŠãªæåååãå€æ°æ²èŒãããŠããŸãã
å | ãšã³ã³ãŒã | 䜿ã |
---|---|---|
str ãš String | UTF-8 | Rust ã§ã®ããã¹ãåŠç |
CStr ãš CString | NULçµç«¯æåå | C é¢æ°ãšã®éä¿¡ |
OsStr ãš OsString | OS åºæ | OS ãšã®éä¿¡ |
以äžã®ãã¹ãŠã®åéã§å€æãè¡ããŸãã
&str
ããCString
: æ«å°Ÿã®\0
æåã«ãé åãå²ãåœãŠãå¿ èŠããããŸããCString
ãã*const i8
: C é¢æ°ãåŒã³åºãããã®ãã€ã³ã¿ãå¿ èŠã§ãã*const i8
ãã&CStr
: æ«å°Ÿã®\0
æåãæ€åºã§ãããã®ãå¿ èŠã§ãã&CStr
ãã&[u8]
: ãã€ãã®ã¹ã©ã€ã¹ã¯ãäžæãªããŒã¿ãçšã®æ±çšç㪠ã€ã³ã¿ãŒãã§ãŒã¹ã§ãã&[u8]
ãã&OsStr
:&OsStr
ã¯OsString
ã«å€æããããã®äžéã¹ãããã§ãããOsStrExt
ã䜿çšããŠäœæããŸãã&OsStr
å ã®ããŒã¿ãè¿ããããã«åã³readdirãåŒã³åºããããã«ããããã«ã¯&OsStr
å ã®ããŒã¿ãã¯ããŒã³ããå¿ èŠããããŸãã
Nomicon ã«ããFFI ã«é¢ããæçãªç« ããããŸãã
以äžã®ã³ãŒãã https://play.rust-lang.org/ ã«ã³ããŒããäžè¶³ããŠããé¢æ°ãšã¡ãœãããèšå ¥ããŸãã
// TODO: å®è£ ãå®äºãããããããåé€ããŸãã #![allow(unused_imports, unused_variables, dead_code)] mod ffi { use std::os::raw::{c_char, c_int}; #[cfg(not(target_os = "macos"))] use std::os::raw::{c_long, c_uchar, c_ulong, c_ushort}; // ãªããŒã¯åãhttps://doc.rust-lang.org/nomicon/ffi.html ãã芧ãã ããã #[repr(C)] pub struct DIR { _data: [u8; 0], _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, } // readdir(3) ã® Linux ããã¥ã¢ã« ããŒãžã«æ²¿ã£ãã¬ã€ã¢ãŠããino_t ãš // off_t 㯠// /usr/include/x86_64-linux-gnu/{sys/types.h, bits/typesizes.h} ã®å®çŸ©ã«åŸã£ãŠè§£æ±ºãããŸãã #[cfg(not(target_os = "macos"))] #[repr(C)] pub struct dirent { pub d_ino: c_ulong, pub d_off: c_long, pub d_reclen: c_ushort, pub d_type: c_uchar, pub d_name: [c_char; 256], } // macOSããã¥ã¢ã« ããŒãžã®dir(5)ã«æ²¿ã£ãã¬ã€ã¢ãŠãã #[cfg(all(target_os = "macos"))] #[repr(C)] pub struct dirent { pub d_fileno: u64, pub d_seekoff: u64, pub d_reclen: u16, pub d_namlen: u16, pub d_type: u8, pub d_name: [c_char; 1024], } unsafe extern "C" { pub unsafe fn opendir(s: *const c_char) -> *mut DIR; #[cfg(not(all(target_os = "macos", target_arch = "x86_64")))] pub unsafe fn readdir(s: *mut DIR) -> *const dirent; // https://github.com/rust-lang/libc/issues/414ãããã³ macOS çããã¥ã¢ã« ããŒãžã®stat(2)ã«ããã // _DARWIN_FEATURE_64_BIT_INODE ã«é¢ããã»ã¯ã·ã§ã³ãã芧ãã ããã // // ããããã®ã¢ããããŒããå©çšå¯èœã«ãªãåã«ååšããŠãããã©ãããã©ãŒã ("Platforms that existed before these updates were available")ããšã¯ã // Intel ããã³ PowerPC äžã® macOSïŒiOS / wearOS ãªã©ã§ã¯ãªãïŒãæããŸãã #[cfg(all(target_os = "macos", target_arch = "x86_64"))] #[link_name = "readdir$INODE64"] pub unsafe fn readdir(s: *mut DIR) -> *const dirent; pub unsafe fn closedir(s: *mut DIR) -> c_int; } } use std::ffi::{CStr, CString, OsStr, OsString}; use std::os::unix::ffi::OsStrExt; #[derive(Debug)] struct DirectoryIterator { path: CString, dir: *mut ffi::DIR, } impl DirectoryIterator { fn new(path: &str) -> Result<DirectoryIterator, String> { // opendir ãåŒã³åºããæåããå Žå㯠Ok å€ãè¿ãã // ãã以å€ã®å Žåã¯ã¡ãã»ãŒãžãšãšãã« Err ãè¿ããŸãã unimplemented!() } } impl Iterator for DirectoryIterator { type Item = OsString; fn next(&mut self) -> Option<OsString> { // NULL ãã€ã³ã¿ãè¿ããããŸã§ readdir ãåŒã³åºãç¶ããŸãã unimplemented!() } } impl Drop for DirectoryIterator { fn drop(&mut self) { // å¿ èŠã«å¿ã㊠closedir ãåŒã³åºããŸãã unimplemented!() } } fn main() -> Result<(), String> { let iter = DirectoryIterator::new(".")?; println!("files: {:#?}", iter.collect::<Vec<_>>()); Ok(()) }
解ç
mod ffi { use std::os::raw::{c_char, c_int}; #[cfg(not(target_os = "macos"))] use std::os::raw::{c_long, c_uchar, c_ulong, c_ushort}; // ãªããŒã¯åãhttps://doc.rust-lang.org/nomicon/ffi.html ãã芧ãã ããã #[repr(C)] pub struct DIR { _data: [u8; 0], _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, } // readdir(3) ã® Linux ããã¥ã¢ã« ããŒãžã«æ²¿ã£ãã¬ã€ã¢ãŠããino_t ãš // off_t 㯠// /usr/include/x86_64-linux-gnu/{sys/types.h, bits/typesizes.h} ã®å®çŸ©ã«åŸã£ãŠè§£æ±ºãããŸãã #[cfg(not(target_os = "macos"))] #[repr(C)] pub struct dirent { pub d_ino: c_ulong, pub d_off: c_long, pub d_reclen: c_ushort, pub d_type: c_uchar, pub d_name: [c_char; 256], } // macOSããã¥ã¢ã« ããŒãžã®dir(5)ã«æ²¿ã£ãã¬ã€ã¢ãŠãã #[cfg(all(target_os = "macos"))] #[repr(C)] pub struct dirent { pub d_fileno: u64, pub d_seekoff: u64, pub d_reclen: u16, pub d_namlen: u16, pub d_type: u8, pub d_name: [c_char; 1024], } unsafe extern "C" { pub unsafe fn opendir(s: *const c_char) -> *mut DIR; #[cfg(not(all(target_os = "macos", target_arch = "x86_64")))] pub unsafe fn readdir(s: *mut DIR) -> *const dirent; // https://github.com/rust-lang/libc/issues/414ãããã³ macOS çããã¥ã¢ã« ããŒãžã®stat(2)ã«ããã // _DARWIN_FEATURE_64_BIT_INODE ã«é¢ããã»ã¯ã·ã§ã³ãã芧ãã ããã // // ããããã®ã¢ããããŒããå©çšå¯èœã«ãªãåã«ååšããŠãããã©ãããã©ãŒã ("Platforms that existed before these updates were available")ããšã¯ã // Intel ããã³ PowerPC äžã® macOSïŒiOS / wearOS ãªã©ã§ã¯ãªãïŒãæããŸãã #[cfg(all(target_os = "macos", target_arch = "x86_64"))] #[link_name = "readdir$INODE64"] pub unsafe fn readdir(s: *mut DIR) -> *const dirent; pub unsafe fn closedir(s: *mut DIR) -> c_int; } } use std::ffi::{CStr, CString, OsStr, OsString}; use std::os::unix::ffi::OsStrExt; #[derive(Debug)] struct DirectoryIterator { path: CString, dir: *mut ffi::DIR, } impl DirectoryIterator { fn new(path: &str) -> Result<DirectoryIterator, String> { // opendir ãåŒã³åºããæåããå Žå㯠Ok å€ãè¿ãã // ãã以å€ã®å Žåã¯ã¡ãã»ãŒãžãšãšãã« Err ãè¿ããŸãã let path = CString::new(path).map_err(|err| format!("Invalid path: {err}"))?; // SAFETY: path.as_ptr()ãNULLã§ããããšã¯ãããŸããã let dir = unsafe { ffi::opendir(path.as_ptr()) }; if dir.is_null() { Err(format!("Could not open {path:?}")) } else { Ok(DirectoryIterator { path, dir }) } } } impl Iterator for DirectoryIterator { type Item = OsString; fn next(&mut self) -> Option<OsString> { // NULL ãã€ã³ã¿ãè¿ããããŸã§ readdir ãåŒã³åºãç¶ããŸãã // SAFETY: self.dir ã¯æ±ºã㊠NULL ã«ãªããŸããã let dirent = unsafe { ffi::readdir(self.dir) }; if dirent.is_null() { // ãã£ã¬ã¯ããªã®æåŸã«å°éããŸããã return None; } // å®å š: dirent 㯠NULL ã§ã¯ãªããdirent.d_name 㯠NUL // æåçµç«¯ãããŠããŸãã let d_name = unsafe { CStr::from_ptr((*dirent).d_name.as_ptr()) }; let os_str = OsStr::from_bytes(d_name.to_bytes()); Some(os_str.to_owned()) } } impl Drop for DirectoryIterator { fn drop(&mut self) { // Call closedir as needed. // SAFETY: self.dir is never NULL. if unsafe { ffi::closedir(self.dir) } != 0 { panic!("Could not close {:?}", self.path); } } } fn main() -> Result<(), String> { let iter = DirectoryIterator::new(".")?; println!("files: {:#?}", iter.collect::<Vec<_>>()); Ok(()) } #[cfg(test)] mod tests { use super::*; use std::error::Error; #[test] fn test_nonexisting_directory() { let iter = DirectoryIterator::new("no-such-directory"); assert!(iter.is_err()); } #[test] fn test_empty_directory() -> Result<(), Box<dyn Error>> { let tmp = tempfile::TempDir::new()?; let iter = DirectoryIterator::new( tmp.path().to_str().ok_or("Non UTF-8 character in path")?, )?; let mut entries = iter.collect::<Vec<_>>(); entries.sort(); assert_eq!(entries, &[".", ".."]); Ok(()) } #[test] fn test_nonempty_directory() -> Result<(), Box<dyn Error>> { let tmp = tempfile::TempDir::new()?; std::fs::write(tmp.path().join("foo.txt"), "The Foo Diaries\n")?; std::fs::write(tmp.path().join("bar.png"), "<PNG>\n")?; std::fs::write(tmp.path().join("crab.rs"), "//! Crab\n")?; let iter = DirectoryIterator::new( tmp.path().to_str().ok_or("Non UTF-8 character in path")?, )?; let mut entries = iter.collect::<Vec<_>>(); entries.sort(); assert_eq!(entries, &[".", "..", "bar.png", "crab.rs", "foo.txt"]); Ok(()) } }
Android ã§ã® Rust ãžãããã
Rust 㯠Android ã®ã·ã¹ãã ãœãããŠã§ã¢ã§ãµããŒããããŠããŸããã€ãŸããæ°ãããµãŒãã¹ãã©ã€ãã©ãªããã©ã€ããããã«ã¯ãã¡ãŒã ãŠã§ã¢ã Rust ã§äœæã§ããŸãïŒãŸãã¯ãå¿ èŠã«å¿ããŠæ¢åã®ã³ãŒããæ¹åã§ããŸãïŒã
Android 㧠Rust ã䜿çšãããããšãå¢ããŠããããã次ã®ããããã«èšåããããšãããããããŸãã
-
Service example: DNS over HTTP.
-
Libraries: Rutabaga Virtual Graphics Interface.
-
Kernel Drivers: Binder.
-
Firmware: pKVM firmware.
ã»ããã¢ãã
ã³ãŒãã®ãã¹ãã®ããã«Cuttlefish Android Virtual Device ã䜿çšããŸããæ¢åã®Deviceãããã°ããã«ã¢ã¯ã»ã¹ã§ããããšã確èªããããã§ãªããã°ä»¥äžã®ã³ãã³ãã«ããäœæããŠãããŠãã ããã
source build/envsetup.sh
lunch aosp_cf_x86_64_phone-trunk_staging-userdebug
acloud create
詳ããã¯ãAndroid ããããã㌠Codelab ãã芧ãã ããã
The code on the following pages can be found in the src/android/
directory of the course material. Please git clone
the repository to follow along.
èŠç¹ïŒ
-
Cuttlefish ã¯ãäžè¬ç㪠Linux ãã¹ã¯ãããã§åäœããããã«èšèšããããªãã¡ã¬ã³ã¹ Android ããã€ã¹ã§ããmacOS ã®ãµããŒããäºå®ãããŠããŸãã
-
Cuttlefish ã·ã¹ãã ã€ã¡ãŒãžã¯ãå®éã®ããã€ã¹ã«å¯Ÿããé«ãå¿ å®åºŠãç¶æããŠãããããå€ãã® Rust ãŠãŒã¹ã±ãŒã¹ãå®è¡ããã®ã«çæ³çãªãšãã¥ã¬ãŒã¿ã§ãã
ãã«ãã®ã«ãŒã«
Android ãã«ãã·ã¹ãã ïŒSoongïŒã¯ãããŸããŸãªã¢ãžã¥ãŒã«ãéã㊠Rust ããµããŒãããŠããŸãã
ã¢ãžã¥ãŒã« ã¿ã€ã | 説æ |
---|---|
rust_binary | Rust ãã€ããªãçæããŸãã |
rust_library | Rust ã©ã€ãã©ãªãçæããrlib ãš dylib ã®äž¡æ¹ã®ããªã¢ã³ããæäŸããŸãã |
rust_ffi | cc ã¢ãžã¥ãŒã«ã§äœ¿çšã§ãã Rust C ã©ã€ãã©ãªãçæããéçããªã¢ã³ããšå
±æããªã¢ã³ãã®äž¡æ¹ãæäŸããŸãã |
rust_proc_macro | proc-macro Rust ã©ã€ãã©ãªãçæããŸãããããã¯ã³ã³ãã€ã© ãã©ã°ã€ã³ã«äŒŒãŠããŸãã |
rust_test | æšæºã® Rust ãã¹ãããŒãã¹ã䜿çšãã Rust ãã¹ããã€ããªãçæããŸãã |
rust_fuzz | libfuzzer ãå©çšããŠãRust ãã¡ãºãã€ããªãçæããŸãã |
rust_protobuf | ãœãŒã¹ãçæããç¹å®ã® protobuf çšã®ã€ã³ã¿ãŒãã§ãŒã¹ãæäŸãã Rust ã©ã€ãã©ãªãçæããŸãã |
rust_bindgen | ãœãŒã¹ãçæããC ã©ã€ãã©ãªãžã® Rust ãã€ã³ãã£ã³ã°ãå«ã Rust ã©ã€ãã©ãªãçæããŸãã |
次㫠rust_binary
ãš rust_library
ãèŠãŠãããŸãã
è¿œå ã§æ¬¡ã®é ç®ã«èšåããããšãããããããŸãã
-
Cargo ã¯å€èšèªãªããžããªçšã«æé©åãããŠããŸããããŸããã€ã³ã¿ãŒãããããããã±ãŒãžãããŠã³ããŒãããŸãã
-
ã³ã³ãã©ã€ã¢ã³ã¹ããã³ããã©ãŒãã³ã¹äžã®çç±ãããAndroid ã§ã¯ã¯ã¬ãŒããããªãŒå ã«é 眮ããå¿ èŠããããŸãããŸããC /C++ / Java ã³ãŒããšã®çžäºéçšæ§ãå¿ èŠã§ããSoong ã䜿çšããããšã§ããã®ã®ã£ãããåããããšãã§ããŸãã
-
Soong has many similarities to Bazel, which is the open-source variant of Blaze (used in google3).
-
è±ç¥è: ã¹ã¿ãŒãã¬ãã¯ã®ãããŒã¿ãã¯ãã¹ã³ïŒSoongïŒåã¢ã³ããã€ãã§ãã
Rust ãã€ããª
ç°¡åãªã¢ããªããå§ããŸããããAOSP ãã§ãã¯ã¢ãŠãã®ã«ãŒãã§ã次ã®ãã¡ã€ã«ãäœæããŸãã
hello_rust/Android.bp:
rust_binary {
name: "hello_rust",
crate_name: "hello_rust",
srcs: ["src/main.rs"],
}
hello_rust/src/main.rs:
//! Rust ã®ãã¢ã /// æšæ¶ãæšæºåºåã«åºåããŸãã fn main() { println!("Hello from Rust!"); }
ããã§ããã€ããªããã«ããpushãå®è¡ã§ããŸãã
m hello_rust
adb push "$ANDROID_PRODUCT_OUT/system/bin/hello_rust" /data/local/tmp
adb shell /data/local/tmp/hello_rust
Hello from Rust!
Rust ã©ã€ãã©ãª
rust_library
ã䜿çšããŠãAndroid çšã®æ°ãã Rust ã©ã€ãã©ãªãäœæããŸãã
ããã§ã¯ã2 ã€ã®ã©ã€ãã©ãªãžã®äŸåé¢ä¿ã宣èšããŸãã
libgreeting
: 以äžã§å®çŸ©ããŸããlibtextwrap
: ãã§ã«external/rust/crates/
ã«åã蟌ãŸããŠããã¯ã¬ãŒãã§ãã
hello_rust/Android.bp:
rust_binary {
name: "hello_rust_with_dep",
crate_name: "hello_rust_with_dep",
srcs: ["src/main.rs"],
rustlibs: [
"libgreetings",
"libtextwrap",
],
prefer_rlib: true, // ãã€ããã㯠ãªã³ã¯ ãšã©ãŒãåé¿ããããã«å¿
èŠã§ãã
}
rust_library {
name: "libgreetings",
crate_name: "greetings",
srcs: ["src/lib.rs"],
}
hello_rust/src/main.rs:
//! Rust ã®ãã¢ã
use greetings::greeting;
use textwrap::fill;
/// æšæ¶ãæšæºåºåã«åºåããŸãã
fn main() {
println!("{}", fill(&greeting("Bob"), 24));
}
hello_rust/src/lib.rs:
//! æšæ¶ã©ã€ãã©ãªã
/// `name` ã«æšæ¶ããŸãã
pub fn greeting(name: &str) -> String {
format!("Hello {name}, it is very nice to meet you!")
}
åãšåãããã«ãã€ããªããã«ããpushãå®è¡ããŸãã
m hello_rust_with_dep
adb push "$ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep" /data/local/tmp
adb shell /data/local/tmp/hello_rust_with_dep
Hello Bob, it is very
nice to meet you!
AIDLïŒAndroidã€ã³ã¿ãŒãã§ã€ã¹å®çŸ©èšèªïŒ
Rust ã§ã¯ Android ã€ã³ã¿ãŒãã§ãŒã¹å®çŸ©èšèªïŒAIDLïŒ ããµããŒããããŠããŸãã
- Rust ã³ãŒãã¯æ¢åã® AIDL ãµãŒããŒãåŒã³åºãããšãã§ããŸãã
- Rust ã§ã¯æ°ãã AIDL ãµãŒããŒãäœæã§ããŸãã
èªçæ¥ãµãŒãã¹ã®ãã¥ãŒããªã¢ã«
To illustrate how to use Rust with Binder, we're going to walk through the process of creating a Binder interface. We're then going to both implement the described service and write client code that talks to that service.
AIDL ã€ã³ã¿ãŒãã§ãŒã¹
ãµãŒãã¹ã® API ã宣èšããã«ã¯ãAIDL ã€ã³ã¿ãŒãã§ãŒã¹ã䜿çšããŸãã
birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:
package com.example.birthdayservice;
/** èªçæ¥ãµãŒãã¹ã®ã€ã³ã¿ãŒãã§ãŒã¹ã*/
interface IBirthdayService {
/** ããèªçæ¥ããã§ãšãããšããã¡ãã»ãŒãžãçæããŸãã*/
String wishHappyBirthday(String name, int years);
}
birthday_service/aidl/Android.bp:
aidl_interface {
name: "com.example.birthdayservice",
srcs: ["com/example/birthdayservice/*.aidl"],
unstable: true,
backend: {
rust: { // Rust ã¯ããã©ã«ãã§ã¯ç¡å¹ã§ãã
enabled: true,
},
},
}
- Note that the directory structure under the
aidl/
directory needs to match the package name used in the AIDL file, i.e. the package iscom.example.birthdayservice
and the file is ataidl/com/example/IBirthdayService.aidl
.
Generated Service API
Binder generates a trait corresponding to the interface definition. trait to talk to the service.
birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:
/** èªçæ¥ãµãŒãã¹ã®ã€ã³ã¿ãŒãã§ãŒã¹ã*/
interface IBirthdayService {
/** ããèªçæ¥ããã§ãšãããšããã¡ãã»ãŒãžãçæããŸãã*/
String wishHappyBirthday(String name, int years);
}
Generated trait:
trait IBirthdayService {
fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::Result<String>;
}
Your service will need to implement this trait, and your client will use this trait to talk to the service.
- The generated bindings can be found at
out/soong/.intermediates/<path to module>/
. - Point out how the generated function signature, specifically the argument and return types, correspond the interface definition.
String
for an argument results in a different Rust type thanString
as a return type.
ãµãŒãã¹ã®å®è£
次ã«ãAIDL ãµãŒãã¹ãå®è£ ããŸãã
birthday_service/src/lib.rs:
use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService;
use com_example_birthdayservice::binder;
/// `IBirthdayService` ã®å®è£
ã
pub struct BirthdayService;
impl binder::Interface for BirthdayService {}
impl IBirthdayService for BirthdayService {
fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::Result<String> {
Ok(format!("Happy Birthday {name}, congratulations with the {years} years!"))
}
}
birthday_service/Android.bp:
rust_library {
name: "libbirthdayservice",
srcs: ["src/lib.rs"],
crate_name: "birthdayservice",
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
],
}
- Point out the path to the generated
IBirthdayService
trait, and explain why each of the segments is necessary. - TODO: What does the
binder::Interface
trait do? Are there methods to override? Where source?
AIDL ãµãŒããŒ
次ã«ããµãŒãã¹ãå ¬éãããµãŒããŒãäœæããŸãã
birthday_service/src/server.rs:
//! èªçæ¥ãµãŒãã¹ã
use birthdayservice::BirthdayService;
use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::BnBirthdayService;
use com_example_birthdayservice::binder;
const SERVICE_IDENTIFIER: &str = "birthdayservice";
/// èªçæ¥ãµãŒãã¹ã®ãšã³ã㪠ãã€ã³ãã
fn main() {
let birthday_service = BirthdayService;
let birthday_service_binder = BnBirthdayService::new_binder(
birthday_service,
binder::BinderFeatures::default(),
);
binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder.as_binder())
.expect("Failed to register service");
binder::ProcessState::join_thread_pool();
}
birthday_service/Android.bp:
rust_binary {
name: "birthday_server",
crate_name: "birthday_server",
srcs: ["src/server.rs"],
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
"libbirthdayservice",
],
prefer_rlib: true, // ãã€ããã㯠ãªã³ã¯ ãšã©ãŒãåé¿ããããã§ãã
}
The process for taking a user-defined service implementation (in this case the BirthdayService
type, which implements the IBirthdayService
) and starting it as a Binder service has multiple steps, and may appear more complicated than students are used to if they've used Binder from C++ or another language. Explain to students why each step is necessary.
- Create an instance of your service type (
BirthdayService
). - Wrap the service object in corresponding
Bn*
type (BnBirthdayService
in this case). This type is generated by Binder and provides the common Binder functionality that would be provided by theBnBinder
base class in C++. We don't have inheritance in Rust, so instead we use composition, putting ourBirthdayService
within the generatedBnBinderService
. - Call
add_service
, giving it a service identifier and your service object (theBnBirthdayService
object in the example). - Call
join_thread_pool
to add the current thread to Binder's thread pool and start listening for connections.
ãããã€
次ã«ããµãŒãã¹ããã«ããpushãéå§ããŸãã
m birthday_server
adb push "$ANDROID_PRODUCT_OUT/system/bin/birthday_server" /data/local/tmp
adb root
adb shell /data/local/tmp/birthday_server
å¥ã®ã¿ãŒããã«ã§ããµãŒãã¹ãå®è¡ãããŠããããšã確èªããŸãã
adb shell service check birthdayservice
Service birthdayservice: found
service call
ã䜿çšããŠãµãŒãã¹ãåŒã³åºãããšãã§ããŸãã
adb shell service call birthdayservice 1 s16 Bob i32 24
Result: Parcel(
0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.'
0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.'
0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'
0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'
0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'
0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'
0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'
0x00000070: 00210073 00000000 's.!..... ')
AIDL ã¯ã©ã€ã¢ã³ã
ããããããã§ãæ°ãããµãŒãã¹çšã® Rust ã¯ã©ã€ã¢ã³ããäœæããŸãã
birthday_service/src/client.rs:
use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService;
use com_example_birthdayservice::binder;
const SERVICE_IDENTIFIER: &str = "birthdayservice";
/// èªçæ¥ãµãŒãã¹ãåŒã³åºããŸãã
fn main() -> Result<(), Box<dyn Error>> {
let name = std::env::args().nth(1).unwrap_or_else(|| String::from("Bob"));
let years = std::env::args()
.nth(2)
.and_then(|arg| arg.parse::<i32>().ok())
.unwrap_or(42);
binder::ProcessState::start_thread_pool();
let service = binder::get_interface::<dyn IBirthdayService>(SERVICE_IDENTIFIER)
.map_err(|_| "Failed to connect to BirthdayService")?;
// Call the service.
let msg = service.wishHappyBirthday(&name, years)?;
println!("{msg}");
}
birthday_service/Android.bp:
rust_binary {
name: "birthday_client",
crate_name: "birthday_client",
srcs: ["src/client.rs"],
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
],
prefer_rlib: true, // ãã€ããã㯠ãªã³ã¯ ãšã©ãŒãåé¿ããããã§ãã
}
ã¯ã©ã€ã¢ã³ãã libbirthdayservice
ã«äŸåããŠããªãããšã«æ³šç®ããŠãã ããã
ããã€ã¹ã§ã¯ã©ã€ã¢ã³ãããã«ããpushãå®è¡ããŸãã
m birthday_client
adb push "$ANDROID_PRODUCT_OUT/system/bin/birthday_client" /data/local/tmp
adb shell /data/local/tmp/birthday_client Charlie 60
Happy Birthday Charlie, congratulations with the 60 years!
Strong<dyn IBirthdayService>
is the trait object representing the service that the client has connected to.Strong
is a custom smart pointer type for Binder. It handles both an in-process ref count for the service trait object, and the global Binder ref count that tracks how many processes have a reference to the object.- Note that the trait object that the client uses to talk to the service uses the exact same trait that the server implements. For a given Binder interface, there is a single Rust trait generated that both client and server use.
- Use the same service identifier used when registering the service. This should ideally be defined in a common crate that both the client and server can depend on.
APIã®å€æŽ
APIãæ¡åŒµããŠãã¯ã©ã€ã¢ã³ããèªçæ¥ã«ãŒãã«è¿œå ããè€æ°è¡ã®ã¡ãã»ãŒãžãæå®ã§ããããã«ããŸãã
package com.example.birthdayservice;
/** èªçæ¥ãµãŒãã¹ã®ã€ã³ã¿ãŒãã§ãŒã¹ã*/
interface IBirthdayService {
/** ããèªçæ¥ããã§ãšãããšããã¡ãã»ãŒãžãçæããŸãã*/
String wishHappyBirthday(String name, int years, in String[] text);
}
This results in an updated trait definition for IBirthdayService
:
trait IBirthdayService {
fn wishHappyBirthday(
&self,
name: &str,
years: i32,
text: &[String],
) -> binder::Result<String>;
}
- Note how the
String[]
in the AIDL definition is translated as a&[String]
in Rust, i.e. that idiomatic Rust types are used in the generated bindings wherever possible:in
array arguments are translated to slices.out
andinout
args are translated to&mut Vec<T>
.- Return values are translated to returning a
Vec<T>
.
Updating Client and Service
Update the client and server code to account for the new API.
birthday_service/src/lib.rs:
impl IBirthdayService for BirthdayService {
fn wishHappyBirthday(
&self,
name: &str,
years: i32,
text: &[String],
) -> binder::Result<String> {
let mut msg = format!(
"Happy Birthday {name}, congratulations with the {years} years!",
);
for line in text {
msg.push('\n');
msg.push_str(line);
}
Ok(msg)
}
}
birthday_service/src/client.rs:
let msg = service.wishHappyBirthday(
&name,
years,
&[
String::from("Habby birfday to yuuuuu"),
String::from("And also: many more"),
],
)?;
- TODO: Move code snippets into project files where they'll actually be built?
Working With AIDL Types
AIDL types translate into the appropriate idiomatic Rust type:
- Primitive types map (mostly) to idiomatic Rust types.
- Collection types like slices,
Vec
s and string types are supported. - References to AIDL objects and file handles can be sent between clients and services.
- File handles and parcelables are fully supported.
Primitive Types
Primitive types map (mostly) idiomatically:
AIDL Type | Rust å | Note |
---|---|---|
boolean | bool | |
byte | i8 | Note that bytes are signed. |
char | u16 | Note the usage of u16 , NOT u32 . |
int | i32 | |
long | i64 | |
float | f32 | |
double | f64 | |
String | String |
é åå
The array types (T[]
, byte[]
, and List<T>
) get translated to the appropriate Rust array type depending on how they are used in the function signature:
Position | Rust å |
---|---|
in argument | &[T] |
out /inout argument | &mut Vec<T> |
Return | Vec<T> |
- In Android 13 or higher, fixed-size arrays are supported, i.e.
T[N]
becomes[T; N]
. Fixed-size arrays can have multiple dimensions (e.g.int[3][4]
). In the Java backend, fixed-size arrays are represented as array types. - Arrays in parcelable fields always get translated to
Vec<T>
.
ãªããžã§ã¯ãã®éä¿¡
AIDL objects can be sent either as a concrete AIDL type or as the type-erased IBinder
interface:
birthday_service/aidl/com/example/birthdayservice/IBirthdayInfoProvider.aidl:
package com.example.birthdayservice;
interface IBirthdayInfoProvider {
String name();
int years();
}
birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:
import com.example.birthdayservice.IBirthdayInfoProvider;
interface IBirthdayService {
/** The same thing, but using a binder object. */
String wishWithProvider(IBirthdayInfoProvider provider);
/** The same thing, but using `IBinder`. */
String wishWithErasedProvider(IBinder provider);
}
birthday_service/src/client.rs:
/// Rust struct implementing the `IBirthdayInfoProvider` interface.
struct InfoProvider {
name: String,
age: u8,
}
impl binder::Interface for InfoProvider {}
impl IBirthdayInfoProvider for InfoProvider {
fn name(&self) -> binder::Result<String> {
Ok(self.name.clone())
}
fn years(&self) -> binder::Result<i32> {
Ok(self.age as i32)
}
}
fn main() {
binder::ProcessState::start_thread_pool();
let service = connect().expect("Failed to connect to BirthdayService");
// Create a binder object for the `IBirthdayInfoProvider` interface.
let provider = BnBirthdayInfoProvider::new_binder(
InfoProvider { name: name.clone(), age: years as u8 },
BinderFeatures::default(),
);
// Send the binder object to the service.
service.wishWithProvider(&provider)?;
// Perform the same operation but passing the provider as an `SpIBinder`.
service.wishWithErasedProvider(&provider.as_binder())?;
}
- Note the usage of
BnBirthdayInfoProvider
. This serves the same purpose asBnBirthdayService
that we saw previously.
Parcelables
Binder for Rust supports sending parcelables directly:
birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl:
package com.example.birthdayservice;
parcelable BirthdayInfo {
String name;
int years;
}
birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:
import com.example.birthdayservice.BirthdayInfo;
interface IBirthdayService {
/** The same thing, but with a parcelable. */
String wishWithInfo(in BirthdayInfo info);
}
birthday_service/src/client.rs:
fn main() {
binder::ProcessState::start_thread_pool();
let service = connect().expect("Failed to connect to BirthdayService");
let info = BirthdayInfo { name: "Alice".into(), years: 123 };
service.wishWithInfo(&info)?;
}
Sending Files
Files can be sent between Binder clients/servers using the ParcelFileDescriptor
type:
birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:
interface IBirthdayService {
/** The same thing, but loads info from a file. */
String wishFromFile(in ParcelFileDescriptor infoFile);
}
birthday_service/src/client.rs:
fn main() {
binder::ProcessState::start_thread_pool();
let service = connect().expect("Failed to connect to BirthdayService");
// Open a file and put the birthday info in it.
let mut file = File::create("/data/local/tmp/birthday.info").unwrap();
writeln!(file, "{name}")?;
writeln!(file, "{years}")?;
// Create a `ParcelFileDescriptor` from the file and send it.
let file = ParcelFileDescriptor::new(file);
service.wishFromFile(&file)?;
}
birthday_service/src/lib.rs:
impl IBirthdayService for BirthdayService {
fn wishFromFile(
&self,
info_file: &ParcelFileDescriptor,
) -> binder::Result<String> {
// Convert the file descriptor to a `File`. `ParcelFileDescriptor` wraps
// an `OwnedFd`, which can be cloned and then used to create a `File`
// object.
let mut info_file = info_file
.as_ref()
.try_clone()
.map(File::from)
.expect("Invalid file handle");
let mut contents = String::new();
info_file.read_to_string(&mut contents).unwrap();
let mut lines = contents.lines();
let name = lines.next().unwrap();
let years: i32 = lines.next().unwrap().parse().unwrap();
Ok(format!("Happy Birthday {name}, congratulations with the {years} years!"))
}
}
ParcelFileDescriptor
wraps anOwnedFd
, and so can be created from aFile
(or any other type that wraps anOwnedFd
), and can be used to create a newFile
handle on the other side.- Other types of file descriptors can be wrapped and sent, e.g. TCP, UDP, and UNIX sockets.
Testing in Android
Building on Testing, we will now look at how unit tests work in AOSP. Use the rust_test
module for your unit tests:
testing/Android.bp:
rust_library {
name: "libleftpad",
crate_name: "leftpad",
srcs: ["src/lib.rs"],
}
rust_test {
name: "libleftpad_test",
crate_name: "leftpad_test",
srcs: ["src/lib.rs"],
host_supported: true,
test_suites: ["general-tests"],
}
testing/src/lib.rs:
#![allow(unused)] fn main() { //! Left-padding library. /// Left-pad `s` to `width`. pub fn leftpad(s: &str, width: usize) -> String { format!("{s:>width$}") } #[cfg(test)] mod tests { use super::*; #[test] fn short_string() { assert_eq!(leftpad("foo", 5), " foo"); } #[test] fn long_string() { assert_eq!(leftpad("foobar", 6), "foobar"); } } }
You can now run the test with
atest --host libleftpad_test
The output looks like this:
INFO: Elapsed time: 2.666s, Critical Path: 2.40s
INFO: 3 processes: 2 internal, 1 linux-sandbox.
INFO: Build completed successfully, 3 total actions
//comprehensive-rust-android/testing:libleftpad_test_host PASSED in 2.3s
PASSED libleftpad_test.tests::long_string (0.0s)
PASSED libleftpad_test.tests::short_string (0.0s)
Test cases: finished with 2 passing and 0 failing out of 2 test cases
Notice how you only mention the root of the library crate. Tests are found recursively in nested modules.
GoogleTest
GoogleTest ã¯ã¬ãŒãã«ããããããã£ãŒã䜿çšããæè»ãªãã¹ã ã¢ãµãŒã·ã§ã³ãå¯èœã«ãªããŸãã
use googletest::prelude::*;
#[googletest::test]
fn test_elements_are() {
let value = vec!["foo", "bar", "baz"];
expect_that!(value, elements_are!(eq(&"foo"), lt(&"xyz"), starts_with("b")));
}
æåŸã®èŠçŽ ã "!"
ã«å€æŽãããšããã¹ãã¯å€±æãããšã©ãŒç®æã瀺ãæ§é åããããšã©ãŒ ã¡ãã»ãŒãžã衚瀺ãããŸãã
---- test_elements_are stdout ----
Value of: value
Expected: has elements:
0. is equal to "foo"
1. is less than "xyz"
2. starts with prefix "!"
Actual: ["foo", "bar", "baz"],
where element #2 is "baz", which does not start with "!"
at src/testing/googletest.rs:6:5
Error: See failure output above
-
GoogleTest 㯠Rust ãã¬ã€ã°ã©ãŠã³ãã®äžéšã§ã¯ãªãããããã®äŸã¯ããŒã«ã«ç°å¢ã§å®è¡ããå¿ èŠããããŸãã
cargo add googletest
ã䜿çšããŠãæ¢åã® Cargo ãããžã§ã¯ãã«ãã°ããè¿œå ããŸãã -
use googletest::prelude::*;
è¡ã¯ãäžè¬çã«äœ¿çšããããã¯ããšåãã€ã³ããŒãããŸãã -
This just scratches the surface, there are many builtin matchers. Consider going through the first chapter of "Advanced testing for Rust applications", a self-guided Rust course: it provides a guided introduction to the library, with exercises to help you get comfortable with
googletest
macros, its matchers and its overall philosophy. -
A particularly nice feature is that mismatches in multi-line strings are shown as a diff:
#[test]
fn test_multiline_string_diff() {
let haiku = "Memory safety found,\n\
Rust's strong typing guides the way,\n\
Secure code you'll write.";
assert_that!(
haiku,
eq("Memory safety found,\n\
Rust's silly humor guides the way,\n\
Secure code you'll write.")
);
}
ããã«ãããå·®åãè²åããããŸãïŒããã§ã¯è²åããããŠããŸããïŒã
Value of: haiku
Expected: is equal to "Memory safety found,\nRust's silly humor guides the way,\nSecure code you'll write."
Actual: "Memory safety found,\nRust's strong typing guides the way,\nSecure code you'll write.",
which isn't equal to "Memory safety found,\nRust's silly humor guides the way,\nSecure code you'll write."
Difference(-actual / +expected):
Memory safety found,
-Rust's strong typing guides the way,
+Rust's silly humor guides the way,
Secure code you'll write.
at src/testing/googletest.rs:17:5
- ãã®ã¯ã¬ãŒã㯠GoogleTest for C++ ãRustã«ç§»æ€ãããã®ã§ãã
ã¢ãã¯
ã¢ãã¯ã«ã¯ãMockall ãšããã©ã€ãã©ãªãåºã䜿çšãããŠããŸãããã¬ã€ãã䜿çšããããã«ã³ãŒãããªãã¡ã¯ã¿ãªã³ã°ããå¿ èŠããããŸããããã«ãããããã«ã¢ãã¯ã§ããããã«ãªããŸãã
use std::time::Duration;
#[mockall::automock]
pub trait Pet {
fn is_hungry(&self, since_last_meal: Duration) -> bool;
}
#[test]
fn test_robot_dog() {
let mut mock_dog = MockPet::new();
mock_dog.expect_is_hungry().return_const(true);
assert_eq!(mock_dog.is_hungry(Duration::from_secs(10)), true);
}
-
Mockall is the recommended mocking library in Android (AOSP). There are other mocking libraries available on crates.io, in particular in the area of mocking HTTP services. The other mocking libraries work in a similar fashion as Mockall, meaning that they make it easy to get a mock implementation of a given trait.
-
ã¢ãã¯ã䜿çšããéã¯å°ã泚æãå¿ èŠã§ããã¢ãã¯ã䜿çšãããšããã¹ããäŸåé¢ä¿ããå®å šã«åé¢ã§ããŸãããã®çµæãããé«éã§å®å®ãããã¹ãå®è¡ãå¯èœã«ãªããŸããäžæ¹ãã¢ãã¯ã誀ã£ãŠæ§æãããå®éã®äŸåé¢ä¿ã®åäœãšã¯ç°ãªãåºåãè¿ãããå¯èœæ§ããããŸãã
å¯èœãªéããå®éã®äŸåé¢ä¿ã䜿çšããããšãããããããŸããããšãã°ãå€ãã®ããŒã¿ããŒã¹ã§ã¯ã€ã³ã¡ã¢ãª ããã¯ãšã³ããæ§æã§ããŸããã€ãŸãããã¹ãã§æ£ããåäœãåŸããããããé«éã§ããã¹ãåŸã¯èªåçã«ã¯ãªãŒã³ã¢ãããããŸãã
åæ§ã«ãå€ãã®ãŠã§ã ãã¬ãŒã ã¯ãŒã¯ã§ã¯ã
localhost
äžã®ã©ã³ãã ãªããŒãã«ãã€ã³ãããããã»ã¹å ãµãŒããŒãèµ·åã§ããŸãããã®ãããªæ§æã¯å®éã®ç°å¢ã§ã³ãŒãããã¹ãããããšãå¯èœã«ããã®ã§ããã¬ãŒã ã¯ãŒã¯ãã¢ãã¯ããããšãããåžžã«åªå ããŠå©çšããŸãããã -
Mockall 㯠Rust ãã¬ã€ã°ã©ãŠã³ãã®äžéšã§ã¯ãªãããããã®äŸã¯ããŒã«ã«ç°å¢ã§å®è¡ããå¿ èŠããããŸãã
cargo add mockall
ã䜿çšããŠãMockall ãæ¢åã® Cargo ãããžã§ã¯ãã«ãã°ããè¿œå ããŸãã -
Mockall ã«ã¯ããã«å€ãã®æ©èœããããŸããç¹ã«ãæž¡ãããåŒæ°ã«å¿ããŠæåŸ å€ãèšå®ã§ããŸããããã§ã¯ãæåŸã«é€ãäžããŠãããŠãã 3 æéåŸã«ç©ºè ¹ã«ãªãç«ãã¢ãã¯ããããã«ããã䜿çšããŸãã
#[test]
fn test_robot_cat() {
let mut mock_cat = MockPet::new();
mock_cat
.expect_is_hungry()
.with(mockall::predicate::gt(Duration::from_secs(3 * 3600)))
.return_const(true);
mock_cat.expect_is_hungry().return_const(false);
assert_eq!(mock_cat.is_hungry(Duration::from_secs(1 * 3600)), false);
assert_eq!(mock_cat.is_hungry(Duration::from_secs(5 * 3600)), true);
}
.times(n)
ã䜿çšãããšãã¢ãã¯ã¡ãœãããåŒã³åºãããåæ°ãn
ã«å¶éã§ããŸãããããæºããããªãå Žåãã¢ãã¯ã¯ããããæã«èªåçã«ãããã¯ã«ãªããŸãã
ãã°åºå
log
ã¯ã¬ãŒãã䜿çšããŠãèªåçã« ïŒããã€ã¹äžã§ã¯ïŒlogcat
ãŸã㯠ïŒãã¹ãäžã§ã¯ïŒstdout
ã«ãã°ãèšé²ããããã«ããŸãã
hello_rust_logs/Android.bp:
rust_binary {
name: "hello_rust_logs",
crate_name: "hello_rust_logs",
srcs: ["src/main.rs"],
rustlibs: [
"liblog_rust",
"liblogger",
],
host_supported: true,
}
hello_rust_logs/src/main.rs:
//! Rust ãã®ã³ã°ã®ãã¢ã
use log::{debug, error, info};
/// æšæ¶ããã°ã«èšé²ããŸãã
fn main() {
logger::init(
logger::Config::default()
.with_tag_on_device("rust")
.with_max_level(log::LevelFilter::Trace),
);
debug!("Starting program.");
info!("Things are going fine.");
error!("Something went wrong!");
}
ããã€ã¹ã§ãã€ããªããã«ããpushãå®è¡ããŸãã
m hello_rust_logs
adb push "$ANDROID_PRODUCT_OUT/system/bin/hello_rust_logs" /data/local/tmp
adb shell /data/local/tmp/hello_rust_logs
adb logcat
ã§ãã°ã衚瀺ã§ããŸãã
adb logcat -s rust
09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting program.
09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going fine.
09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went wrong!
- The logger implementation in
liblogger
is only needed in the final binary, if you're logging from a library you only need thelog
facade crate.
çžäºéçšæ§
Rust ã¯ä»ã®èšèªãšã®çžäºéçšæ§ã«åªããŠããããã次ã®ããšãå¯èœã§ãã
- ä»ã®èšèªãã Rust é¢æ°ãåŒã³åºãã
- Rust ããä»ã®èšèªã§èšè¿°ãããé¢æ°ãåŒã³åºãã
ä»ã®èšèªã®é¢æ°ãåŒã³åºãå Žåã¯ãå€éšé¢æ°ã€ã³ã¿ãŒãã§ãŒã¹ïŒFFI: foreign function interfaceïŒã䜿çšããŸãã
C ãšã®çžäºéçšæ§
Rust ã¯ãC ã®åŒã³åºãèŠåã«ãããªããžã§ã¯ã ãã¡ã€ã«ã®ãªã³ã¯ãå®å šã«ãµããŒãããŠããŸããåæ§ã«ãRust é¢æ°ããšã¯ã¹ããŒãã㊠C ããåŒã³åºãããšãã§ããŸãã
ããã¯æåã§è¡ãããšãã§ããŸãã
unsafe extern "C" { safe fn abs(x: i32) -> i32; } fn main() { let x = -42; let abs_x = abs(x); println!("{x}, {abs_x}"); }
We already saw this in the Safe FFI Wrapper exercise.
ããã¯ãã¿ãŒã²ãã ãã©ãããã©ãŒã ãå®å šã«ç解ããŠããããšãåæãšããŠããŸããæ¬çªç°å¢ã§ã®äœ¿çšæšå¥šãããŸããã
次ã«ãããè¯ãéžæè¢ãèŠãŠãããŸãã
Bindgen ã®äœ¿çš
bindgen ããŒã«ã䜿çšãããšãC ããã㌠ãã¡ã€ã«ãããã€ã³ãã£ã³ã°ãèªåçæã§ããŸãã
ãŸããå°ã㪠C ã©ã€ãã©ãªãäœæããŸãã
interoperability/bindgen/libbirthday.h:
typedef struct card {
const char* name;
int years;
} card;
void print_card(const card* card);
interoperability/bindgen/libbirthday.c:
#include <stdio.h>
#include "libbirthday.h"
void print_card(const card* card) {
printf("+--------------\n");
printf("| Happy Birthday %s!\n", card->name);
printf("| Congratulations with the %i years!\n", card->years);
printf("+--------------\n");
}
ããã Android.bp
ãã¡ã€ã«ã«è¿œå ããŸãã
interoperability/bindgen/Android.bp:
cc_library {
name: "libbirthday",
srcs: ["libbirthday.c"],
}
ã©ã€ãã©ãªã®ã©ãã㌠ããã㌠ãã¡ã€ã«ãäœæããŸãïŒãã®äŸã§ã¯å¿ é ã§ã¯ãããŸããïŒã
interoperability/bindgen/libbirthday_wrapper.h:
#include "libbirthday.h"
ããã§ããã€ã³ãã£ã³ã°ãèªåçæã§ããŸãã
interoperability/bindgen/Android.bp:
rust_bindgen {
name: "libbirthday_bindgen",
crate_name: "birthday_bindgen",
wrapper_src: "libbirthday_wrapper.h",
source_stem: "bindings",
static_libs: ["libbirthday"],
}
ããã§ãRust ããã°ã©ã ã§ãã€ã³ãã£ã³ã°ã䜿çšã§ããŸãã
interoperability/bindgen/Android.bp:
rust_binary {
name: "print_birthday_card",
srcs: ["main.rs"],
rustlibs: ["libbirthday_bindgen"],
}
interoperability/bindgen/main.rs:
//! Bindgen ã®ãã¢ã use birthday_bindgen::{card, print_card}; fn main() { let name = std::ffi::CString::new("Peter").unwrap(); let card = card { name: name.as_ptr(), years: 42 }; // SAFETY: The pointer we pass is valid because it came from a Rust // reference, and the `name` it contains refers to `name` above which also // remains valid. `print_card` doesn't store either pointer to use later // after it returns. unsafe { print_card(&card as *const card); } }
ããã€ã¹ã§ãã€ããªããã«ããpushãå®è¡ããŸãã
m print_birthday_card
adb push "$ANDROID_PRODUCT_OUT/system/bin/print_birthday_card" /data/local/tmp
adb shell /data/local/tmp/print_birthday_card
ããã§ãèªåçæããããã¹ããå®è¡ããŠããã€ã³ãã£ã³ã°ãæ©èœããŠããããšã確èªã§ããŸãã
interoperability/bindgen/Android.bp:
rust_test {
name: "libbirthday_bindgen_test",
srcs: [":libbirthday_bindgen"],
crate_name: "libbirthday_bindgen_test",
test_suites: ["general-tests"],
auto_gen_config: true,
clippy_lints: "none", // çæããããã¡ã€ã«ãlint ãã§ãã¯ãã¹ããã
lints: "none",
}
atest libbirthday_bindgen_test
Rust ã®åŒã³åºã
Rust ã®é¢æ°ãšåã¯ãC ã«ç°¡åã«ãšã¯ã¹ããŒãã§ããŸãã
interoperability/rust/libanalyze/analyze.rs
//! Rust FFI ã®ãã¢ã #![deny(improper_ctypes_definitions)] use std::os::raw::c_int; /// Analyze the numbers. // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] pub extern "C" fn analyze_numbers(x: c_int, y: c_int) { if x < y { println!("x ({x}) is smallest!"); } else { println!("y ({y}) is probably larger than x ({x})"); } }
interoperability/rust/libanalyze/analyze.h
#ifndef ANALYSE_H
#define ANALYSE_H
void analyze_numbers(int x, int y);
#endif
interoperability/rust/libanalyze/Android.bp
rust_ffi {
name: "libanalyze_ffi",
crate_name: "analyze_ffi",
srcs: ["analyze.rs"],
include_dirs: ["."],
}
ããã§ãããã C ãã€ããªããåŒã³åºããããã«ãªããŸããã
interoperability/rust/analyze/main.c
#include "analyze.h"
int main() {
analyze_numbers(10, 20);
analyze_numbers(123, 123);
return 0;
}
interoperability/rust/analyze/Android.bp
cc_binary {
name: "analyze_numbers",
srcs: ["main.c"],
static_libs: ["libanalyze_ffi"],
}
ããã€ã¹ã§ãã€ããªããã«ããpushãå®è¡ããŸãã
m analyze_numbers
adb push "$ANDROID_PRODUCT_OUT/system/bin/analyze_numbers" /data/local/tmp
adb shell /data/local/tmp/analyze_numbers
#[unsafe(no_mangle)]
disables Rust's usual name mangling, so the exported symbol will just be the name of the function. You can also use #[unsafe(export_name = "some_name")]
to specify whatever name you want.
C++
CXX ã¯ã¬ãŒãã䜿çšãããšãRust ãš C++ ã®éã§å®å šãªçžäºéçšæ§ã確ä¿ã§ããŸãã
å šäœçãªã¢ãããŒãã¯æ¬¡ã®ããã«ãªããŸãã
ããªããžã¢ãžã¥ãŒã«
CXX ã¯ãåèšèªããä»ã®èšèªã«å
¬éãããé¢æ°ã·ã°ããã£ã®èšè¿°ã«äŸåããŸãããã®èšè¿°ã¯ã#[cxx::bridge]
å±æ§ãã¯ãã§ã¢ãããŒã·ã§ã³ããã Rust ã¢ãžã¥ãŒã«å
ã® extern ãããã¯ã䜿çšããŠæå®ããŸãã
#[allow(unsafe_op_in_unsafe_fn)]
#[cxx::bridge(namespace = "org::blobstore")]
mod ffi {
// äž¡æ¹ã®èšèªããã¢ã¯ã»ã¹ã§ãããã£ãŒã«ããæã€å
±ææ§é äœã
struct BlobMetadata {
size: usize,
tags: Vec<String>,
}
// C++ ã«å
¬éããã Rust ã®åãšã·ã°ããã£ã
extern "Rust" {
type MultiBuf;
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
}
// Rust ã«å
¬éããã C++ ã®åãšã·ã°ããã£ã
unsafe extern "C++" {
include!("include/blobstore.h");
type BlobstoreClient;
fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
fn put(self: Pin<&mut BlobstoreClient>, parts: &mut MultiBuf) -> u64;
fn tag(self: Pin<&mut BlobstoreClient>, blobid: u64, tag: &str);
fn metadata(&self, blobid: u64) -> BlobMetadata;
}
}
- ããªããžã¯éåžžãã¯ã¬ãŒãå
ã®
ffi
ã¢ãžã¥ãŒã«ã§å®£èšããŸãã - ããªããž ã¢ãžã¥ãŒã«ã§è¡ããã宣èšãããCXX ã¯ããããã Rust ãš C++ ã®å / é¢æ°ã®å®çŸ©ãçæãããããã®ã¢ã€ãã ãäž¡æ¹ã®èšèªã«å ¬éããŸãã
- çæããã Rust ã³ãŒãã衚瀺ããã«ã¯ãcargo-expand ã䜿çšããŠãå±éããã proc ãã¯ãã衚瀺ããŸããã»ãšãã©ã®äŸã§ã¯ã
cargo expand ::ffi
ã䜿çšããŠffi
ã¢ãžã¥ãŒã«ã®ã¿ãå±éããŸãïŒãã ãããã㯠Android ãããžã§ã¯ãã«ã¯åœãŠã¯ãŸããŸããïŒã - çæããã C++ ã³ãŒãã衚瀺ããã«ã¯ã
target/cxxbridge
ã確èªããŸãã
Rust ã®ããªããžå®£èš
#[cxx::bridge]
mod ffi {
extern "Rust" {
type MyType; // ãªããŒã¯å
fn foo(&self); // `MyType` ã®ã¡ãœãã
fn bar() -> Box<MyType>; // Free function
}
}
struct MyType(i32);
impl MyType {
fn foo(&self) {
println!("{}", self.0);
}
}
fn bar() -> Box<MyType> {
Box::new(MyType(123))
}
- 芪ã¢ãžã¥ãŒã«ã®ã¹ã³ãŒãå
ã«ãã
extern "Rust"
åç §ã¢ã€ãã ã§å®£èšãããã¢ã€ãã ã - CXX ã³ãŒã ãžã§ãã¬ãŒã¿ã¯ã
extern "Rust"
ã»ã¯ã·ã§ã³ã䜿çšããŠã察å¿ãã C++ 宣èšãå«ã C++ ããã㌠ãã¡ã€ã«ãçæããŸããçæãããããããŒã®ãã¹ã¯ãrs.hãšãããã¡ã€ã«æ¡åŒµåéšåãé€ããããªããžãå«ã Rust ãœãŒã¹ãã¡ã€ã«ãšåãã«ãªããŸãã
çæããã C++
#[cxx::bridge]
mod ffi {
// C++ ã«å
¬éããã Rust ã®åãšã·ã°ããã£ã
extern "Rust" {
type MultiBuf;
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
}
}
ãããã次ã®ãã㪠C++ ãçæãããŸãã
struct MultiBuf final : public ::rust::Opaque {
~MultiBuf() = delete;
private:
friend ::rust::layout;
struct layout {
static ::std::size_t size() noexcept;
static ::std::size_t align() noexcept;
};
};
::rust::Slice<::std::uint8_t const> next_chunk(::org::blobstore::MultiBuf &buf) noexcept;
C++ ã®ããªããžå®£èš
#[cxx::bridge]
mod ffi {
// Rust ã«å
¬éããã C++ ã®åãšã·ã°ããã£ã
unsafe extern "C++" {
include!("include/blobstore.h");
type BlobstoreClient;
fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
fn put(self: Pin<&mut BlobstoreClient>, parts: &mut MultiBuf) -> u64;
fn tag(self: Pin<&mut BlobstoreClient>, blobid: u64, tag: &str);
fn metadata(&self, blobid: u64) -> BlobMetadata;
}
}
ãããã次ã®ãã㪠Rust ãçæãããŸãã
#[repr(C)]
pub struct BlobstoreClient {
_private: ::cxx::private::Opaque,
}
pub fn new_blobstore_client() -> ::cxx::UniquePtr<BlobstoreClient> {
extern "C" {
#[link_name = "org$blobstore$cxxbridge1$new_blobstore_client"]
fn __new_blobstore_client() -> *mut BlobstoreClient;
}
unsafe { ::cxx::UniquePtr::from_raw(__new_blobstore_client()) }
}
impl BlobstoreClient {
pub fn put(&self, parts: &mut MultiBuf) -> u64 {
extern "C" {
#[link_name = "org$blobstore$cxxbridge1$BlobstoreClient$put"]
fn __put(
_: &BlobstoreClient,
parts: *mut ::cxx::core::ffi::c_void,
) -> u64;
}
unsafe {
__put(self, parts as *mut MultiBuf as *mut ::cxx::core::ffi::c_void)
}
}
}
// ...
- ããã°ã©ããŒã¯ãå ¥åããã·ã°ããã£ãæ£ç¢ºã§ããããšãä¿èšŒããå¿ èŠã¯ãããŸãããCXX ã¯ãã·ã°ããã£ã C++ ã§å®£èšããããã®ãšå®å šã«å¯Ÿå¿ãããšããããšãéçã«ä¿èšŒããŸãã
unsafe extern
ãããã¯ã䜿çšãããšãRust ããå®å šã«åŒã³åºãã C++ é¢æ°ã宣èšã§ããŸãã
å ±æã®å
#[cxx::bridge]
mod ffi {
#[derive(Clone, Debug, Hash)]
struct PlayingCard {
suit: Suit,
value: u8, // A=1ãJ=11ãQ=12ãK=13
}
enum Suit {
Clubs,
Diamonds,
Hearts,
Spades,
}
}
- C ã®ãããªïŒåäœïŒåæåã®ã¿ããµããŒããããŠããŸãã
- å
±æåã®
#[derive()]
ã§ã¯ããµããŒãããããã¬ã€ãã®æ°ãéãããŠããŸãã察å¿ããæ©èœã¯ C++ ã³ãŒãã§ãçæãããŸããããšãã°ãHash
ãå°åºãããšã察å¿ãã C++ åã®std::hash
ã®å®è£ ãçæãããŸãã
å ±æã®åæå
#[cxx::bridge]
mod ffi {
enum Suit {
Clubs,
Diamonds,
Hearts,
Spades,
}
}
çæããã Rust:
#![allow(unused)] fn main() { #[derive(Copy, Clone, PartialEq, Eq)] #[repr(transparent)] pub struct Suit { pub repr: u8, } #[allow(non_upper_case_globals)] impl Suit { pub const Clubs: Self = Suit { repr: 0 }; pub const Diamonds: Self = Suit { repr: 1 }; pub const Hearts: Self = Suit { repr: 2 }; pub const Spades: Self = Suit { repr: 3 }; } }
çæããã C++:
enum class Suit : uint8_t {
Clubs = 0,
Diamonds = 1,
Hearts = 2,
Spades = 3,
};
- Rust åŽã§ã¯ãå ±æåæåã«å¯ŸããŠçæãããã³ãŒãã¯ãå®éã«ã¯æ°å€ãã©ããããæ§é äœã§ããããã¯ãåæåã¯ã©ã¹ããªã¹ãããããã¹ãŠã®ããªã¢ã³ããšã¯ç°ãªãå€ãä¿æããããšã¯ C++ ã§ã¯ UB ã§ã¯ãªããRust è¡šçŸã¯åãåäœãããå¿ èŠãããããã§ãã
Rustã®ãšã©ãŒåŠç
#[cxx::bridge]
mod ffi {
extern "Rust" {
fn fallible(depth: usize) -> Result<String>;
}
}
fn fallible(depth: usize) -> anyhow::Result<String> {
if depth == 0 {
return Err(anyhow::Error::msg("fallible1 requires depth > 0"));
}
Ok("Success!".into())
}
Result
ãè¿ã Rust é¢æ°ã¯ãC++ åŽã§äŸå€ã«å€æãããŸãã- ã¹ããŒãããäŸå€ã¯åžžã«
rust::Error
åã§ãäž»ã«ãšã©ãŒ ã¡ãã»ãŒãžã®æååãååŸããæ段ãæäŸããŸãããšã©ãŒ ã¡ãã»ãŒãžã¯ããšã©ãŒåã®Display
ã®å®è£ ããååŸãããŸãã - Rust ãã C++ ã«ããã㯠ã¢ã³ã¯ã€ã³ããè¡ããšãããã»ã¹ã¯å¿ ãçŽã¡ã«çµäºããŸãã
C++ã®ãšã©ãŒåŠç
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("example/include/example.h");
fn fallible(depth: usize) -> Result<String>;
}
}
fn main() {
if let Err(err) = ffi::fallible(99) {
eprintln!("Error: {}", err);
process::exit(1);
}
}
Result
ãè¿ãããã«å®£èšããã C++ é¢æ°ã¯ãC++ åŽã§ã¹ããŒãããããããäŸå€ããã£ããããåŒã³åºãå ã® Rust é¢æ°ã«Err
å€ãšããŠè¿ããŸãã- CXX ããªããžã§Resultãè¿ãããã«å®£èšãããŠããªãextern "C++"é¢æ°ããäŸå€ãã¹ããŒããããšã
Result
ãè¿ããããšãããã°ã©ã 㯠C++ ã®std::terminate
ãåŒã³åºããŸãããã®åäœã¯ãåãäŸå€ãnoexcept
C++ é¢æ°ã§ã¹ããŒãããå Žåãšåçã§ãã
ãã®ä»ã®å
Rust å | C++ å |
---|---|
String | rust::String |
&str | rust::Str |
CxxString | std::string |
&[T] /&mut [T] | rust::Slice |
Box<T> | rust::Box<T> |
UniquePtr<T> | std::unique_ptr<T> |
Vec<T> | rust::Vec<T> |
CxxVector<T> | std::vector<T> |
- ãããã®åã¯ãå ±ææ§é äœã®ãã£ãŒã«ããšãextern é¢æ°ã®åŒæ°ãšæ»ãå€ã§äœ¿çšã§ããŸãã
- Rust ã®
String
ã¯std::string
ã«çŽæ¥ãããã³ã°ãããŸãããããã«ã¯æ¬¡ã®ãããªçç±ããããŸããstd::string
ã¯ãString
ãå¿ èŠãšãã UTF-8 äžå€æ¡ä»¶ãæºãããŸããã- ãã® 2 ã€ã®åã¯ã¡ã¢ãªå ã®ã¬ã€ã¢ãŠããç°ãªããããèšèªéã§çŽæ¥æž¡ãããšã¯ã§ããŸããã
std::string
ã¯ãRust ã®ã ãŒã ã»ãã³ãã£ã¯ã¹ãšäžèŽããªãã ãŒãã³ã³ã¹ãã©ã¯ã¿ãå¿ èŠãšãããããstd::string
ã Rust ã«å€ã§æž¡ãããšã¯ã§ããŸããã
Building in Android
cc_library_static
ãäœæããŠãCXX ã§çæãããããããŒãšãœãŒã¹ãã¡ã€ã«ãå«ã C++ ã©ã€ãã©ãªããã«ãããŸãã
cc_library_static {
name: "libcxx_test_cpp",
srcs: ["cxx_test.cpp"],
generated_headers: [
"cxx-bridge-header",
"libcxx_test_bridge_header"
],
generated_sources: ["libcxx_test_bridge_code"],
}
libcxx_test_bridge_header
ãšlibcxx_test_bridge_code
ããCXX CXXã«ããçæããã C++ ãã€ã³ãã£ã³ã°ã«å¯ŸããäŸåé¢ä¿ã§ããããšã説æããŸãã次ã®ã¹ã©ã€ãã§ãããããã©ã®ãããªèšè¿°ã«ãªã£ãŠãããã説æããŸãã- ãŸããäžè¬ç㪠CXX å®çŸ©ãååŸããããã«ã¯ã
cxx-bridge-header
ã©ã€ãã©ãªã«äŸåããå¿ èŠãããããšã«ã泚æããŠãã ããã - Android 㧠CXX ã䜿çšããããã®è©³çŽ°ãªããã¥ã¡ã³ãã«ã€ããŠã¯ãAndroid ã®ããã¥ã¡ã³ããã芧ãã ããããã®ãªã³ã¯ãã¯ã©ã¹ãšå ±æããŠãåè¬è ãåŸã§æé ã確èªã§ããããã«ããããšãããããããŸãã
Building in Android
genrule ã 2 ã€äœæããŸãã1 ã€ã¯ CXX ããããŒã®çæçšããã 1 ã€ã¯ CXX ãœãŒã¹ãã¡ã€ã«ã®çæçšã§ããããã㯠cc_library_static
ãžã®å
¥åãšããŠäœ¿çšãããŸãã
// lib.rs ã«ãã Rustãããšã¯ã¹ããŒããããé¢æ°ã«å¯Ÿãã
// C++ ãã€ã³ãã£ã³ã°ãå«ã C++ ããããŒãçæããŸãã
genrule {
name: "libcxx_test_bridge_header",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) --header > $(out)",
srcs: ["lib.rs"],
out: ["lib.rs.h"],
}
// Rust ãåŒã³åºã C++ ã³ãŒããçæããŸãã
genrule {
name: "libcxx_test_bridge_code",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) > $(out)",
srcs: ["lib.rs"],
out: ["lib.rs.cc"],
}
cxxbridge
ããŒã«ã¯ãããªããž ã¢ãžã¥ãŒã«ã® C++ åŽãçæããã¹ã¿ã³ãã¢ãã³ ããŒã«ã§ããAndroid ã«çµã¿èŸŒãŸããŠãããSoong ããŒã«ãšããŠå©çšã§ããŸãã- æ
£äŸãšããŠãRust ãœãŒã¹ãã¡ã€ã«ã
lib.rs
ã®å Žåãããã㌠ãã¡ã€ã«ã®ååã¯lib.rs.h
ããœãŒã¹ãã¡ã€ã«ã®ååã¯lib.rs.cc
ãšãªããŸãããã ãããã®åœåèŠåã¯åŒ·å¶ã§ã¯ãããŸããã
Building in Android
libcxx
ãš cc_library_static
ã«äŸåãã rust_binary
ãäœæããŸãã
rust_binary {
name: "cxx_test",
srcs: ["lib.rs"],
rustlibs: ["libcxx"],
static_libs: ["libcxx_test_cpp"],
}
Java ãšã®çžäºéçšæ§
Java ã§ã¯ãJava Native InterfaceïŒJNIïŒ ãä»ããŠå
±æãªããžã§ã¯ããèªã¿èŸŒãããšãã§ããŸããjni
ã¯ã¬ãŒã ã䜿çšãããšãäºææ§ã®ããã©ã€ãã©ãªãäœæã§ããŸãã
ãŸããJava ã«ãšã¯ã¹ããŒããã Rust é¢æ°ãäœæããŸãã
interoperability/java/src/lib.rs:
#![allow(unused)] fn main() { //! Rust <-> Java FFI ã®ãã¢ã use jni::objects::{JClass, JString}; use jni::sys::jstring; use jni::JNIEnv; /// HelloWorld::hello method implementation. // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] pub extern "system" fn Java_HelloWorld_hello( mut env: JNIEnv, _class: JClass, name: JString, ) -> jstring { let input: String = env.get_string(&name).unwrap().into(); let greeting = format!("Hello, {input}!"); let output = env.new_string(greeting).unwrap(); output.into_raw() } }
interoperability/java/Android.bp:
rust_ffi_shared {
name: "libhello_jni",
crate_name: "hello_jni",
srcs: ["src/lib.rs"],
rustlibs: ["libjni"],
}
次ã«ãJava ãããã®é¢æ°ãåŒã³åºããŸãã
interoperability/java/HelloWorld.java:
class HelloWorld {
private static native String hello(String name);
static {
System.loadLibrary("hello_jni");
}
public static void main(String[] args) {
String output = HelloWorld.hello("Alice");
System.out.println(output);
}
}
interoperability/java/Android.bp:
java_binary {
name: "helloworld_jni",
srcs: ["HelloWorld.java"],
main_class: "HelloWorld",
required: ["libhello_jni"],
}
æåŸã«ããã€ããªããã«ããåæãå®è¡ããŸãã
m helloworld_jni
adb sync # requires adb root && adb remount
adb shell /system/bin/helloworld_jni
Chromium ã® Rust ãžãããã
Rust 㯠Chromium ã®ãµãŒãããŒã㣠ã©ã€ãã©ãªã§ãµããŒããããŠããŸããRust ãšæ¢åã® Chromium C++ ã³ãŒããæ¥ç¶ããã«ã¯ããã¡ãŒã¹ã ããŒãã£ã®ã°ã«ãŒã³ãŒãã䜿çšããŸãã
æ¬æ¥ã¯ãRust ã§æååã䜿ã£ãŠé¢çœãããšãããããšæããŸããããèªåã®æ åœéšåã«UTF8 æååã衚瀺ããã³ãŒããããå Žåã¯ãããã§è¿°ã¹ãéšåã§ã¯ãªããèªåã®ã³ãŒãã«å¯ŸããŠãã®æé ãå®è¡ããŠæ§ããŸããã
ã»ããã¢ãã
Chromium ããã«ãããŠå®è¡ã§ããããšã確èªããŸããã³ãŒããæ¯èŒçæè¿ã®ãã®ïŒ2023 幎 11 æã«å¯Ÿå¿ããã³ãããäœçœ® 1223636 以éïŒã§ããã°ãä»»æã®ãã©ãããã©ãŒã ãšãã«ããã©ã°ã®ã»ããã§åé¡ãããŸããã
gn gen out/Debug
autoninja -C out/Debug chrome
out/Debug/chrome # or on Mac, out/Debug/Chromium.app/Contents/MacOS/Chromium
ïŒå埩åŠçã®æéãæçã«ããã«ã¯ãã³ã³ããŒãã³ãã®ãããã°ãã«ããããããããŸãããããããã©ã«ãã§ãïŒ
ãŸã 確èªããŠããªãå Žåã¯ãChromium ã®ãã«ãæ¹æ³ ã確èªããŠãã ããããªããChromium ããã«ãããããã®ã»ããã¢ããã«ã¯æéãããããŸãã
ãŸããVisual Studio Code ãã€ã³ã¹ããŒã«ããŠããããšãããããããŸãã
æŒç¿ã«ã€ããŠ
ã³ãŒã¹ã®ãã®ããŒãã«ã¯ãçžäºã«é¢é£ããäžé£ã®æŒç¿ããããŸããã³ãŒã¹ã®æåŸã ãã§ãªããå šäœãéããŠæŒç¿ãè¡ããŸããç¹å®ã®ããŒããå®äºããæéããªãå ŽåããåŸã§è¿œãã€ãã°ããããå¿é ã¯ãããŸããã
Chromium ãš Cargo ã®ãšã³ã·ã¹ãã ã®æ¯èŒ
The Rust community typically uses cargo
and libraries from crates.io. Chromium is built using gn
and ninja
and a curated set of dependencies.
Rust ã§ã³ãŒããèšè¿°ããéã¯ã次ã®éžæè¢ããããŸãã
//build/rust/*.gni
ã®ãã³ãã¬ãŒãïŒäŸ: åŸã§èª¬æããrust_static_library
ïŒãåèã«ããŠãgn
ãšninja
ã䜿çšããŸããããã«ã¯ãChromium ã®ç£æ»æžã¿ã®ããŒã«ãã§ãŒã³ãšã¯ã¬ãŒãã䜿çšãããŸããcargo
ã䜿çšããŸãããå®éã®å©çšãChromium ã®ç£æ»æžã¿ã®ããŒã«ãã§ãŒã³ãšã¯ã¬ãŒãã«å¶éããŸããcargo
ã䜿çšããããŒã«ãã§ãŒã³ ã ã€ã³ã¿ãŒãããããããŠã³ããŒãããã¯ã¬ãŒã ãä¿¡é ŒããŸãã
ããããã¯ãgn
ãš ninja
ã«çŠç¹ãåœãŠãŸãããããã䜿çšããããšã§ãChromium ãã©ãŠã¶ã« Rust ã³ãŒããçµã¿èŸŒãããšãã§ããŸãããããšã¯å¥ã«ãCargo 㯠Rust ãšã³ã·ã¹ãã ã®éèŠãªéšåã§ããã䜿ãããªããããã«ãªã£ãŠããã¹ãã§ãã
Mini exercise
å°äººæ°ã®ã°ã«ãŒãã«åãã以äžãè¡ããŸãã
cargo
ãã¡ãªãããããããå¯èœæ§ã®ããã·ããªãªããã¬ã€ã³ã¹ããŒãã³ã°ãããããã®ã·ããªãªã®ãªã¹ã¯ ãããã¡ã€ã«ãè©äŸ¡ããŸããgn
ãninja
ããªãã©ã€ã³ã®cargo
ãªã©ã䜿çšããéã«ãã©ã®ããŒã«ãã©ã€ãã©ãªã人ã ãä¿¡é Œããªããã°ãªããªããã«ã€ããŠè©±ãåããŸãã
åè¬è ã«ãæŒç¿ãå®äºããåã«ã¹ããŒã«ãŒ ããŒããã®ãããªããããé¡ãããŠãã ãããã³ãŒã¹ã®åè¬è å士ãå°ççã«éãŸã£ãŠãããšä»®å®ããŠã3ïœ4 人ã®å°äººæ°ã®ã°ã«ãŒãã§è©±ãåã£ãŠãããããã«ãé¡ãããŠãã ããã
æŒç¿ã®ååã«é¢ããã¡ã¢ãšãã³ãïŒãCargo ãã¡ãªãããããããã·ããªãªãïŒ:
-
ããŒã«ã®äœææã Chromium ã®äžéšã®ãããã¿ã€ãã³ã°æã«ãcrates.io ã©ã€ãã©ãªã®å å®ãããšã³ã·ã¹ãã ã«ã¢ã¯ã»ã¹ã§ããã®ã¯çŽ æŽãããããšã§ããã»ãŒãã¹ãŠã®äºæã«ã€ããŠã¯ã¬ãŒããçšæãããŠããã倧æŠã®å Žåã¯ãšãŠãå¿«é©ã«äœ¿çšã§ããŸãïŒã³ãã³ãã©ã€ã³ã解æããããã®
clap
ãããŸããŸãªåœ¢åŒãšã®éã§ã·ãªã¢ã«åãŸãã¯éã·ãªã¢ã«åãè¡ãããã®serde
ãã€ãã¬ãŒã¿ãæäœããããã®itertools
ãªã©ïŒãcargo
ã䜿çšãããšãã©ã€ãã©ãªãç°¡åã«è©Šãããšãã§ããŸãïŒCargo.toml
ã« 1 è¡è¿œå ããŠã³ãŒãã®èšè¿°ãéå§ããã ãã§ãïŒãperl
ã®æ®åã«åœ¹ç«ã£ã CPAN ããpython
ã«ãããpip
ãšæ¯èŒããŠã¿ããšè¯ããããããŸããã
-
äž»èŠãª Rust ããŒã«ïŒãã€ããªãŒãçŸåšã®å®å®çãå€ãå®å®çã§åäœããå¿ èŠãããã¯ã¬ãŒãããã¹ããããšãã«ãå¥ã®
rustc
ããŒãžã§ã³ã«åãæ¿ããã®ã«äœ¿çšããrustup
ãªã©ïŒã ãã§ãªãããµãŒãããŒã㣠ããŒã«ã®ãšã³ã·ã¹ãã ïŒMozilla ãæäŸããã»ãã¥ãªãã£ç£æ»ã®å®¹æåãšå ±æã®ããcargo vet
ããã³ãããŒã¯ã容æã«å®è¡ããæ¹æ³ãæäŸããcriterion
ã¯ã¬ãŒããªã©ïŒã«ãããéçºãšã¯ã¹ããªãšã³ã¹ã¯éåžžã«å¿«é©ãšãªã£ãŠããŸããcargo
ã䜿çšãããšãcargo install --locked cargo-vet
ãä»ããŠããŒã«ãç°¡åã«è¿œå ã§ããŸãã- Chrome æ¡åŒµæ©èœã VScode æ¡åŒµæ©èœãšæ¯èŒããŠã¿ãã®ãè¯ããããããŸããã
-
cargo
ãé©åãªéžæãšãªããããªãå¹ åºãæ±çšçãªãããžã§ã¯ãã®äŸã以äžã«ç€ºããŸãã- æå€ãããããŸããããæ¥çã§ã¯ã³ãã³ãã©ã€ã³ ããŒã«ã®äœæã«äœ¿çšããèšèªãšããŠãRust ã®äººæ°ãé«ãŸã£ãŠããŸããã©ã€ãã©ãªã®å¹ ãšãšã«ãŽããã¯ã¹ã®ç¹ã§ Python ã«å¹æµãã€ã€ããè±å¯ãªåã·ã¹ãã ã®ãããã§å ç¢ã§ãïŒã€ã³ã¿ããªã¿èšèªã§ã¯ãªãã³ã³ãã€ã«èšèªãªã®ã§ïŒå®è¡é床ãé«éã§ãã
- Rust ãšã³ã·ã¹ãã ã«åå ããã«ã¯ãCargo ãªã©ã®æšæºã® Rust ããŒã«ã䜿çšããå¿ èŠããããŸããå€éšããã³ã³ããªãã¥ãŒã·ã§ã³ãåããChromium 以å€ïŒBazel ã Android / Soong ã®ãã«ãç°å¢ãªã©ïŒã§ã®äœ¿çšãæšå¥šãããã©ã€ãã©ãªã§ã¯ãCargo ã䜿çšããããšãããããããŸãã
-
cargo
ããŒã¹ã® Chromium é¢é£ãããžã§ã¯ãã®äŸ:serde_json_lenient
ïŒGoogle ã®ä»ã®éšéã§ãã¹ãããçµæãPR ã®ããã©ãŒãã³ã¹ãåäžïŒfont-types
ãªã©ã®ãã©ã³ãåã©ã€ãã©ãªgnrt
ããŒã«ïŒãã®ã³ãŒã¹ã®åŸåã§åãäžããŸãïŒã¯ãã³ãã³ãã©ã€ã³ã®è§£æã«ã¯clap
ã䜿çšããæ§æãã¡ã€ã«ã«ã¯toml
ã䜿çšããŸãã- Disclaimer: a unique reason for using
cargo
was unavailability ofgn
when building and bootstrapping Rust standard library when building Rust toolchain. run_gnrt.py
uses Chromium's copy ofcargo
andrustc
.gnrt
depends on third-party libraries downloaded from the internet, butrun_gnrt.py
askscargo
that only--locked
content is allowed viaCargo.lock
.)
- Disclaimer: a unique reason for using
以äžã®ã¢ã€ãã ã¯ãæé»çãŸãã¯æ瀺çã«ä¿¡é ŒãããŠãããšã¿ãªããŠæ§ããŸããã
- LLVM ã©ã€ãã©ãªãClang ã³ã³ãã€ã©ã
rustc
ãœãŒã¹ïŒGitHub ããååŸãããRust ã³ã³ãã€ã© ããŒã ã«ããã¬ãã¥ãŒãåãããã®ïŒãããŒãã¹ãã©ããçšã«ããŠã³ããŒãããããã€ã㪠Rust ã³ã³ãã€ã©ã«äŸåããrustc
ïŒRust ã³ã³ãã€ã©ïŒ rustup
ïŒrustup
ã¯rustc
ãšåãã https://github.com/rust-lang/ çµç¹ã®åäžã§éçºãããŠããããšã説æãããšè¯ããããããŸããïŒcargo
ãrustfmt
ãªã©- ããŸããŸãªå
éšã€ã³ãã©ã¹ãã©ã¯ãã£ïŒ
rustc
ããã«ããã botãäºåæ§ç¯æžã¿ã®ããŒã«ãã§ãŒã³ã Chromium ãšã³ãžãã¢ã«é åžããããã®ã·ã¹ãã ãªã©ïŒ cargo audit
ãcargo vet
ãªã©ã® Cargo ããŒã«//third_party/rust
ã«åã蟌ãŸããRust ã©ã€ãã©ãªïŒsecurity@chromium.org ãç£æ»ïŒ- ãã®ä»ã® Rust ã©ã€ãã©ãªïŒããããªãã®ãããã°ãéåžžã«äººæ°ããããã䜿çšããããã®ããããŸãïŒ
Chromium ã® Rust ããªã·ãŒ
Chromium ã§ã¯ãChromium ã® ãšãªã¢ ãã¯ãã«ã« ãªãŒã ã«ãã£ãŠæ¿èªãããŠãããŸããªã±ãŒã¹ãé€ãããã¡ãŒã¹ã ããŒãã£ã§ã®Rust䜿çšã¯ãŸã èš±å¯ãããŠããŸããã
ãµãŒãããŒã㣠ã©ã€ãã©ãªã«é¢ãã Chromium ã®ããªã·ãŒã«ã€ããŠã¯ããã¡ã ãã芧ãã ãããRust ã¯ãããã©ãŒãã³ã¹ãã»ãã¥ãªãã£ãé«ããããã§æé©ãªéžæè¢ã§ããå Žåãå«ããããŸããŸãªç¶æ³ã§ãµãŒãããŒã㣠ã©ã€ãã©ãªã«äœ¿çšããããšãèš±å¯ãããŠããŸãã
C / C++ API ãçŽæ¥å ¬éãã Rust ã©ã€ãã©ãªã¯ã»ãšãã©ãªããããããããã©ã€ãã©ãªã®ã»ãŒãã¹ãŠã§ãå°éã®ãã¡ãŒã¹ã ããŒã㣠ã°ã«ãŒã³ãŒããå¿ èŠã«ãªããŸãã
ç¹å®ã®ãµãŒãããŒã㣠ã¯ã¬ãŒãçšã®ãã¡ãŒã¹ã ããŒã㣠Rust ã°ã«ãŒã³ãŒãã¯éåžžã
third_party/rust/<crate>/<version>/wrapper
ã«çœ®ãããã¹ãã§ãã
以äžã®çç±ãããæ¬æ¥ã®ã³ãŒã¹ã§ã¯ä»¥äžã«çŠç¹ãåœãŠãŸãã
- ãµãŒãããŒãã£ã® Rust ã©ã€ãã©ãªïŒãã¯ã¬ãŒããïŒãå°å ¥ããã
- Chromium C++ ããã¯ã¬ãŒãã䜿çšã§ããããã«ã°ã«ãŒã³ãŒããèšè¿°ããã
ãã®ããªã·ãŒãå€æŽãããå Žåã¯ãããã«åãããŠã³ãŒã¹ãå€æŽãããŸãã
Build rules
Rust ã³ãŒãã¯éåžžãcargo
ã䜿çšããŠãã«ããããŸããChromium ã¯å¹çãé«ããããã« gn
ãš ninja
ã䜿çšããŠãã«ããããŸããããã®éçã«ãŒã«ã«ãã£ãŠæ倧éã®äžŠååŠçãå¯èœã«ãªããŸããRust ãäŸå€ã§ã¯ãããŸããã
Chromium ã« Rust ã³ãŒããè¿œå ãã
Chromium ã®æ¢åã® BUILD.gn
ãã¡ã€ã«ã§ãrust_static_library
ã宣èšããŸãã
import("//build/rust/rust_static_library.gni")
rust_static_library("my_rust_lib") {
crate_root = "lib.rs"
sources = [ "lib.rs" ]
}
ä»ã® Rust ã¿ãŒã²ããã« deps
ãè¿œå ããããšãã§ããŸããåŸã§ããã䜿çšããŠããµãŒãããŒãã£ã®ã³ãŒããžã®äŸåãèšå®ããŸãã
ã¯ã¬ãŒãã«ãŒããšãœãŒã¹ã®å®å
šãªãªã¹ãã®äž¡æ¹ãæå®ããå¿
èŠããããŸããcrate_root
㯠Rust ã³ã³ãã€ã©ã«æž¡ããããã¡ã€ã«ã§ãã³ã³ãã€ã«åäœã®ã«ãŒããã¡ã€ã«ïŒé垞㯠lib.rs
ïŒãè¡šããŸããsources
ã¯ãã¹ãŠã®ãœãŒã¹ãã¡ã€ã«ã®å®å
šãªãªã¹ãã§ãåãã«ããå¿
èŠãªã¿ã€ãã³ã°ã ninja
ãå€æããããã«å¿
èŠã§ãã
ïŒRust ã§ã¯ã¯ã¬ãŒãå
šäœãã³ã³ãã€ã«åäœã§ãããããsource_set
ãšåŒã¹ããããªãã®ã¯ååšããŸãããstatic_library
ãæå°åäœã§ãïŒã
åè¬è ã¯ããªã Rust ã®éçã©ã€ãã©ãªã«å¯Ÿãã gn ã®çµã¿èŸŒã¿ãµããŒã ã§ã¯ãªããgn ãã³ãã¬ãŒãã䜿çšããå¿ èŠãããã®ãçåã«æããããããŸããããã®çãã¯ããã®ãã³ãã¬ãŒãã CXX çžäºéçšãRustã®featuresãåäœãã¹ãããµããŒãããŠããããã§ãããã®äžéšã¯åŸã§äœ¿çšããŸãã
unsafe
Rust ã³ãŒãã®è¿œå
å®å
šã§ãªã Rust ã³ãŒãã¯ããã©ã«ãã§ã¯ rust_static_library
ã§çŠæ¢ãããŠãããã³ã³ãã€ã«ã§ããŸãããå®å
šã§ãªã Rust ã³ãŒããå¿
èŠãªå Žåã¯ãgn ã¿ãŒã²ããã« allow_unsafe = true
ãè¿œå ããŸãïŒãããå¿
èŠã«ãªãç¶æ³ã«ã€ããŠã¯ããã®ã³ãŒã¹ã®åŸåã§èª¬æããŸãïŒã
import("//build/rust/rust_static_library.gni")
rust_static_library("my_rust_lib") {
crate_root = "lib.rs"
sources = [
"lib.rs",
"hippopotamus.rs"
]
allow_unsafe = true
}
Chromium C++ããRustã®ã³ãŒãã«äŸåããã
äžèšã®ã¿ãŒã²ãããããã€ãã® Chromium C++ ã¿ãŒã²ããã® deps
ã«è¿œå ããã ãã§ãã
import("//build/rust/rust_static_library.gni")
rust_static_library("my_rust_lib") {
crate_root = "lib.rs"
sources = [ "lib.rs" ]
}
# ãŸã㯠source_setãstatic_library ãªã©
component("preexisting_cpp") {
deps = [ ":my_rust_lib" ]
}
Visual Studio Code
Rust ã³ãŒãã§ã¯åãçç¥ãããŠãããããåªãã IDE 㯠C++ ã®å Žåãããããã«æçšã§ããVisual Studio Code 㯠Chromium ã® Rust ã§é©åã«æ©èœããŸããVisual Studio Code ã䜿çšããã«ãããã以äžã®ç¹ã確èªããŠãã ããã
- VSCode ã«ã以åã®åœ¢åŒã® Rust ãµããŒãã§ã¯ãªãã
rust-analyzer
æ¡åŒµæ©èœãããããšãç¢ºèª gn gen out/Debug --export-rust-project
ïŒãŸãã¯ããªãã®ãããžã§ã¯ãã«ãããåæ§ã®åºåãã£ã¬ã¯ããªïŒln -s out/Debug/rust-project.json rust-project.json
IDE ã«æççãªåè¬è ã«å¯ŸããŠã¯ãrust-analyzer ã®ã³ãŒã ã¢ãããŒã·ã§ã³ãšæ¢çŽ¢æ©èœã®ãã¢ãè¡ããšè¯ããããããŸããã
以äžã®æé ã«æ²¿ã£ãŠãã¢ãè¡ãããšãããããããŸãïŒä»£ããã«èªåãæã粟éããŠãã Chromium é¢é£ã® Rustã³ãŒãã䜿çšããŠãæ§ããŸããïŒã
components/qr_code_generator/qr_code_generator_ffi_glue.rs
ãéããŸãã- `qr_code_generator_ffi_glue.rs ã®
QrCode::new
åŒã³åºãïŒ26 è¡ç®ä»è¿ïŒã«ã«ãŒãœã«ãåãããŸãã - show documentation ã®ãã¢ãè¡ããŸãïŒäžè¬çãªãã€ã³ãã£ã³ã°: vscode = ctrl kiãvim/CoC = KïŒã
- go to definition ã®ãã¢ãè¡ããŸãïŒäžè¬çãªãã€ã³ãã£ã³ã°: vscode = F12ãvim/CoC = g dïŒïŒããã«ããã
//third_party/rust/.../qr_code-.../src/lib.rs
ã«ç§»åããŸãïŒã - outline ã®ãã¢ãè¡ãã
QrCode::with_bits
ã¡ãœããã«ç§»åããŸãïŒ164 è¡ç®ä»è¿ãã¢ãŠãã©ã€ã³ã¯ VSCode ã®ãã¡ã€ã« ãšã¯ã¹ãããŒã© ãã€ã³ã«ãããŸããäžè¬ç㪠vim/CoC ãã€ã³ãã£ã³ã° = space oïŒã - Demo type annotations (there are quite a few nice examples in the
QrCode::with_bits
method)
BUILD.gn
ãã¡ã€ã«ã®ç·šéåŸã¯ gn gen ... --export-rust-project
ãåå®è¡ããå¿
èŠãããããšã説æããŠãã ããïŒãã®ã»ãã·ã§ã³ã®æŒç¿å
šäœã§æ°åè¡ããŸãïŒã
Build rules exercise
Chromium ã®ãã«ãã§ã以äžãå«ãæ°ãã Rust ã¿ãŒã²ããã //ui/base/BUILD.gn
ã«è¿œå ããŸãã
#![allow(unused)] fn main() { // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] pub extern "C" fn hello_from_rust() { println!("Hello from Rust!") } }
Important: note that no_mangle
here is considered a type of unsafety by the Rust compiler, so you'll need to allow unsafe code in your gn
target.
ãã®æ°ãã Rust ã¿ãŒã²ããã //ui/base:base
ã®äŸåé¢ä¿ãšããŠè¿œå ããŸãããã®é¢æ°ã ui/base/resource/resource_bundle.cc
ã®å
é ã§å®£èšããŸãïŒåŸã»ã©ããã€ã³ãã£ã³ã°çæããŒã«ã§ãããèªååããæ¹æ³ã説æããŸãïŒã
extern "C" void hello_from_rust();
ãã®é¢æ°ã ui/base/resource/resource_bundle.cc
å
ã®ã©ããããåŒã³åºããŸããããããã¯ResourceBundle::MaybeMangleLocalizedString
ã®å
é ããåŒã³åºãããšã§ããChromium ããã«ãããŠå®è¡ãã"Hello from Rust!" ãäœåºŠãåºåãããŠããããšã確èªããŸãã
VSCode ã䜿çšããŠããå Žåã¯ãVSCode ã§é©åã«åäœããããã« Rust ãèšå®ããŸããããã¯ãåŸç¶ã®æŒç¿ã§åœ¹ç«ã¡ãŸããèšå®ãå®äºããããprintln!
㧠"Go to definition" ãå³ã¯ãªãã¯ã§å©çšã§ããããã«ãªããŸãã
åèæ å ±
rust_static_library
gn ãã³ãã¬ãŒã ã§äœ¿çšã§ãããªãã·ã§ã³- Information about
#[unsafe(no_mangle)]
extern "C"
ã«é¢ããæ å ±- gn ã®
--export-rust-project
ã¹ã€ããã«é¢ããæ å ± - VSCode ã« rust-analyzer ãã€ã³ã¹ããŒã«ããæ¹æ³
ãã®äŸã¯ãå ±éã®çžäºéçšèšèªã§ãã C ã«éçŽãããŠãããããäžè¬çã§ã¯ãããŸãããC++ ãš Rust ã¯ã©ã¡ãããC ABI é¢æ°ããã€ãã£ãã«å®£èšããŠåŒã³åºãããšãã§ããŸããã³ãŒã¹ã®åŸåã§ãC++ ã Rust ã«çŽæ¥æ¥ç¶ããŸãã
allow_unsafe = true
is required here because #[unsafe(no_mangle)]
might allow Rust to generate two functions with the same name, and Rust can no longer guarantee that the right one is called.
çŽç²ãª Rust å®è¡å¯èœãã¡ã€ã«ãå¿
èŠãªå Žåã¯ãrust_executable
gn ãã³ãã¬ãŒãã䜿çšããŠè¡ãããšãã§ããŸãã
ãã¹ã
Rust ã³ãã¥ããã£ã¯éåžžããã¹ã察象ã®ã³ãŒããšåããœãŒã¹ãã¡ã€ã«ã«é 眮ãããã¢ãžã¥ãŒã«ã§åäœãã¹ããäœæããŸããããã¯æ¬ã³ãŒã¹ã® åã®éšå ã§èª¬æããŠããã以äžã®ããã«ãªããŸãã
#![allow(unused)] fn main() { #[cfg(test)] mod tests { #[test] fn my_test() { todo!() } } }
Chromium ã§ã¯åäœãã¹ããå¥ã®ãœãŒã¹ãã¡ã€ã«ã«é
眮ããŠãããRust ã§ããã®æ¹éãç¶ç¶ããŸããããã«ããããã¹ããåžžã«æ€åºå¯èœã«ãªãã2 床ç®ã«ïŒtest
æ§æã§ïŒ.rs
ãã¡ã€ã«ãåãã«ãããå¿
èŠããªããªããŸãã
ãã®çµæãChromium 㧠Rust ã³ãŒãããã¹ãããããã®æ¬¡ã®éžæè¢ãæäŸãããŸãã
- ãã€ãã£ã Rust ãã¹ãïŒäŸ:
#[test]
ïŒã//third_party/rust
以å€ã§ã¯æšå¥šãããŸããã - C++ ã§äœæãããFFI åŒã³åºããä»ã㊠Rust ãå®è¡ãã
gtest
ãã¹ããRust ã³ãŒããåãªãèãFFI ã¬ã€ã€ã§ãããæ¢åã®åäœãã¹ãã§ä»åŸãã®æ©èœãæŒããªãã«ããŒãããå Žåã«ã¯ååã§ãã - Rust ã§äœæãããå
¬é API ãä»ããŠãã¹ã察象ã®ã¯ã¬ãŒãã䜿çšãã
gtest
ãã¹ãïŒå¿ èŠã«å¿ããŠpub mod for_testing { ... }
ã䜿çšïŒãããã«ã€ããŠã¯ã次ã®æ°æã®ã¹ã©ã€ãã§èª¬æããŸãã
ãµãŒãããŒã㣠ã¯ã¬ãŒãã®ãã€ãã£ã Rust ãã¹ããæçµçã« Chromium bot ã«ãã£ãŠå®è¡ãããå¿ èŠãããããšã説æããŸãïŒãã®ãããªãã¹ããå¿ èŠã«ãªãããšã¯ãã£ãã«ãªãããµãŒãããŒãã£ã®ã¯ã¬ãŒããè¿œå ãŸãã¯æŽæ°ããåŸã«ã®ã¿å¿ èŠãšãªããŸãïŒã
C++ ã®gtest
ãš Rust ã®gtest
ãã©ã®ãããªå Žåã«äœ¿ãã¹ãããããã€ãã®äŸã䜿ã£ãŠèª¬æãããšããã§ãããã
-
QR ã«ã¯ããã¡ãŒã¹ã ããŒãã£ã® Rust ã¬ã€ã€ã®æ©èœã¯ã»ãšãã©ãããŸããïŒåãªãã·ã³ FFI ã°ã«ãŒã§ãïŒããã®ãããC++ ãš Rust ã®å®è£ ã®äž¡æ¹ããã¹ãããã«ã¯ãæ¢åã® C++ åäœãã¹ãã䜿çšããŸãïŒãã¹ãããã©ã¡ãŒã¿åãã
ScopedFeatureList
ã䜿çšã㊠Rust ãæå¹åãŸãã¯ç¡å¹åã§ããããã«ãªã£ãŠããŸãïŒã -
ä»®å®ã®ããŸãã¯éçºäžã® PNG çµ±åã§ã¯ã
libpng
ã§ã¯æäŸãããŠããã®ã«ãpng
ã¯ã¬ãŒãã§ã¯æ¬ èœããŠãããã¯ã»ã«å€æïŒRGBA => BGRAãã¬ã³ãè£æ£ãªã©ïŒã®ã¡ã¢ãªã»ãŒããªå®è£ ãå¿ èŠãšãªãå ŽåããããŸãããã®ãããªæ©èœã®éçºã«ãããŠã¯ãå¥éRustã§ãã¹ããäœæããããšã圹ç«ã€å ŽåããããŸãã
rust_gtest_interop
ã©ã€ãã©ãª
rust_gtest_interop
ã©ã€ãã©ãªã䜿çšãããšã次ã®ããšãã§ããŸãã
- Rust é¢æ°ã
gtest
ãã¹ãã±ãŒã¹ãšããŠäœ¿çšããïŒ#[gtest(...)]
å±æ§ã䜿çšïŒã expect_eq!
ãªã©ã®ãã¯ãã䜿çšããïŒassert_eq!
ãšäŒŒãŠããŸãããã¢ãµãŒã·ã§ã³ã倱æããŠããããã¯ããããã¹ããçµäºããŸããïŒã
Example:
use rust_gtest_interop::prelude::*;
#[gtest(MyRustTestSuite, MyAdditionTest)]
fn test_addition() {
expect_eq!(2 + 2, 4);
}
Rust ãã¹ãçšã® GN ã«ãŒã«
Rust ã® gtest
ãã¹ãããã«ãããæãç°¡åãªæ¹æ³ã¯ãC++ ã§äœæããããã¹ãããã§ã«å«ãŸããŠããæ¢åã®ãã¹ããã€ããªã«è¿œå ããããšã§ãã次ã«äŸã瀺ããŸãã
test("ui_base_unittests") {
...
sources += [ "my_rust_lib_unittest.rs" ]
deps += [ ":my_rust_lib" ]
}
å¥éãstatic_library
㧠Rust ãã¹ããäœæããããšãå¯èœã§ããããµããŒã ã©ã€ãã©ãªãžã®äŸåé¢ä¿ãæåã§å®£èšããå¿
èŠããããŸãã
rust_static_library("my_rust_lib_unittests") {
testonly = true
is_gtest_unittests = true
crate_root = "my_rust_lib_unittest.rs"
sources = [ "my_rust_lib_unittest.rs" ]
deps = [
":my_rust_lib",
"//testing/rust_gtest_interop",
]
}
test("ui_base_unittests") {
...
deps += [ ":my_rust_lib_unittests" ]
}
chromium::import!
ãã¯ã
GN ã® deps
ã« :my_rust_lib
ãè¿œå ããåŸããmy_rust_lib_unittest.rs
ãã my_rust_lib
ãã€ã³ããŒãããŠäœ¿çšããæ¹æ³ã«ã€ããŠåŠã¶å¿
èŠããããŸããmy_rust_lib
ã«ã¯æ瀺ç㪠crate_name
ãæå®ãããŠããªããããã¯ã¬ãŒãåã¯ã¿ãŒã²ããã®ãã«ãã¹ãšååã«åºã¥ããŠçæãããŸãã幞ããèªåçã«ã€ã³ããŒãããã chromium
ã¯ã¬ãŒããã chromium::import!
ãã¯ãã䜿çšããã°ããã®ãããªæ±ãã«ããååã®äœ¿çšãåé¿ã§ããŸãã
chromium::import! {
"//ui/base:my_rust_lib";
}
use my_rust_lib::my_function_under_test;
å éšã§ããã¯ãã¯æ¬¡ã®ããã«å±éãããŸãã
extern crate ui_sbase_cmy_urust_ulib as my_rust_lib;
use my_rust_lib::my_function_under_test;
詳ããã¯ãchromium::import
ãã¯ãã® ããã¥ã¡ã³ã ã³ã¡ã³ã ãã芧ãã ããã
rust_static_library
ã¯ãcrate_name
ããããã£ã«ããæ瀺çãªååã®æå®ããµããŒãããŠããŸãããã¯ã¬ãŒãåã¯ã°ããŒãã«ã«äžæã§ããå¿
èŠããããããããã¯æšå¥šãããŸãããcrates.io ã¯ã¯ã¬ãŒãåã®äžææ§ãä¿èšŒããŠãããããcargo_crate
GN ã¿ãŒã²ããïŒåŸè¿°ã® gnrt
ããŒã«ã§çæïŒã¯çãã¯ã¬ãŒãåã䜿çšããŸãã
Testing exercise
æ°ããªæŒç¿ã®æéã§ãïŒ
Chromium ãã«ãã§ä»¥äžãè¡ã£ãŠãã ããã
hello_from_rust
ã®æšªã«ãã¹ãå¯èœãªé¢æ°ãè¿œå ããŸããããšãã°ãåŒæ°ãšããŠåãåã£ã 2 ã€ã®æŽæ°ãè¿œå ãããn çªç®ã®ãã£ããããæ°ãèšç®ãããã¹ã©ã€ã¹å ã®æŽæ°ãåèšããããªã©ãèããããŸãã- æ°ããé¢æ°ã®ãã¹ããå«ãå¥åã®
..._unittest.rs
ãã¡ã€ã«ãè¿œå ããŸãã - æ°ãããã¹ãã
BUILD.gn
ã«è¿œå ããŸãã - ãã¹ãããã«ãããŠå®è¡ããæ°ãããã¹ããæ©èœããããšã確èªããŸãã
C++ãšã®çžäºéçšæ§
Rust ã³ãã¥ããã£ã«ã¯ C++ ãš Rust ã®çžäºéçšã®ããã®ãªãã·ã§ã³ãè€æ°çšæãããŠããã絶ããæ°ããããŒã«ãéçºãããŠããŸããçŸåšã®ãšãããChromium ã§ã¯ CXX ãšããããŒã«ã䜿çšããŠããŸãã
èšèªå¢çå šäœãã€ã³ã¿ãŒãã§ãŒã¹å®çŸ©èšèªïŒRust ã«ãã䌌ãŠããŸãïŒã§èšè¿°ãããšãCXX ããŒã«ã Rust ãš C++ ã®äž¡æ¹ã§é¢æ°ãšåã®å®£èšãçæããŸãã
CXX ã®è©³çŽ°ãªäœ¿çšäŸã«ã€ããŠã¯ãCXX ãã¥ãŒããªã¢ã« ãã芧ãã ããã
å³ãèŠãªãã話ããŸããããè£ã§è¡ãããåŠçã¯ä»¥åãšãŸã£ããåãã§ããããã®ããã»ã¹ãèªååãããšæ¬¡ã®ãããªã¡ãªãããããããšã説æããŸãã
- ãã®ããŒã«ã¯ãC++ åŽãš Rust åŽãäžèŽããããšãä¿èšŒããŸãïŒããšãã°ã
#[cxx::bridge]
ãå®éã® C++ ãŸã㯠Rust ã®å®çŸ©ãšäžèŽããªãå Žåãã³ã³ãã€ã« ãšã©ãŒãçºçããŸãããåæãããŠããªãæåãã€ã³ãã£ã³ã°ã䜿çšãããšãæªå®çŸ©ã®åäœãçºçããŸãïŒã - ãã®ããŒã«ã¯ãC 以å€ã®æ©èœã«å¯Ÿãã FFI ãµã³ã¯ïŒå°ã㪠C-ABI äºæã®ããªãŒé¢æ°ïŒã®çæãèªååããŸãïŒRust ãŸã㯠C++ ã¡ãœãããžã® FFI åŒã³åºãã®æå¹åãªã©ãæåãã€ã³ãã£ã³ã°ã§ã¯ããã®ãããªãããã¬ãã«ã®ããªãŒé¢æ°ãæåã§äœæããå¿ èŠããããŸãïŒã
- ããŒã«ãšã©ã€ãã©ãªã¯ã次ã®ãããªäžé£ã®äž»èŠãªåãåŠçã§ããŸãã
&[T]
ã¯ãç¹å®ã® ABI ãã¡ã¢ãª ã¬ã€ã¢ãŠããä¿èšŒãããã®ã§ã¯ãããŸããããFFI ã®å¢çãè¶ ããŠæž¡ãããšãã§ããŸããæåãã€ã³ãã£ã³ã°ã§ã¯ãstd::span<T>
/&[T]
ãæåã§åé¢ãããã€ã³ã¿ãšé·ãããåæ§ç¯ããå¿ èŠããããŸããèšèªããšã«ç©ºã®ã¹ã©ã€ã¹ã®è¡šçŸæ¹æ³ãè¥å¹²ç°ãªãããããšã©ãŒãçºçãããããªããŸããstd::unique_ptr<T>
ãstd::shared_ptr<T>
ãBox
ãªã©ã®ã¹ããŒã ãã€ã³ã¿ã¯ããã€ãã£ãã«ãµããŒããããŠããŸããæåãã€ã³ãã£ã³ã°ã§ã¯ãC-ABI äºæã®æªå å·¥ãã€ã³ã¿ãæž¡ãå¿ èŠããããããã©ã€ãã¿ã€ã ãšã¡ã¢ãªå®å šæ§ã«é¢ãããªã¹ã¯ãé«ãŸããŸããrust::String
åãšCxxString
åã¯ãèšèªéã®æååè¡šçŸã®éããç解ããç¶æããŸãïŒããšãã°ãrust::String::lossy
ã¯ãé UTF8 ã®å ¥åãã Rust æååãäœæã§ããŸãããŸããrust::String::c_str
ã¯æååã NUL çµç«¯ã§ããŸãïŒã
ãã€ã³ãã£ã³ã°ã®äŸ
CXX ã§ã¯ãC++ ãš Rust ã®å¢çå
šäœã .rs
ãœãŒã¹ã³ãŒãå
ã® cxx::bridge
ã¢ãžã¥ãŒã«ã§å®£èšããå¿
èŠããããŸãã
#[cxx::bridge]
mod ffi {
extern "Rust" {
type MultiBuf;
fn next_chunk(buf: &mut MultiBuf) -> &[u8];
}
unsafe extern "C++" {
include!("example/include/blobstore.h");
type BlobstoreClient;
fn new_blobstore_client() -> UniquePtr<BlobstoreClient>;
fn put(self: &BlobstoreClient, buf: &mut MultiBuf) -> Result<u64>;
}
}
// Rust ã®åãšé¢æ°ã®å®çŸ©ãããã«èšè¿°ããŸãã
以äžã説æããŸãã
- ããã¯éåžžã® Rust
mod
ã®ããã«èŠããŸããã#[cxx::bridge]
ããã·ãŒãžã£ã« ãã¯ãã¯ããã«å¯ŸããŠè€éãªåŠçãè¡ããŸããçæãããã³ãŒãã¯ãã£ãšæŽç·ŽãããŠããŸãããããã§ãã³ãŒãã«ã¯ffi
ãšããmod
ãäœæãããŸãã - Rust ã§ã® C++ ã®
std::unique_ptr
ã®ãã€ãã£ã ãµããŒã - Native support for Rust slices in C++
- C++ ãã Rust ããã³ Rust ã®åãžã®åŒã³åºãïŒäžéšïŒ
- Rust ãã C++ ããã³ C++ ã®åãžã®åŒã³åºãïŒäžéšïŒ
ãããã誀解: Rust 㧠C++ ããããŒã解æãããŠããããã«èŠããŸãããããã¯èª€è§£ã§ãããã®ããããŒã¯ Rust ã§ã¯è§£éããããC++ ã³ã³ãã€ã©ã®ããã«çæããã C++ ã³ãŒãã« #include
ãããŠããã ãã§ãã
CXXã®éç
CXX ã䜿çšãããšãã«æã圹ç«ã€ããŒãžã¯ãåãªãã¡ã¬ã³ã¹ ã§ãã
CXX ã¯åºæ¬çã«ã次ã®ãããªã±ãŒã¹ã«é©ããŠããŸãã
- Rust-C++ ã€ã³ã¿ãŒãã§ãŒã¹ãååã«ã·ã³ãã«ã§ããã¹ãŠã宣èšã§ããå Žåã
- ãã§ã« CXX ã§ãã€ãã£ãã«ãµããŒããããŠããåã®ã¿ã䜿çšããŠããå ŽåïŒäŸ:
std::unique_ptr
ãstd::string
ã&[u8]
ïŒã
Rust ã® Option
åããµããŒããããŠããªããªã©ãCXX ã«ã¯å€ãã®å¶éããããŸãã
ããããå¶éã«ãããChromium ã§ã¯ ä»»æã® Rust ãš C++ ã®çžäºéçšã¯è¡ããããRustã®äœ¿çšã¯ååã«ç¬ç«ããã³ãŒãã«éå®ãããŠããŸããChromium ã§ã® Rust ã®ãŠãŒã¹ã±ãŒã¹ãæ€èšããéã¯ããŸããèšèªå¢çã® CXX ãã€ã³ãã£ã³ã°ã®äžæžããäœæããŠãã·ã³ãã«ã«èŠãããã©ããã確èªããããšãããããããŸãã
ãŸããCXX ã®ãã®ä»ã®åä»ãªç¹ã説æããå¿ èŠããããŸãã次ã«äŸã瀺ããŸãã
- ãšã©ãŒåŠçã C++ äŸå€ã«åºã¥ããŠè¡ãããïŒæ¬¡ã®ã¹ã©ã€ããåç §ïŒã
- é¢æ°ãã€ã³ã¿ã䜿ãã«ããã
CXXã«ããããšã©ãŒåŠç
CXX ã® Result<T,E>
ã®ãµããŒã ã¯ãC++ äŸå€ã«äŸåããŠãããããChromium ã§ã¯äœ¿çšã§ããŸããã以äžã®ä»£æ¿æ段ããããŸãã
-
Result<T, E>
ã®T
ã®éšå:- out ãã©ã¡ãŒã¿ãä»ããŠè¿ãããšãã§ããŸãïŒäŸ:
&mut T
ïŒããã®ããã«ã¯ãT
ã FFI ã®å¢çãè¶ããŠæž¡ããå¿ èŠããããŸããããšãã°ãT
ã«ã¯ä»¥äžãæå®ããå¿ èŠããããŸãã- ããªããã£ãåïŒ
u32
ãusize
ãªã©ïŒ - (
Box<T>
ãšã¯ç°ãªã)é©åãªããã©ã«ãå€ãæã€cxx
ã§ãã€ãã£ãã«ãµããŒããããŠããåïŒUniquePtr<T>
ãªã©ïŒã
- ããªããã£ãåïŒ
- Rust åŽã§ä¿æããåç
§ãä»ããŠå
¬éã§ããŸããããã¯ã
T
ã Rust åã®å Žåã«å¿ èŠã«ãªãããšããããŸããRust å㯠FFI ã®å¢çãè¶ ããŠæž¡ãããšãã§ãããUniquePtr<T>
ã«æ ŒçŽããããšãã§ããŸããã
- out ãã©ã¡ãŒã¿ãä»ããŠè¿ãããšãã§ããŸãïŒäŸ:
-
Result<T, E>
ã®E
ã®éšå:- ããŒã«å€ãšããŠè¿ãããšãã§ããŸãïŒããšãã°ã
true
ã¯æåãfalse
ã¯å€±æãè¡šããŸãïŒã - çè«äžã¯ãšã©ãŒã®è©³çŽ°ãä¿æã§ããŸããããããŸã§ã¯å®éã«å¿ èŠã«ãªãããšã¯ãããŸããã§ããã
- ããŒã«å€ãšããŠè¿ãããšãã§ããŸãïŒããšãã°ã
CXX Error Handling: QR Example
QR ã³ãŒãçæããŒã«ã¯ãããŒã«å€ãæåãŸãã¯å€±æãäŒéããæåã®çµæã FFI ã®å¢çãè¶ ããŠåãæž¡ãããšãã§ãã äžäŸ ã§ãã
#[cxx::bridge(namespace = "qr_code_generator")]
mod ffi {
extern "Rust" {
fn generate_qr_code_using_rust(
data: &[u8],
min_version: i16,
out_pixels: Pin<&mut CxxVector<u8>>,
out_qr_size: &mut usize,
) -> bool;
}
}
åè¬è
㯠out_qr_size
åºåã®ã»ãã³ãã£ã¯ã¹ã«é¢å¿ãæã£ãŠããå¯èœæ§ããããŸããããã¯ãã¯ã¿ãŒã®ãµã€ãºã§ã¯ãªããQR ã³ãŒãã®ãµã€ãºã§ãïŒã€ãŸãããã®æ
å ±ã¯åé·ã§ããããã¯ã¿ãŒã®ãµã€ãºã®å¹³æ¹æ ¹ã«çžåœããŸãïŒã
Rust é¢æ°ãåŒã³åºãåã« out_qr_size
ãåæåããããšã®éèŠæ§ã説æããŸããããåæåãããŠããªãã¡ã¢ãªãæã Rust åç
§ãäœæãããšãæªå®çŸ©ã®åäœãšãªããŸãïŒãã®ãããªã¡ã¢ãªãéåç
§ããæäœã®ã¿ã UB ã«ãªãC++ ãšã¯ç°ãªããŸãïŒã
Pin
ã«ã€ããŠåè¬è
ããå°ããããå Žåã¯ãCXX ã C++ ããŒã¿ãžã®å¯å€åç
§ã®ããã« Pin
ãå¿
èŠãšããçç±ã説æããŸããã€ãŸããC++ ã®ããŒã¿ã«ã¯èªå·±åç
§ãã€ã³ã¿ãå«ãŸããŠããå¯èœæ§ããããããRust ã®ããŒã¿ã®ããã«ç§»åããããšãã§ããŸããã
CXX Error Handling: PNG Example
PNG ãã³ãŒãã®ãããã¿ã€ãã¯ãæåããçµæã FFI ã®å¢çãè¶ããŠæž¡ããªãå Žåã«äœãã§ãããã瀺ããŠããŸãã
#[cxx::bridge(namespace = "gfx::rust_bindings")]
mod ffi {
extern "Rust" {
/// ãã㯠`Result<PngReader<'a>,()>` ãšåçã® FFI 察å¿ã®çµæã
/// è¿ããŸãã
fn new_png_reader<'a>(input: &'a [u8]) -> Box<ResultOfPngReader<'a>>;
/// `crate::png::ResultOfPngReader` åã® C++ ãã€ã³ãã£ã³ã°
type ResultOfPngReader<'a>;
fn is_err(self: &ResultOfPngReader) -> bool;
fn unwrap_as_mut<'a, 'b>(
self: &'b mut ResultOfPngReader<'a>,
) -> &'b mut PngReader<'a>;
/// `crate::png::PngReader` åã® C++ ãã€ã³ãã£ã³ã°
type PngReader<'a>;
fn height(self: &PngReader) -> u32;
fn width(self: &PngReader) -> u32;
fn read_rgba8(self: &mut PngReader, output: &mut [u8]) -> bool;
}
}
PngReader
ãš ResultOfPngReader
㯠Rust åã§ãããããã®åã®ãªããžã§ã¯ãã¯ãBox<T>
ãä»ããã« FFI å¢çãè¶ããããšã¯ã§ããŸãããCXX ã§ã¯ Rust ãªããžã§ã¯ããå€ã§æ ŒçŽã§ããªããããout_parameter: &mut PngReader
ãšæžãããšã¯ã§ããŸããã
ãã®äŸã¯ãCXX ãä»»æã®ãžã§ããªã¯ã¹ããã³ãã¬ãŒãããµããŒãããŠããªããŠããæåã§éãžã§ããªãã¯åã«ç¹å / åçžåããããšã§ãFFI å¢çãè¶ããŠæž¡ããããšã瀺ããŠããŸãããã®äŸã§ã¯ãResultOfPngReader
ã¯Result<T, E>
ã®é©åãªã¡ãœããïŒis_err
ãunwrap
ãas_mut
ãªã©ïŒã«æž¡ãããéãžã§ããªãã¯åã§ãã
Chromium 㧠cxx ã䜿çšãã
Chromium ã§ã¯ãRust ã䜿çšãããªãŒãããŒãããšã«ç¬ç«ãã #[cxx::bridge] mod
ãå®çŸ©ããŸããéåžžã¯ãrust_static_library
ããšã« 1 ã€ãã€ã«ãªããŸãã
cxx_bindings = [ "my_rust_file.rs" ]
# ãã¹ãŠã®ãœãŒã¹ãã¡ã€ã«ã§ã¯ãªãã#[cxx::bridge] ãå«ããã¡ã€ã«ã®ãªã¹ã
allow_unsafe = true
äžèšã®ã³ãŒãããcrate_root
ã sources
ãšäžŠãã§ãæ¢åã® rust_static_library
ã¿ãŒã²ããã«è¿œå ããã ãã§ãã
C++ ããããŒã¯é©åãªå Žæã§çæãããããã次ã®ããã«ã€ã³ã¯ã«ãŒãã§ããŸãã
#include "ui/base/my_rust_file.rs.h"
//base
ã«ã¯ãChromium C++ åãã CXX Rust åïŒããã³ãã®éæ¹åïŒãžã®å€æãè¡ãããã®ãŠãŒãã£ãªãã£é¢æ°ãããã€ããããŸãïŒäŸ: SpanToRustSlice
ïŒã
åè¬è
ãããallow_unsafe = true
ããªãããã§ãå¿
èŠãªã®ããå°ããããå¯èœæ§ããããŸãã
倧ãŸãã«çãããšãC/C++ ã³ãŒãã¯éåžžã® Rust æšæºã§ã¯ãå®å
šãã§ã¯ãããŸãããRust ãã C/C++ ã«è¡ã£ããæ¥ãããããšãã¡ã¢ãªã«å¯ŸããŠä»»æã®åŠçãè¡ãããRust ç¬èªã®ããŒã¿ ã¬ã€ã¢ãŠãã®å®å
šæ§ãæãªãããå¯èœæ§ããããŸããC/C++ ã®çžäºéçšã§ unsafe
ããŒã¯ãŒããå€ããããšãunsafe
ã«å¯Ÿãã泚ç®åºŠãèããã®ã§ãããã«ã¯ è³åŠäž¡è«ããããŸãããã ãå³å¯ã«ã¯ãå€éšã³ãŒãã Rust ãã€ããªã«åã蟌ããšãRust ã®èŠ³ç¹ããã¯æ³å®ããŠããªãåäœãçºçããå¯èœæ§ããããŸãã
å
·äœçãªçãã¯ããã®ããŒãž ã®äžéšã®å³ã«ãããŸããè£ã§ã¯ãCXX 㯠Rust ã® unsafe
é¢æ°ãš extern "C"
é¢æ°ãçæããŸããããã¯åã®ã»ã¯ã·ã§ã³ã§æåã§è¡ã£ãã®ãšãŸã£ããåãã§ãã
Exercise: Interoperability with C++
ããŒã 1
- å
ã»ã©äœæãã Rust ãã¡ã€ã«ã«ãC++ ããåŒã³åºãåäžã®é¢æ°ã瀺ã
#[cxx::bridge]
ãè¿œå ããŸããããã¯hello_from_rust
ãšããé¢æ°ã§ããã©ã¡ãŒã¿ãåãåãããå€ãè¿ããŸããã - Modify your previous
hello_from_rust
function to removeextern "C"
and#[unsafe(no_mangle)]
. This is now just a standard Rust function. gn
ã¿ãŒã²ãããå€æŽããŠããããã®ãã€ã³ãã£ã³ã°ããã«ãããŸãã- C++ ã³ãŒãã§ã
hello_from_rust
ã®åæ¹å®£èšãåé€ãã代ããã«çæãããããã㌠ãã¡ã€ã«ãã€ã³ã¯ã«ãŒãããŸãã - ãã«ãããŠå®è¡ããŸãã
ããŒã 2
CXX ãå°ã䜿ã£ãŠã¿ãŠãChromium ã«ããã Rust ã®æè»æ§ã«ã€ããŠç解ãæ·±ããŸãããã
以äžãè©ŠããŠãã ããã
- Rust ãã C++ ãåŒã³åºããŸããããã«ã¯ä»¥äžãå¿
èŠã§ãã
cxx::bridge
ããinclude!
ã§ããè¿œå ã®ããã㌠ãã¡ã€ã«ããã®æ°ããããã㌠ãã¡ã€ã«ã§ C++ é¢æ°ã宣èšããå¿ èŠããããŸãã- ãã®ãããªé¢æ°ãåŒã³åºã
unsafe
ãããã¯ããŸã㯠ãã¡ã ã«èšèŒãããŠãããšããã#[cxx::bridge]
å ã§unsafe
ããŒã¯ãŒããæå®ããå¿ èŠããããŸãã #include "third_party/rust/cxx/v1/crate/include/cxx.h"
ãå¿ èŠã«ãªããããããŸããã
- C++ ãã Rust ã« C++ æååãæž¡ããŸãã
- C++ ãªããžã§ã¯ããžã®åç §ã Rust ã«æž¡ããŸãã
- æå³çã«
#[cxx::bridge]
ãšäžèŽããªãããã«Rust é¢æ°ã®ã·ã°ããã£ãå€æŽãã衚瀺ããããšã©ãŒã«æ £ããããã«ããŸãã - æå³çã«
#[cxx::bridge]
ãšäžèŽããªãããã«C++ é¢æ°ã®ã·ã°ããã£ãå€æŽãã衚瀺ããããšã©ãŒã«æ £ããããã«ããŸãã - ãªãããã®åã®
std::unique_ptr
ã C++ ãã Rust ã«æž¡ããŠãRust ãããã€ãã® C++ ãªããžã§ã¯ããææã§ããããã«ããŸãã - Rust ãªããžã§ã¯ããäœæã㊠C++ ã«æž¡ããŠãC++ ããããææã§ããããã«ããŸãïŒãã³ã:
Box
ãå¿ èŠã§ãïŒã - C++ åã§ããã€ãã®ã¡ãœããã宣èšããRust ããåŒã³åºããŸãã
- Rust åã«å¯ŸããŠããã€ãã®ã¡ãœããã宣èšããC++ ããåŒã³åºããŸãã
ããŒã 3
CXX ã®çžäºéçšæ§ã®é·æãšå¶éäºé ã«ã€ããŠç解ãããšããã§ãã€ã³ã¿ãŒãã§ãŒã¹ãéåžžã«ã·ã³ãã«ãªãChromium ã§ã® Rust ã®ãŠãŒã¹ã±ãŒã¹ãããã€ãèããŠã¿ãŸãããããã®ã€ã³ã¿ãŒãã§ãŒã¹ãã©ã®ããã«å®çŸ©ããã°ãããèããŠã¿ãŸãããã
åèæ å ±
次ã®ãããªè³ªåãå¯ããããå¯èœæ§ããããŸãã
- X ãš Y ã®äž¡æ¹ãé¢æ°åã§ããå Žåã«ãå X ã®å€æ°ãå Y ã§åæåãããšåé¡ãçºçããŸããããã¯ãC++ é¢æ°ã
cxx::bridge
å ã®å®£èšãšå®å šã«äžèŽããªãããã§ãã - C++ åç §ã Rust åç §ã«èªç±ã«å€æã§ããããã§ãããUB ã®ãªã¹ã¯ã¯ãªãã§ããããïŒCXX ã®äžéæåã®å Žåããµã€ãºããŒãã§ããããããã®ãªã¹ã¯ã¯ãããŸãããCXX ã®ããªãã¢ã«åã§ã¯ UB ãçºçããå¯èœæ§ããããŸãããCXX ã®èšèšäžããã®ãããªäŸãäœæããã®ã¯éåžžã«å°é£ã§ãã
ãµãŒãããŒãã£ã®ã¯ã¬ãŒããè¿œå ãã
Rust ã©ã€ãã©ãªã¯ãã¯ã¬ãŒãããšåŒã°ããcrates.io ã«ãããŸããRust ã¯ã¬ãŒããäºãã«äŸåãããã®ã¯éåžžã«ç°¡åã§ãããå®éã«ãã®ããã«ãªã£ãŠããŸã
ãããã㣠| C++ library | Rust crate |
---|---|---|
Build system | å€æ° | äžè²«ã㊠Cargo.toml |
äžè¬çãªã©ã€ãã©ãª ãµã€ãº | ãã倧 | å° |
æšç§»çäŸåé¢ä¿ | å° | å€æ° |
Chromium ã®ãšã³ãžãã¢ã«ãšã£ãŠãã¯ã¬ãŒãã«ã¯é·æãšçæããããŸãã
- ãã¹ãŠã®ã¯ã¬ãŒããå ±éã®ãã«ãã·ã¹ãã ã䜿çšããŠãããããChromium ãžã®åã蟌ã¿ãèªååã§ããŸãã
- ããããã¯ã¬ãŒãã«ã¯éåžžãæšç§»çäŸåé¢ä¿ããããããè€æ°ã®ã©ã€ãã©ãªãåã蟌ãããšãå¿ èŠã«ãªãå¯èœæ§ããããŸãã
è°è«ããå 容ã¯æ¬¡ã®ãšããã§ãã
- Chromium ãœãŒã¹ã³ãŒã ããªãŒã«ã¯ã¬ãŒããé 眮ããæ¹æ³
- ã¯ã¬ãŒãçšã®
gn
ãã«ãã«ãŒã«ãäœæããæ¹æ³ - ã¯ã¬ãŒãã®ãœãŒã¹ã³ãŒãã®ååãªå®å šæ§ãç£æ»ããæ¹æ³ã
Cargo.toml
ãã¡ã€ã«ã«ããã¯ã¬ãŒããè¿œå ããæ¹æ³
Chromium ã«ã¯ãäžå
管çãããçŽæ¥çãªã¯ã¬ãŒãäŸåé¢ä¿ã 1 ã»ãããããŸãããããã¯åäžã® Cargo.toml
ã§ç®¡çãããŸãã
[dependencies]
bitflags = "1"
cfg-if = "1"
cxx = "1"
# lots more...
ä»ã® Cargo.toml
ãšåæ§ã«ãäŸåé¢ä¿ã®è©³çŽ° ãæå®ã§ããŸããéåžžã¯ãã¯ã¬ãŒãã§æå¹ã«ãã features
ãæå®ããŸãã
Chromium ã«ã¯ã¬ãŒããè¿œå ããéã¯ãå€ãã®å Žåã gnrt_config.toml
ãšããè¿œå ãã¡ã€ã«ã«æ
å ±ãæå®ããå¿
èŠããããŸããããã«ã€ããŠã¯åŸã§èª¬æããŸãã
gnrt_config.toml
ãæ§æãã
Cargo.toml
ã®ã»ãã«ãgnrt_config.toml
ããããŸããããã«ã¯ãã¯ã¬ãŒããæ±ãããã® Chromium åºæã®æ¡åŒµæ©èœãå«ãŸããŠããŸãã
æ°ããã¯ã¬ãŒããè¿œå ããå Žåã¯ãå°ãªããšã次ã®ããããã® group
ãæå®ããå¿
èŠããããŸãã
# 'safe': The library satisfies the rule-of-2 and can be used in any process.
# 'sandbox': The library does not satisfy the rule-of-2 and must be used in
# a sandboxed process such as the renderer or a utility process.
# 'test': The library is only used in tests.
次ã«äŸã瀺ããŸãã
[crate.my-new-crate]
group = 'test' # only used in test code
ã¯ã¬ãŒãã®ãœãŒã¹ã³ãŒãã®ã¬ã€ã¢ãŠãã«ãã£ãŠã¯ããã®ãã¡ã€ã«ã䜿çšã㊠LICENSE
ãã¡ã€ã«ãèŠã€ããå Žæãæå®ããå¿
èŠããããŸãã
åŸã»ã©ãããã€ãã®åé¡ã解決ããããã«ãã®ãã¡ã€ã«ã«æå®ããå¿ èŠãããèšå®ã«ã€ããŠåãæ±ããŸãã
ã¯ã¬ãŒããããŠã³ããŒããã
gnrt
ãšããããŒã«ã¯ãã¯ã¬ãŒãã®ããŠã³ããŒãæ¹æ³ãš BUILD.gn
ã«ãŒã«ã®çææ¹æ³ãææ¡ããŠããŸãã
ãŸããå¿ èŠãªã¯ã¬ãŒãã次ã®ããã«ããŠã³ããŒãããŸãã
cd chromium/src
vpython3 tools/crates/run_gnrt.py -- vendor
gnrt
ããŒã«ã¯ Chromium ã®ãœãŒã¹ã³ãŒãã®äžéšã§ããããã®ã³ãã³ããå®è¡ãããšãcrates.io
ããäŸåé¢ä¿ãããŠã³ããŒãããŠå®è¡ããŸããããã«é¢ããã»ãã¥ãªãã£äžã®æ±ºå®ã«ã€ããŠã¯ãåã®ã»ã¯ã·ã§ã³ ãã芧ãã ããã
ãã® vendor
ã³ãã³ãã«ããã以äžãããŠã³ããŒããããå ŽåããããŸãã
- Your crate
- çŽæ¥çããã³æšç§»çäŸåé¢ä¿
cargo
ã«ãã£ãŠæ瀺ããããChromium ã§å¿ èŠãšãªãã¯ã¬ãŒãã®å®å šã»ãããåŸãããã®ä»ã®ã¯ã¬ãŒãã®æ°ããããŒãžã§ã³ã
Chromium ã§ã¯ãäžéšã®ã¯ã¬ãŒãã«å¯Ÿãããããã //third_party/rust/chromium_crates_io/patches
ã«ä¿åãããŠããŸãããããã¯èªåçã«åé©çšãããŸããããããé©çšã倱æããå Žåã¯ãæåã«ãã解決ãå¿
èŠã«ãªãå ŽåããããŸãã
gn
ãã«ãã«ãŒã«ãçæãã
ã¯ã¬ãŒããããŠã³ããŒããããã以äžã®ããã« BUILD.gn
ãã¡ã€ã«ãäœæããŸãã
vpython3 tools/crates/run_gnrt.py -- gen
git status
ãå®è¡ãã以äžã確èªããŸãã
third_party/rust/chromium_crates_io/vendor
ã« 1 ã€ä»¥äžã®æ°ããã¯ã¬ãŒã ãœãŒã¹ã³ãŒããããããšthird_party/rust/<crate name>/v<major semver version>
ã« 1 ã€ä»¥äžã®æ°ããBUILD.gn
ãããããš- é©åãª
README.chromium
ãããããš
The "major semver version" is a Rust "semver" version number.
ç¹ã« third_party/rust
以äžã«çæããããã®ããã確èªããŠãã ããã
semver ã«ã€ããŠãç¹ã« Chromium ã§ã¯äºææ§ã®ãªãã¯ã¬ãŒãã®ããŒãžã§ã³ãè€æ°èš±å¯ãããããšã説æããŠãããŸããããããã¯æšå¥šãããŸããããCargo ãšã³ã·ã¹ãã ã§å¿ èŠã«ãªãããšããããŸãã
åé¡ã解決ãã
ãã«ãã倱æããå Žåãbuild.rs
ïŒãã«ãæã«ä»»æã®åŠçãè¡ãããã°ã©ã ïŒãåå ã§ããå¯èœæ§ããããŸããããã¯ããã«ãã®äžŠåæ§ãšåçŸæ§ãæ倧åããããã«éçã§æ±ºå®çãªãã«ãã«ãŒã«ãç®æã gn
ãš ninja
ã®èšèšãšã¯ãåºæ¬çã«ççŸããŠããŸãã
äžéšã® build.rs
ã¢ã¯ã·ã§ã³ã¯èªåçã«ãµããŒããããŸãããä»ã®ã¢ã¯ã·ã§ã³ã«ã¯å¯Ÿå¿ãå¿
èŠã§ãã
ãã«ã ã¹ã¯ãªããã®å¹æ | gn ãã³ãã¬ãŒãã«ãããµããŒã | å¿ èŠãªäœæ¥ |
---|---|---|
rustc ã®ããŒãžã§ã³ã確èªããŠæ©èœãæå¹ãŸãã¯ç¡å¹ã«ãã | ã¯ã | ãªã |
ãã©ãããã©ãŒã ãŸã㯠CPU ã確èªããŠæ©èœãæå¹ãŸãã¯ç¡å¹ã«ãã | ã¯ã | ãªã |
Generating code | ã¯ã | ãã - gnrt_config.toml ã§æå®ãã |
C/C++ ã®ãã«ã | ããã | ããããé©çšãã |
ãã®ä»ã®ä»»æã®ã¢ã¯ã·ã§ã³ | ããã | ããããé©çšãã |
幞ããã»ãšãã©ã®ã¯ã¬ãŒãã«ã¯ãã«ã ã¹ã¯ãªãããå«ãŸããŠããããã»ãšãã©ã®ãã«ã ã¹ã¯ãªããã¯äžäœ 2 ã€ã®ã¢ã¯ã·ã§ã³ã®ã¿ãå®è¡ããŸãã
ã³ãŒããçæãããã«ãã¹ã¯ãªãã
ninja
ããã¡ã€ã«ãèŠã€ããããªããšããã¡ãã»ãŒãžã衚瀺ããå Žåã¯ãbuild.rs
ããœãŒã¹ã³ãŒã ãã¡ã€ã«ãäœæããŠãããã©ããã確èªããŸãã
ãããã¡ã€ã«ãäœæãããããã«ãªã£ãŠããããgnrt_config.toml
ãå€æŽããŠãã¯ã¬ãŒãã« build-script-outputs
ãè¿œå ããŸãããããæšç§»çäŸåé¢ä¿ïŒChromium ã³ãŒããçŽæ¥äŸåãã¹ãã§ãªãäŸåé¢ä¿ïŒã®å Žåã¯ãallow-first-party-usage=false
ãè¿œå ããŸãããã®ãã¡ã€ã«ã«ã¯ããã§ã«ããã€ãã®äŸãå«ãŸããŠããŸãã
[crate.unicode-linebreak]
allow-first-party-usage = false
build-script-outputs = ["tables.rs"]
次ã«ãgnrt.py -- gen
ãåå®è¡ã㊠BUILD.gn
ãã¡ã€ã«ãåçæãããã®ç¹å®ã®åºåãã¡ã€ã«ãåŸç¶ã®ãã«ãã¹ãããã§å
¥åãããããšã ninja ã«æããŸãã
C++ããã«ãããããããã¯ãä»»æã®ã¢ã¯ã·ã§ã³ãå®è¡ãããã«ãã¹ã¯ãªãã
äžéšã®ã¯ã¬ãŒãã¯ãcc
ã¯ã¬ãŒãã䜿çšããŠãC / C++ ã©ã€ãã©ãªã®ãã«ããšãªã³ã¯ãè¡ããŸããä»ã®ã¯ã¬ãŒãã¯ããã«ã ã¹ã¯ãªããå
㧠bindgen
ã䜿çšã㊠C / C++ ã解æããŸãããããã®ã¢ã¯ã·ã§ã³ã¯ãChromium ã®ã³ã³ããã¹ãã§ã¯ãµããŒãã§ããŸãããChromiumã® gnãninjaãLLVM ãã«ãã·ã¹ãã ã¯ããã«ã ã¢ã¯ã·ã§ã³éã®é¢ä¿ãéåžžã«å
·äœçã«è¡šçŸããããã§ãã
ãããã£ãŠã次ã®ãããªãªãã·ã§ã³ããããŸãã
- ãããã®ã¯ã¬ãŒãã䜿çšããªã
- ã¯ã¬ãŒãã«ããããé©çšãã
ããã㯠third_party/rust/chromium_crates_io/patches/<crate>
ã«ä¿åããå¿
èŠããããŸããããšãã°ãcxx
ã¯ã¬ãŒãã«å¯Ÿããããã ãã芧ãã ããããŸãããããã¯ã¯ã¬ãŒããã¢ããã°ã¬ãŒãããããã³ã« gnrt
ã«ãã£ãŠèªåçã«é©çšãããŸãã
ã¯ã¬ãŒããžã®äŸåãèšå®ãã
ãµãŒãããŒã㣠ã¯ã¬ãŒããè¿œå ããŠãã«ãã«ãŒã«ãçæããããã¯ã¬ãŒããžã®äŸåãç°¡åã«èšå®ã§ããŸããrust_static_library
ã¿ãŒã²ãããèŠã€ããŠãã¯ã¬ãŒãå
ã® :lib
ã¿ãŒã²ããã« dep
ãè¿œå ããŸãã
å ·äœçã«ã¯æ¬¡ã®ããã«ããŸãã
次ã«äŸã瀺ããŸãã
rust_static_library("my_rust_lib") {
crate_root = "lib.rs"
sources = [ "lib.rs" ]
deps = [ "//third_party/rust/example_rust_crate/v1:lib" ]
}
ãµãŒãããŒã㣠ã¯ã¬ãŒãã®ç£æ»
æ°ããã©ã€ãã©ãªãè¿œå ããå ŽåãChromium ã®æšæºã® ããªã·ãŒ ãé©çšãããŸãããåœç¶ãªããã»ãã¥ãªãã£å¯©æ»ã®å¯Ÿè±¡ã«ããªããŸãã1 ã€ã®ã¯ã¬ãŒãã ãã§ãªãæšç§»çäŸåé¢ä¿ãåã蟌ãå Žåã審æ»ãã¹ãã³ãŒããå€æ°ååšããããšããããŸãããã®äžæ¹ã§ãå®å šãª Rust ã³ãŒãã®åã蟌ã¿ã«é¢ããŠã¯ãæªãå¯äœçšã¯éå®çãšãªããŸããã¯ã¬ãŒãã®å¯©æ»ã¯ã©ã®ããã«è¡ãããã¹ãã§ããããã
Chromium ã¯ä»åŸ cargo vet ãäžå¿ãšããããã»ã¹ã«ç§»è¡ãããŠããäºå®ã§ããã
ãããŸã§ã®éãæ°ããã¯ã¬ãŒããè¿œå ããããã³ã«ã以äžã®ãã§ãã¯ãè¡ããŸãã
- åã¯ã¬ãŒãã䜿çšãããŠããçç±ãšãã¯ã¬ãŒãå士ã®é¢ä¿ãç解ããŸããåã¯ã¬ãŒãã®ãã«ãã·ã¹ãã ã«
build.rs
ãŸãã¯æç¶ãåãã¯ããå«ãŸããŠããå Žåã¯ããã®ç®çã調ã¹ãŸãããŸããChromium ã®éåžžã®ãã«ãæ¹æ³ãšäºææ§ããããã©ããã確èªããŸãã - åã¯ã¬ãŒããååã«ã¡ã³ããã³ã¹ãããŠããã確èªããŸãã
cd third-party/rust/chromium_crates_io; cargo audit
ã䜿çšããŠæ¢ç¥ã®è匱æ§ããã§ãã¯ããŸãïŒæåã«cargo install cargo-audit
ãå®è¡ããå¿ èŠããããŸãããç®èãªããšã«ãããã«ãã£ãŠã€ã³ã¿ãŒãããããå€ãã®äŸåé¢ä¿ãããŠã³ããŒãããããšã«ãªããŸã 2ïŒãunsafe
ãªã³ãŒãã Rule of Two ãæºãããŠããããšã確èªããŸããfs
ããã³net
ã®API ã䜿çšãããŠãããã©ããã確èªããŸãã- æªæãæã£ãŠäžæ£ã«æ¿å ¥ãããå¯èœæ§ã®ããéšåããªããæ¢ãã®ã«ååãªã¬ãã«ã§ã³ãŒããèªã¿ãŸãïŒå€ãã®å Žåãã³ãŒããå€ãããŠå®ç§ã«ãã§ãã¯ããããšã¯ã§ããŸããïŒã
ãããã¯ã¬ã€ãã©ã€ã³ã«ãããŸãããsecurity@chromium.org
ã®å¯©æ»æ
åœè
ãšååããŠãèªä¿¡ãæã£ãŠã¯ã¬ãŒãã䜿çšããããã®é©åãªæ¹æ³ãèŠã€ããŠãã ããã
ã¯ã¬ãŒãã Chromium ãœãŒã¹ã³ãŒãã«ãã§ãã¯ã€ã³ãã
git status
ãå®è¡ãããšã以äžã確èªã§ããŸãã
//third_party/rust/chromium_crates_io
ã«ããã¯ã¬ãŒãã³ãŒã//third_party/rust/<crate>/<version>
ã«ããã¡ã¿ããŒã¿ïŒBUILD.gn
ãšREADME.chromium
ïŒ
åŸè
ã®å Žæã« OWNERS
ãã¡ã€ã«ãè¿œå ããŠãã ããã
ããããã¹ãŠããCargo.toml
ããã³ gnrt_config.toml
ã®å€æŽãšãšãã« Chromium ãªããžããªã«è¿œå ããå¿
èŠããããŸãã
éèŠ: git add -f
ã䜿çšããå¿
èŠããããŸããããããªããšã.gitignore
ãã¡ã€ã«ã«ãã£ãŠäžéšã®ãã¡ã€ã«ãã¹ããããããå¯èœæ§ãããããã§ãã
ãã®éãã€ã³ã¯ã«ãŒã·ãã§ãªãè¡šçŸãåå 㧠presubmit ãã§ãã¯ã倱æããããšããããŸããããã¯ãRust ã®ã¯ã¬ãŒãããŒã¿ã«ã¯ Git ãã©ã³ãã®ååãå«ãŸããŠããåŸåããããå€ãã®ãããžã§ã¯ãã§äŸç¶ãšããŠã€ã³ã¯ã«ãŒã·ãã§ãªãè¡šçŸã䜿çšãããŠããããã§ãããã®ããã以äžãå®è¡ããå¿ èŠããããŸãã
infra/update_inclusive_language_presubmit_exempt_dirs.sh > infra/inclusive_language_presubmit_exempt_dirs.txt
git add -p infra/inclusive_language_presubmit_exempt_dirs.txt # add whatever changes are yours
ã¯ã¬ãŒããææ°ã®ç¶æ ã«ä¿ã€
ãµãŒãããŒãã£ã® Chromium äŸåé¢ä¿ã®ææè ã¯ãã»ãã¥ãªãã£ã«é¢ããä¿®æ£ãè¡ã£ãŠäŸåé¢ä¿ãææ°ã®ç¶æ ã«ä¿ã€ããšãæ±ããããŸããããã¯ãŸããªãèªååãããããšãæåŸ ãããŠããŸãããçŸç¶ã¯ä»ã®ãµãŒãããŒãã£ã®äŸåé¢ä¿ã®å Žåãšåæ§ã«ãããããããŒããã®è²¬ä»»ãè² ããŸãã
æŒç¿
Chromium ã« uwuify ãè¿œå ããã¯ã¬ãŒãã® ããã©ã«ãã®æ©èœ ãç¡å¹ã«ããŸããã¯ã¬ãŒã㯠Chromium ã®å ¬éæ¿ã§äœ¿çšãããŸãããä¿¡é Œã§ããªãå ¥åã®åŠçã«ã¯äœ¿çšãããªããšä»®å®ããŠãã ããã
ïŒæ¬¡ã®æŒç¿ã§ Chromium ã® uwuify ã䜿çšããŸãããããã§è¡ã£ãŠãæ§ããŸããããŸãã¯ãuwuify
ã䜿çšããæ°ãã rust_executable
ã¿ãŒã²ãã ãäœæããããšãã§ããŸãïŒã
åè¬è ã¯å€æ°ã®æšç§»çäŸåé¢ä¿ãããŠã³ããŒãããå¿ èŠããããŸãã
å¿ èŠãªã¯ã¬ãŒãã¯æ¬¡ã®ãšããã§ãã
instant
lock_api
parking_lot
parking_lot_core
redox_syscall
scopeguard
smallvec
uwuify
åè¬è ãäžèšä»¥å€ã®ã¯ã¬ãŒããããŠã³ããŒãããŠããå Žåã¯ãããã©ã«ãã®æ©èœãç¡å¹ã«ããã®ãå¿ããŠããå¯èœæ§ããããŸãã
ãã®ã¯ã¬ãŒãã«ååããŠããã Daniel Liu ã«æè¬ããŸãã
ãŸãšã --- æŒç¿
ãã®æŒç¿ã§ã¯ãChromium ã®æ°ããæ©èœãè¿œå ããªããããããŸã§åŠãã ããšããŸãšããŸãã
ãããã¯ã ãããžã¡ã³ãéšéããã®ããªãŒãã£ã³ã°
人éé¢ããç±åž¯éšæã«çæ¯ãããã¯ã·ãŒïŒåŠç²Ÿã®äžçš®ïŒã®æãçºèŠãããŸããããã¯ã·ãŒåãã® Chromium ãã§ããã ãæ©ãæäŸããããšãéèŠã§ãã
èŠä»¶ã¯ãChromium ã®ãã¹ãŠã® UI æååããã¯ã·ãŒã®èšèªã«ç¿»èš³ããããšã§ãã
æ£åŒãªç¿»èš³ãè¡ã£ãŠããæéã¯ãããŸãããã幞ãã«ããã¯ã·ãŒã®èšèªã¯è±èªã«éåžžã«è¿ãããã®ç¿»èš³ãè¡ã Rust ã¯ã¬ãŒããããããšãããããŸããã
å®ã¯ãåã®æŒç¿ã§ãã®ã¯ã¬ãŒããã€ã³ããŒãããŠããŸãã
ïŒèšããŸã§ããªããChrome ãå®éã«ç¿»èš³ããã«ã¯çŽ°å¿ã®æ³šæãšåªåãå¿ èŠã§ãã®ã§ãããã¯å ¬éããªãã§ãã ããïŒã
æé
衚瀺åã«ãã¹ãŠã®æååã翻蚳ããããã« ResourceBundle::MaybeMangleLocalizedString
ãå€æŽããŸããChromium ã®ãã®ç¹å¥ãªãã«ãã§ã¯ãmangle_localized_strings_
ã®èšå®ã«é¢ä¿ãªããåžžã«ãã®ããã«ããŸãã
ãããŸã§ã®æŒç¿ããã¹ãŠæ£ããçµãããããŠããã°ãããã§ãã¯ã·ãŒåãã® Chrome ãå®æããŠããã¯ãã§ãã
- UTF16 ãš UTF8 ã«ã€ããŠãåè¬è
㯠Rust æååãåžžã« UTF8 ã§ããããšã«æ³šæããå¿
èŠããããŸãããããããC++ åŽã§
base::UTF16ToUTF8
ã䜿çšããŠå€æãéå€æããæ¹ããããšå€æããã§ãããã - Rust åŽã§å€æãè¡ãå Žåã¯ã
String::from_utf16
ã®å©çšããšã©ãŒåŠçãå€ãã® u16s ã転éå¯èœãª CXX ã§ãµããŒããããŠããå ã¯ã©ãããæ€èšããå¿ èŠããããŸãã - åè¬è
ã¯ããã€ãã®ç°ãªãæ¹æ³ã§ C++ ãš Rust ã®å¢çãèšèšã§ããŸããããšãã°ãæååãå€ã§ååŸããŠè¿ãããŸãã¯æååãžã®å¯å€åç
§ãååŸãããªã©ã§ããå¯å€åç
§ã䜿çšãããŠããå Žåã¯ããããã CXX ã¯
Pin
ã䜿çšããå¿ èŠãããæšã®ã¡ãã»ãŒãžã衚瀺ããŸããPin
ã®æ©èœã説æããC++ ããŒã¿ãžã®å¯å€åç §ã®ããã« CXX ã§Pin
ãå¿ èŠã«ãªãçç±ã説æããå¿ èŠããããããããŸãããçãã¯ãC++ ããŒã¿ã«ã¯èªå·±åç §ãã€ã³ã¿ãå«ãŸããŠããå¯èœæ§ããããããRust ããŒã¿ã®ããã«ç§»åã§ããªãããã§ãã ResourceBundle::MaybeMangleLocalizedString
ãå«ã C++ ã¿ãŒã²ããã¯ãrust_static_library
ã¿ãŒã²ããã«äŸåããå¿ èŠããããŸããåè¬è ã¯ãã§ã«ãããè¡ã£ãŠããã¯ãã§ããrust_static_library
ã¿ãŒã²ããã¯//third_party/rust/uwuify/v0_2:lib
ã«äŸåããå¿ èŠããããŸãã
æŒç¿ã®è§£ç
Chromium ã®æŒç¿ã®è§£çã«ã€ããŠã¯ããã¡ãã® CL ã·ãªãŒãº ãã芧ãã ããã
ãã¢ã¡ã¿ã«Rustãžãããã
ãã¡ãã¯ãã¢ã¡ã¿ã«Rustã«é¢ããç¬ç«ããïŒæ¥ã³ãŒã¹ã§ãã察象ãšããŠããã®ã¯ãRustã®åºæ¬çãªéšåã«é¢ããŠã¯ç¿åŸæžã¿ãªäººã§ïŒäŸãã°ãæ¬è¬åº§ã§ïŒãCãªã©ã®ä»ã®èšèªã§ãã¢ã¡ã¿ã«éçºã®çµéšããããšçæ³çã§ãã
ä»æ¥ãåãæ±ãã®ã¯ããã¢ã¡ã¿ã«Rustã§ããããªãã¡ãOSãªãã§Rustã®ã³ãŒããå®è¡ããŸãããã®ç« ã¯ä»¥äžã®ãããªæ§æã«ãªããŸã:
no_std
Rustãšã¯?- ãã€ã¯ãã³ã³ãããŒã©åãã®ãã¡ãŒã ãŠã§ã¢éçºã
- ã¢ããªã±ãŒã·ã§ã³ããã»ããµåãã®ããŒãããŒãïŒã«ãŒãã«éçºã
- ãã¢ã¡ã¿ã«Rustéçºã«åœ¹ç«ã€ã¯ã¬ãŒãã®çŽ¹ä»ã
For the microcontroller part of the course we will use the BBC micro:bit v2 as an example. It's a development board based on the Nordic nRF52833 microcontroller with some LEDs and buttons, an I2C-connected accelerometer and compass, and an on-board SWD debugger.
ãŸãã¯ããã«ãåŸã»ã©å¿ èŠãšãªãããã€ãã®ããŒã«ãã€ã³ã¹ããŒã«ããŸããgLinuxãŸãã¯Debianã®å Žåã¯ä»¥äžã®ããã«ãªããŸã:
sudo apt install gdb-multiarch libudev-dev picocom pkg-config qemu-system-arm
rustup update
rustup target add aarch64-unknown-none thumbv7em-none-eabihf
rustup component add llvm-tools-preview
cargo install cargo-binutils
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.sh | sh
ããã«ãplugdev
ã°ã«ãŒãã«micro:bitããã°ã©ã çšããã€ã¹ãžã®ã¢ã¯ã»ã¹ãä»äžããŸã:
echo 'SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0d28", MODE="0660", GROUP="logindev", TAG+="uaccess"' |\
sudo tee /etc/udev/rules.d/50-microbit.rules
sudo udevadm control --reload-rules
MacOSã®å Žåã¯ä»¥äžã®ããã«ãªããŸã:
xcode-select --install
brew install gdb picocom qemu
rustup update
rustup target add aarch64-unknown-none thumbv7em-none-eabihf
rustup component add llvm-tools-preview
cargo install cargo-binutils
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.sh | sh
no_std
|
|
|
---|---|---|
|
|
|
HashMap
ã¯RNGã«äŸåããŸããstd
ã¯core
ãšalloc
ã®äž¡æ¹ãåãšã¯ã¹ããŒãããŸãã
æå°éã®no_std
ããã°ã©ã
#![no_main] #![no_std] use core::panic::PanicInfo; #[panic_handler] fn panic(_panic: &PanicInfo) -> ! { loop {} }
- ãã®ã³ãŒãã¯ç©ºã®ãã€ããªã«ã³ã³ãã€ã«ãããŸãã
- ãããã¯ãã³ãã©ã¯
std
ãæäŸããã®ã§ãããã䜿ããªãå Žåã¯èªåã§æäŸããå¿ èŠããããŸãã - ãããã¯ã
panic-halt
ã®ãããªå¥ã®ã¯ã¬ãŒããæäŸãããããã¯ãã³ãã©ãå©çšããããšãã§ããŸãã - ã¿ãŒã²ããã«ãã£ãŠã¯ã
eh_personality
ã«é¢ãããšã©ãŒãåé¿ããããã«panic = "abort"
ãæå®ããŠã³ã³ãã€ã«ããå¿ èŠããããŸãã - ãªãã
main
ã®ãããªããã°ã©ã ã®èŠå®ãšã³ããªãã€ã³ãã¯ãªãã®ã§ãèªåã§ãšã³ããªãã€ã³ããå®çŸ©ããå¿ èŠããããŸããéåžžãRustã³ãŒããå®è¡ã§ããããã«ããããã«ã¯ããªã³ã«ã¹ã¯ãªãããšããçšåºŠã®ã¢ã»ã³ããªã³ãŒããå¿ èŠãšããŸãã
alloc
alloc
ã䜿ãããã«ã¯ãã°ããŒãã«ïŒããŒãïŒã¢ãã±ãŒã¿ãå®è£
ããªããã°ãªããŸããã
#![no_main] #![no_std] extern crate alloc; extern crate panic_halt as _; use alloc::string::ToString; use alloc::vec::Vec; use buddy_system_allocator::LockedHeap; #[global_allocator] static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new(); static mut HEAP: [u8; 65536] = [0; 65536]; pub fn entry() { // SAFETY: `HEAP` is only used here and `entry` is only called once. unsafe { // ã¢ãã±ãŒã¿ãŒã«ã¡ã¢ãªãå²ãåœãŠãŸãã HEAP_ALLOCATOR.lock().init(HEAP.as_mut_ptr() as usize, HEAP.len()); } // ããã§ãããŒãå²ãåœãŠãå¿ èŠãšããåŠçãå®è¡ã§ããããã«ãªããŸããã let mut v = Vec::new(); v.push("A string".to_string()); }
buddy_system_allocator
ã¯ãµãŒãããŒãã£ã®ã¯ã¬ãŒãã§ãåçŽãªããã£ã·ã¹ãã ã¢ãã±ãŒã¿ã§ãããã®ä»ã«ãå©çšã§ããã¯ã¬ãŒãã¯ãããŸãããèªåã§å®è£ ããããå¥ã®ã¢ãã±ãŒã¿ã«èªåã®ã³ãŒããããã¯ããããšãå¯èœã§ãã- ãã©ã¡ãŒã¿å®æ°
LockedHeap
ã¯ã¢ãã±ãŒã¿ã®æ倧ãªãŒãã瀺ããŸãããã®å Žåã2**32ãã€ãã®é åã確ä¿ããããšãå¯èœã§ãã - ããäŸåé¢ä¿ã«ããã¯ã¬ãŒãã
alloc
ã«äŸåããå Žåãå¿ ããã€ããªãã¡ã€ã«ãããäžã€ã ãã®ã°ããŒãã«ãªã¢ãã±ãŒã¿ãååšããããã«ããªããã°ãªããŸãããéåžžãããã¯ãããã¬ãã«ã®ãã€ããªãçæããã¯ã¬ãŒãã«ããå¶åŸ¡ãããŸãã extern crate panic_halt as _
ãšããéšåã¯ãpanic_halt
ã¯ã¬ãŒãã確å®ã«ãªã³ã¯ãããããã¯ãã³ãã©ãå©çšå¯èœã«ããããã«å¿ èŠã§ãã- ãã®äŸã§ç€ºããã³ãŒãã¯ãã«ãã§ããŸããããšã³ããªãã€ã³ãããªãã®ã§å®è¡ããããšã¯ã§ããŸããã
ãã€ã¯ãã³ã³ãããŒã©
cortex_m_rt
ã¯ã¬ãŒãã¯Cortex Mãã€ã¯ãã³ã³ãããŒã©åãã®ãªã»ãããã³ãã©ïŒãšãã®ä»ããããïŒãæäŸããŸãã
#![no_main] #![no_std] extern crate panic_halt as _; mod interrupts; use cortex_m_rt::entry; #[entry] fn main() -> ! { loop {} }
次ã¯ãæœè±¡åºŠã®äœãã¬ãã«ããé ã«åšèŸºI/Oã«ã¢ã¯ã»ã¹ããæ¹æ³ã«ã€ããŠèŠãŠãããŸãã
- ãªã»ãããã³ãã©ã¯ãªã¿ãŒã³ããªãã®ã§ã
cortex_m_rt::entry
ãã¯ãã¯å¯Ÿè±¡é¢æ°ãfn() -> !
ãšããåã§ããããšãèŠæ±ããŸãã - ãã®äŸã¯
cargo embed --bin minimal
ã«ããå®è¡ããŸã
çMMIOïŒã¡ã¢ãªããããI/OïŒ
倧åã®ãã€ã¯ãã³ã³ãããŒã©ã¯ã¡ã¢ãªããããRIO空éãéããŠåšèŸºI/Oã«ã¢ã¯ã»ã¹ããŸããmicro:bitã®LEDãå ãããŠã¿ãŸããã:
#![no_main] #![no_std] extern crate panic_halt as _; mod interrupts; use core::mem::size_of; use cortex_m_rt::entry; /// GPIO 0 çªããŒãã®åšèŸºã¢ãã¬ã¹ const GPIO_P0: usize = 0x5000_0000; // GPIO åšèŸºæ©åšãªãã»ãã const PIN_CNF: usize = 0x700; const OUTSET: usize = 0x508; const OUTCLR: usize = 0x50c; // PIN_CNF ãã£ãŒã«ã const DIR_OUTPUT: u32 = 0x1; const INPUT_DISCONNECT: u32 = 0x1 << 1; const PULL_DISABLED: u32 = 0x0 << 2; const DRIVE_S0S1: u32 = 0x0 << 8; const SENSE_DISABLED: u32 = 0x0 << 16; #[entry] fn main() -> ! { // GPIO 0 ã® 21 çªãã³ãš 28 çªãã³ãããã·ã¥ãã«åºåãšããŠèšå®ããŸãã let pin_cnf_21 = (GPIO_P0 + PIN_CNF + 21 * size_of::<u32>()) as *mut u32; let pin_cnf_28 = (GPIO_P0 + PIN_CNF + 28 * size_of::<u32>()) as *mut u32; // SAFETY: The pointers are to valid peripheral control registers, and no // aliases exist. unsafe { pin_cnf_21.write_volatile( DIR_OUTPUT | INPUT_DISCONNECT | PULL_DISABLED | DRIVE_S0S1 | SENSE_DISABLED, ); pin_cnf_28.write_volatile( DIR_OUTPUT | INPUT_DISCONNECT | PULL_DISABLED | DRIVE_S0S1 | SENSE_DISABLED, ); } // 28 çªãã³ãããŒã21 çªãã³ããã€ã«èšå®ã㊠LED ããªã³ã«ããŸãã let gpio0_outset = (GPIO_P0 + OUTSET) as *mut u32; let gpio0_outclr = (GPIO_P0 + OUTCLR) as *mut u32; // SAFETY: The pointers are to valid peripheral control registers, and no // aliases exist. unsafe { gpio0_outclr.write_volatile(1 << 28); gpio0_outset.write_volatile(1 << 21); } loop {} }
- GPIO 0ã®ãã³21ã¯ãããªã¯ã¹LEDã®äžçªç®ã®åã«ããã³28ã¯æåã®è¡ã«æ¥ç¶ãããŠããŸãã
äŸã®å®è¡æ¹æ³:
cargo embed --bin mmio
åšèŸºI/Oãžã¢ã¯ã»ã¹ããããã®ã¯ã¬ãŒãïŒPACsïŒ
svd2rust
ã¯CMSIS-SVD ãã¡ã€ã«ãããã¡ã¢ãªããããããåšèŸºI/Oã«å¯Ÿããã»ãŒå®å
šïŒmostly-safeïŒãªRustã©ãããŒãçæããŸãã
#![no_main] #![no_std] extern crate panic_halt as _; use cortex_m_rt::entry; use nrf52833_pac::Peripherals; #[entry] fn main() -> ! { let p = Peripherals::take().unwrap(); let gpio0 = p.P0; // GPIO 0 ã® 21 çªãã³ãš 28 çªãã³ãããã·ã¥ãã«åºåãšããŠèšå®ããŸãã gpio0.pin_cnf[21].write(|w| { w.dir().output(); w.input().disconnect(); w.pull().disabled(); w.drive().s0s1(); w.sense().disabled(); w }); gpio0.pin_cnf[28].write(|w| { w.dir().output(); w.input().disconnect(); w.pull().disabled(); w.drive().s0s1(); w.sense().disabled(); w }); // 28 çªãã³ãããŒã21 çªãã³ããã€ã«èšå®ã㊠LED ããªã³ã«ããŸãã gpio0.outclr.write(|w| w.pin28().clear()); gpio0.outset.write(|w| w.pin21().set()); loop {} }
- SVD (System View Description)ãã¡ã€ã«ã¯XMLãã¡ã€ã«ã§ããã€ã¹ã®ã¡ã¢ãªããããèšè¿°ãããã®ã§ãããéåžžã·ãªã³ã³ãã³ãã«ããæäŸãããŸãã
- åšèŸºI/Oããšã«ãã¬ãžã¹ã¿ããã£ãŒã«ããšå€ãååã説æãã¢ãã¬ã¹ãªã©ã«ããæ§æãããŠããŸãã
- SVDãã¡ã€ã«ã«ã¯ãã誀ããããããŸãæ å ±ãäžè¶³ããŠããããšãå€ãã®ã§ãæ§ã ãªãããžã§ã¯ãããããä¿®æ£ã»è¿œå ããã¯ã¬ãŒããšããŠå ¬éããŠããŸãã
cortex-m-rt
ã¯ãã¯ã¿ããŒãã«ãæäŸããŸãã- ãã
cargo install cargo-binutils
ãå®è¡ããŠããã°ãcargo objdump --bin pac -- -d --no-show-raw-insn
ãå®è¡ããããšã«ããçæããããã€ããªã®äžèº«ãèŠãããšãã§ããŸãã
äŸã®å®è¡æ¹æ³:
cargo embed --bin pac
HALã¯ã¬ãŒã
å€ãã®ãã€ã¯ãã³ã³ãããŒã©ã«å¯ŸããHALã¯ã¬ãŒããæ§ã
ãªåšèŸºI/Oã«å¯Ÿããã©ãããŒãæäŸããŠããŸãããããã®ã¯ã¬ãŒãã®å€ãã¯embedded-hal
ãå®çŸ©ãããã¬ã€ããå®è£
ããŠããŸãã
#![no_main] #![no_std] extern crate panic_halt as _; use cortex_m_rt::entry; use embedded_hal::digital::OutputPin; use nrf52833_hal::gpio::{p0, Level}; use nrf52833_hal::pac::Peripherals; #[entry] fn main() -> ! { let p = Peripherals::take().unwrap(); // GPIO 0 çªããŒãã® HAL ã©ãããŒãäœæããŸãã let gpio0 = p0::Parts::new(p.P0); // GPIO 0 ã® 21 çªãã³ãš 28 çªãã³ãããã·ã¥ãã«åºåãšããŠèšå®ããŸãã let mut col1 = gpio0.p0_28.into_push_pull_output(Level::High); let mut row1 = gpio0.p0_21.into_push_pull_output(Level::Low); // 28 çªãã³ãããŒã21 çªãã³ããã€ã«èšå®ã㊠LED ããªã³ã«ããŸãã col1.set_low().unwrap(); row1.set_high().unwrap(); loop {} }
set_low
ãšset_high
ã¯embedded_hal
ã®OutputPin
ãã¬ã€ãã®å®çŸ©ããã¡ãœããã§ãã- Cortex-MãRISC-Vã®å€ãã®ããã€ã¹ã«å¯ŸããŠHALã¯ã¬ãŒããååšãããããã«ã¯STM32ãGD32ãnRFãNXPãMSP430ãAVRãPICãã€ã¯ãã³ã³ãããŒã©ãªã©ãå«ãŸããŸãã
äŸã®å®è¡æ¹æ³:
cargo embed --bin hal
ããŒããµããŒãã¯ã¬ãŒã
ããŒããµããŒãã¯ã¬ãŒãã¯ç¹å®ã®ããŒãã«å¯ŸããŠæŽã«å©äŸ¿æ§ãåäžãããã©ãããŒãæäŸããŸãã
#![no_main] #![no_std] extern crate panic_halt as _; use cortex_m_rt::entry; use embedded_hal::digital::OutputPin; use microbit::Board; #[entry] fn main() -> ! { let mut board = Board::take().unwrap(); board.display_pins.col1.set_low().unwrap(); board.display_pins.row1.set_high().unwrap(); loop {} }
- ãã®äŸã§ã¯ãããŒããµããŒãã¯ã¬ãŒãã¯åã«åãããããååãæäŸããå°ãã®åæåãå®æœããŠããã ãã§ãã
- ãã€ã¯ãã³ã³ãããŒã©ã®å€ã«å®è£
ããããªã³ããŒãããã€ã¹ã«å¯Ÿãããã©ã€ããæäŸãããŠããããšããããŸãã
microbit-v2
ã¯ãããªã¯ã¹LEDã«å¯Ÿããç°¡åãªãã©ã€ããå«ãã§ããŸãã
äŸã®å®è¡æ¹æ³:
cargo embed --bin board_support
ã¿ã€ãã¹ããŒããã¿ãŒã³
#[entry] fn main() -> ! { let p = Peripherals::take().unwrap(); let gpio0 = p0::Parts::new(p.P0); let pin: P0_01<Disconnected> = gpio0.p0_01; // let gpio0_01_again = gpio0.p0_01; // ãšã©ãŒã移åæžã¿ã let mut pin_input: P0_01<Input<Floating>> = pin.into_floating_input(); if pin_input.is_high().unwrap() { // ... } let mut pin_output: P0_01<Output<OpenDrain>> = pin_input .into_open_drain_output(OpenDrainConfig::Disconnect0Standard1, Level::Low); pin_output.set_high().unwrap(); // pin_input.is_high(); // ãšã©ãŒã移åæžã¿ã let _pin2: P0_02<Output<OpenDrain>> = gpio0 .p0_02 .into_open_drain_output(OpenDrainConfig::Disconnect0Standard1, Level::Low); let _pin3: P0_03<Output<PushPull>> = gpio0.p0_03.into_push_pull_output(Level::Low); loop {} }
- ãã®äŸã§ã¯ããã³ãè¡šãã¿ã€ãã¯
Copy
ãClone
ãå®è£ ããŠããŸããããã®ããããã äžã€ã®ã€ã³ã¹ã¿ã³ã¹ã ããååšå¯èœã§ãããã³ãããŒãæ§é äœããã ãŒãããããšãä»ã®èª°ããã®ãã³ã«ã¢ã¯ã»ã¹ããããšã¯ã§ããªããªããŸãã - ãã³ã®èšå®ãå€æŽããããšã¯å€ããã³ã®ã€ã³ã¹ã¿ã³ã¹ãæ¶è²»ããããšã«ãªããŸãããã®ããããã以éã¯å€ãã€ã³ã¹ã¿ã³ã¹ã䜿ãç¶ããããšã¯ã§ããªããªããŸãã
- å€æ°ã®åã¯ãã®ç¶æ ãè¡šãããã«ãªã£ãŠããŸããäŸãã°ããã®äŸã§ã¯åãGPIOãã³ã®ç¶æ ãè¡šããŠããŸãããã®ããã«ã¹ããŒããã·ã³ãã¿ã€ãã·ã¹ãã ã«ç¹ã蟌ãããšã§ãæ£ããèšå®ãããã«ãã³ã䜿ã£ãŠããŸãããšããªããªããŸããäžæ£ãªç¶æ é·ç§»ã«é¢ããŠã¯ã³ã³ãã€ã«æã«çºèŠãããããã«ãªããŸãã
- ã€ã³ããããã³ã«å¯ŸããŠ
is_high
ãåŒã³åºãããšã¯å¯èœã§ãã¢ãŠãããããã³ã«å¯ŸããŠset_high
ãåŒã³åºãããšãå¯èœã§ãããããããã®éã®çµã¿åããã¯äžå¯èœã§ãã - å€ãã®HALã¯ã¬ãŒãããã®ãã¿ãŒã³ãçšããŠããŸãã
embedded-hal
The embedded-hal
crate provides a number of traits covering common microcontroller peripherals:
- GPIO
- PWM
- Delay timers
- I2C and SPI buses and devices
Similar traits for byte streams (e.g. UARTs), CAN buses and RNGs and broken out into embedded-io
, embedded-can
and rand_core
respectively.
Other crates then implement drivers in terms of these traits, e.g. an accelerometer driver might need an I2C or SPI device instance.
- The traits cover using the peripherals but not initialising or configuring them, as initialisation and configuration is usually highly platform-specific.
- å€ãã®ãã€ã¯ãã³ã³ãããŒã©ã«å¯Ÿããå®è£ ã«å ããŠãRaspberry Piäžã®Linuxåãã®å®è£ ãååšããŸãã
embedded-hal-async
provides async versions of the traits.embedded-hal-nb
provides another approach to non-blocking I/O, based on thenb
crate.
probe-rs
ãšcargo-embed
probe-rsã¯çµã¿èŸŒã¿åããããã°ã«æçšãªããŒã«ã»ããã§ããããã¯OpenOCDã®ãããªãã®ã§ãããããé«åºŠã«çµ±åãããŠããŸãã
- SWD (Serial Wire Debug) ãCMSIS-DAPçµç±ã®JTAGã ST-LinkãJ-LinkãããŒã
- GDBã¹ã¿ããMicrosoft DAP (Debug Adapter Protocol)ãµãŒã
- Cargoãšã®ã€ã³ãã°ã¬ãŒã·ã§ã³
cargo-embed
ã¯cargoã®ãµãã³ãã³ãã§ããããã€ããªããã«ããããããã©ãã·ã¥ããããRTTïŒReal Time TransfersïŒã®åºåãã°ãååŸããããGDBã«æ¥ç¶ããããã®ãã®ã§ããèšå®ã¯å¯Ÿè±¡ãšãããããžã§ã¯ããã£ã¬ã¯ããªã«ãããEmbed.toml
ãã¡ã€ã«ã«ããè¡ããŸãã
- CMSIS-DAP ã¯USBäžã®ARMæšæºãããã³ã«ã§ãã€ã³ãµãŒãããã»ãããã¬ãæ§ã ãªArm Cortexããã»ããµã®ã³ã¢ãµã€ãã»ãããã°ã»ã¢ã¯ã»ã¹ããŒãã«ã¢ã¯ã»ã¹ããããã®ãã®ã§ããBBC micro:bit ã®ãªã³ããŒãã»ãããã¬ããããå©çšããŠããŸãã
- ST-Link ã¯ST Microelectronicsã«ããã€ã³ãµãŒãããã»ãããã¬ã®ç·ç§°ã§ã J-Linkã¯SEGGERã«ããã€ã³ãµãŒãããã»ãããã¬ã®ç·ç§°ã§ãã
- ãããã°ã»ã¢ã¯ã»ã¹ããŒãã¯éåžžïŒãã³ã®JTAGã€ã³ã¿ãã§ãŒã¹ãã2ãã³ã®ã·ãªã¢ã«ã¯ã€ã€ãããã°ã§ãã
- probe-rsã¯èªåã§ç¬èªã®ããŒã«ãçµ±åãããå Žåã«å©çšã§ããã©ã€ãã©ãªã§ãã
- Microsoft Debug Adapter Protocol ã¯VSCodeãä»ã®IDEããããµããŒãããããã€ã¯ãã³ã³ãããŒã©äžã§å®è¡ãããŠããã³ãŒãããããã°ããããšãå¯èœã«ããŸãã
- cargo-embedã¯probe-rsã©ã€ãã©ãªãå©çšããŠçæããããã€ããªã§ãã
- RTT (Real Time Transfers)ã¯ãããã°ãã¹ããšã¿ãŒã²ããéã®ããŒã¿ãå€ãã®ãªã³ã°ãããã¡ãä»ããŠãããšãããããã®ã¡ã«ããºã ã§ãã
ãããã°
Embed.toml:
[default.general]
chip = "nrf52833_xxAA"
[debug.gdb]
enabled = true
ã²ãšã€ã®ã¿ãŒããã«ã§ãsrc/bare-metal/microcontrollers/examples/
ã«ãããŠäžèšãå®è¡:
cargo embed --bin board_support debug
å¥ã®ã¿ãŒããã«ã§ãåããã£ã¬ã¯ããªã§äžèšãå®è¡:
gLinuxãŸãã¯Debianã®å Žå:
gdb-multiarch target/thumbv7em-none-eabihf/debug/board_support --eval-command="target remote :1337"
MacOSã®å Žåã¯ä»¥äžã®ããã«ãªããŸã:
arm-none-eabi-gdb target/thumbv7em-none-eabihf/debug/board_support --eval-command="target remote :1337"
GDBã§äžèšãå®è¡ããŠã¿ãŠãã ãã:
b src/bin/board_support.rs:29
b src/bin/board_support.rs:30
b src/bin/board_support.rs:32
c
c
c
ä»ã®ãããžã§ã¯ã
- RTIC
- "Real-Time Interrupt-driven Concurrency".
- Shared resource management, message passing, task scheduling, timer queue.
- Embassy
async
executors with priorities, timers, networking, USB.
- TockOS
- Security-focused RTOS with preemptive scheduling and Memory Protection Unit support.
- Hubris
- Microkernel RTOS from Oxide Computer Company with memory protection, unprivileged drivers, IPC.
- Bindings for FreeRTOS.
ããã€ãã®ãã©ãããã©ãŒã ã§ã¯ std
ã®å®è£
ãããäŸãã° esp-idfã
- RTICã¯RTOSãšããŠæããããšãã§ããŸããã䞊è¡å®è¡ã®ãã¬ãŒã ã¯ãŒã¯ãšããŠæããããšãã§ããŸãã
- ä»ã®HALãå šãå«ãã§ããŸããã
- ã¹ã±ãžã¥ãŒãªã³ã°ã¯ã«ãŒãã«ã§ã¯ãªããCortex-M NVIC (Nested Virtual Interrupt Controller)ãå©çšããŠè¡ããŸãã
- Cortex-Mã®ã¿ã®å¯Ÿå¿ã§ãã
- Googleã¯TockOSãTitanã»ãã¥ãªãã£ããŒã®Havenãã€ã¯ãã³ã³ãããŒã©ã§å©çšããŠããŸãã
- FreeRTOS ã¯ã»ãšãã©Cã§æžãããŠããŸãããã¢ããªã±ãŒã·ã§ã³ãéçºããããã®Rustãã€ã³ãã£ã³ã°ãååšããŸãã
ç·Žç¿åé¡
I2Cæ¥ç¶ã®ã³ã³ãã¹ããæ¹äœãèªã¿åãããã®çµæãã·ãªã¢ã«ããŒãã«åºåããŸãã
ç·Žç¿åé¡ã«åãçµãã ããšã¯ã 解çãã¿ãŠãæ§ããŸããã
ã³ã³ãã¹
I2Cæ¥ç¶ã®ã³ã³ãã¹ããæ¹äœãèªã¿åãããã®çµæãã·ãªã¢ã«ããŒãã«åºåããŸããããæéãããã°ãLEDããã¿ã³ããªããšãå©çšããŠæ¹äœãåºåããŠã¿ãŠãã ããã
ãã³ã:
lsm303agr
ã¯ã¬ãŒããšmicrobit-v2
ã¯ã¬ãŒãã®ããã¥ã¡ã³ãããªãã³ã«micro:bitããŒããŠã§ã¢ä»æ§ã確èªããŠã¿ãŠãã ããã- LSM303AGRæ £æ§èšæž¬åšã¯å éšã®I2Cãã¹ã«æ¥ç¶ãããŠããŸãã
- TWIã¯I2Cã®å¥åãªã®ã§ãI2Cãã¹ã¿ã¯TWIMãšããååã«ãªã£ãŠããŸãã
- The LSM303AGR driver needs something implementing the
embedded_hal::i2c::I2c
trait. Themicrobit::hal::Twim
struct implements this. - æ§ã
ãªãã³ãåšèŸºI/Oã®ããã®
microbit::Board
ãšããæ§é äœããããŸãã - nRF52833ããŒã¿ã·ãŒããèŠãããšãã§ããŸããããã®ç·Žç¿åé¡ã®ããã«ã¯å¿ èŠãªãã¯ãã§ãã
ç·Žç¿åé¡ã®ãã³ãã¬ãŒã ãããŠã³ããŒãããŠãcompass
ãšãããã£ã¬ã¯ããªã®äžã«ããäžèšã®ãã¡ã€ã«ãèŠãŠãã ããã
src/main.rs:
#![no_main] #![no_std] extern crate panic_halt as _; use core::fmt::Write; use cortex_m_rt::entry; use microbit::{hal::{Delay, uarte::{Baudrate, Parity, Uarte}}, Board}; #[entry] fn main() -> ! { let mut board = Board::take().unwrap(); // Configure serial port. let mut serial = Uarte::new( board.UARTE0, board.uart.into(), Parity::EXCLUDED, Baudrate::BAUD115200, ); // Use the system timer as a delay provider. let mut delay = Delay::new(board.SYST); // Set up the I2C controller and Inertial Measurement Unit. // TODO writeln!(serial, "Ready.").unwrap(); loop { // Read compass data and log it to the serial port. // TODO } }
Cargo.toml (å€æŽã¯äžèŠãªã¯ãã§ã):
[workspace]
[package]
name = "compass"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
cortex-m-rt = "0.7.3"
embedded-hal = "1.0.0"
lsm303agr = "1.1.0"
microbit-v2 = "0.15.1"
panic-halt = "1.0.0"
Embed.toml (å€æŽã¯äžèŠãªã¯ãã§ã):
[default.general]
chip = "nrf52833_xxAA"
[debug.gdb]
enabled = true
[debug.reset]
halt_afterwards = true
.cargo/config.toml (å€æŽã¯äžèŠãªã¯ãã§ã):
[build]
target = "thumbv7em-none-eabihf" # Cortex-M4F
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = ["-C", "link-arg=-Tlink.x"]
Linuxã§ã¯ã·ãªã¢ã«ããŒãåºåãäžèšã®ã³ãã³ãã§ç¢ºèªããŸã:
picocom --baud 115200 --imap lfcrlf /dev/ttyACM0
Mac OSã§ã¯ãããªæãã«ãªããŸãïŒããã€ã¹åãå°ãéããããããŸããïŒ:
picocom --baud 115200 --imap lfcrlf /dev/tty.usbmodem14502
Ctrl+A Ctrl+Q ã§picocomãçµäºããŸãã
ãã¢ã¡ã¿ã« Rust ã®ååã®æŒç¿
ã³ã³ãã¹
ïŒæŒç¿ã«æ»ãïŒ
#![no_main] #![no_std] extern crate panic_halt as _; use core::fmt::Write; use cortex_m_rt::entry; use core::cmp::{max, min}; use embedded_hal::digital::InputPin; use lsm303agr::{ AccelMode, AccelOutputDataRate, Lsm303agr, MagMode, MagOutputDataRate, }; use microbit::display::blocking::Display; use microbit::hal::twim::Twim; use microbit::hal::uarte::{Baudrate, Parity, Uarte}; use microbit::hal::{Delay, Timer}; use microbit::pac::twim0::frequency::FREQUENCY_A; use microbit::Board; const COMPASS_SCALE: i32 = 30000; const ACCELEROMETER_SCALE: i32 = 700; #[entry] fn main() -> ! { let mut board = Board::take().unwrap(); // ã·ãªã¢ã«ããŒããèšå®ããŸãã let mut serial = Uarte::new( board.UARTE0, board.uart.into(), Parity::EXCLUDED, Baudrate::BAUD115200, ); // ã·ã¹ãã ã¿ã€ããŒãé 延ç®çã§äœ¿çšããŸãã let mut delay = Delay::new(board.SYST); // I2C ã³ã³ãããŒã©ãšæ £æ§æž¬å®ãŠããããã»ããã¢ããããŸãã writeln!(serial, "Setting up IMU...").unwrap(); let i2c = Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100); let mut imu = Lsm303agr::new_with_i2c(i2c); imu.init().unwrap(); imu.set_mag_mode_and_odr( &mut delay, MagMode::HighResolution, MagOutputDataRate::Hz50, ) .unwrap(); imu.set_accel_mode_and_odr( &mut delay, AccelMode::Normal, AccelOutputDataRate::Hz50, ) .unwrap(); let mut imu = imu.into_mag_continuous().ok().unwrap(); // ãã£ã¹ãã¬ã€ãšã¿ã€ããŒãã»ããã¢ããããŸãã let mut timer = Timer::new(board.TIMER0); let mut display = Display::new(board.display_pins); let mut mode = Mode::Compass; let mut button_pressed = false; writeln!(serial, "Ready.").unwrap(); loop { // ã³ã³ãã¹ããŒã¿ãèªã¿åããã·ãªã¢ã«ããŒãã«èšé²ããŸãã while !(imu.mag_status().unwrap().xyz_new_data() && imu.accel_status().unwrap().xyz_new_data()) {} let compass_reading = imu.magnetic_field().unwrap(); let accelerometer_reading = imu.acceleration().unwrap(); writeln!( serial, "{},{},{}\t{},{},{}", compass_reading.x_nt(), compass_reading.y_nt(), compass_reading.z_nt(), accelerometer_reading.x_mg(), accelerometer_reading.y_mg(), accelerometer_reading.z_mg(), ) .unwrap(); let mut image = [[0; 5]; 5]; let (x, y) = match mode { Mode::Compass => ( scale(-compass_reading.x_nt(), -COMPASS_SCALE, COMPASS_SCALE, 0, 4) as usize, scale(compass_reading.y_nt(), -COMPASS_SCALE, COMPASS_SCALE, 0, 4) as usize, ), Mode::Accelerometer => ( scale( accelerometer_reading.x_mg(), -ACCELEROMETER_SCALE, ACCELEROMETER_SCALE, 0, 4, ) as usize, scale( -accelerometer_reading.y_mg(), -ACCELEROMETER_SCALE, ACCELEROMETER_SCALE, 0, 4, ) as usize, ), }; image[y][x] = 255; display.show(&mut timer, image, 100); // ãã¿ã³ A ãæŒãããå Žåã次ã®ã¢ãŒãã«åãæ¿ããŠãã¹ãŠã® LED ãçæéç¹æ» // ãããŸãã if board.buttons.button_a.is_low().unwrap() { if !button_pressed { mode = mode.next(); display.show(&mut timer, [[255; 5]; 5], 200); } button_pressed = true; } else { button_pressed = false; } } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] enum Mode { Compass, Accelerometer, } impl Mode { fn next(self) -> Self { match self { Self::Compass => Self::Accelerometer, Self::Accelerometer => Self::Compass, } } } fn scale(value: i32, min_in: i32, max_in: i32, min_out: i32, max_out: i32) -> i32 { let range_in = max_in - min_in; let range_out = max_out - min_out; cap(min_out + range_out * (value - min_in) / range_in, min_out, max_out) } fn cap(value: i32, min_value: i32, max_value: i32) -> i32 { max(min_value, min(value, max_value)) }
ã¢ããªã±ãŒã·ã§ã³ããã»ããµ
ãããŸã§ã¯Arm Cortex-Mã·ãªãŒãºã®ãããªãã€ã¯ãã³ã³ãããŒã©ã«ã€ããŠèŠãŠããŸãããä»åºŠã¯Cortex-Aã察象ãšããŠäœããæžããŠã¿ãŸããããç°¡ååã®ããã«ãããã§ã¯ïŒæ¬ç©ã®ããŒããŠã§ã¢ã§ã¯ãªãïŒQEMUã®aarch64 'virt'ããŒããå©çšããŸãã
- 倧ãŸãã«èšã£ãŠããã€ã¯ãã³ã³ãããŒã©ãMMUãè€æ°ã®ç¹æš©ã¬ãã«ïŒArm CPUã«ãããäŸå€ã¬ãã«ãx86ã«ããããªã³ã°ïŒãæããªãã®ã«å¯Ÿããã¢ããªã±ãŒã·ã§ã³ããã»ããµã¯ããããæã£ãŠããŸãã
- QEMU ã¯åã ã®ã¢ãŒããã¯ãã£ã«å¯ŸããŠæ§ã ãªç°ãªããã·ã³ãããŒãã¢ãã«ããµããŒãããŠããŸããä»å䜿ã 'virt' ããŒãã¯ç¹å®ã®æ¬ç©ã®ããŒããŠã§ã¢ã«å¯Ÿå¿ãããã®ã§ã¯ãªããçŽç²ã«ä»®æ³ãã·ã³ãšããŠèšèšããããã®ã§ãã
Rust ã®æºå
Rustã®ã³ãŒããå®è¡ã§ããããã«ãªãåã«ããã€ãã®åæåãå¿ èŠã§ãã
.section .init.entry, "ax"
.global entry
entry:
/*
* Load and apply the memory management configuration, ready to
* enable MMU and caches.
*/
adrp x30, idmap
msr ttbr0_el1, x30
mov_i x30, .Lmairval
msr mair_el1, x30
mov_i x30, .Ltcrval
/* Copy the supported PA range into TCR_EL1.IPS. */
mrs x29, id_aa64mmfr0_el1
bfi x30, x29, #32, #4
msr tcr_el1, x30
mov_i x30, .Lsctlrval
/*
* Ensure everything before this point has completed, then
* invalidate any potentially stale local TLB entries before they
* start being used.
*/
isb
tlbi vmalle1
ic iallu
dsb nsh
isb
/*
* Configure sctlr_el1 to enable MMU and cache and don't proceed
* until this has completed.
*/
msr sctlr_el1, x30
isb
/* Disable trapping floating point access in EL1. */
mrs x30, cpacr_el1
orr x30, x30, #(0x3 << 20)
msr cpacr_el1, x30
isb
/* Zero out the bss section. */
adr_l x29, bss_begin
adr_l x30, bss_end
0: cmp x29, x30
b.hs 1f
stp xzr, xzr, [x29], #16
b 0b
1: /* Prepare the stack. */
adr_l x30, boot_stack_end
mov sp, x30
/* Set up exception vector. */
adr x30, vector_table_el1
msr vbar_el1, x30
/* Call into Rust code. */
bl main
/* Loop forever waiting for interrupts. */
2: wfi
b 2b
- ãã®åæåå
容ã¯Cã®å Žåãšåãã«ãªããŸããããã»ããµç¶æ
ãåæåããŠãBSSããŒãåãããŠãã¹ã¿ãã¯ãã€ã³ã¿ãèšå®ããŸãã
- BSSïŒæŽå²çãªçç±ã«ããblock starting symbolãšåŒã°ããŠãããã®ïŒã¯ãªããžã§ã¯ããã¡ã€ã«ã«ãããŠãŒãåæåãããéçãªå€æ°ãå«ãéšåã§ãããã®éšåã¯ãŒãã«ããé åã®æµªè²»ãé¿ããããã«ã€ã¡ãŒãžããã¯é€å€ãããŠããŸããã³ã³ãã€ã©ã¯ããŒãããã®é åããŒãåæåããããšãæ³å®ããŠããã®ã§ãã
- ã¡ã¢ãªã®åæåæ¹æ³ãã€ã¡ãŒãžã®ããŒãæ¹æ³ã«ãã£ãŠã¯BSSã¯ãã§ã«ãŒãåããããŠããããšããããŸãããããã§ã¯å¿µã®çºã«ãŒãåãããŠããŸãã
- ãããªãã¡ã¢ãªã®readãwriteãããåã«MMUãšãã£ãã·ã¥ãæå¹åããå¿
èŠããããŸãããããããªããšïŒ
- ã¢ã©ã€ã³ãããŠããªãã¢ã¯ã»ã¹ããã©ãŒã«ãã«ãªããŸããæã
ã¯ã³ã³ãã€ã©ãã¢ã©ã€ã³ãããŠããªãã¢ã¯ã»ã¹ãçæããªãããã«
+strict-align
ãªãã·ã§ã³ ãèšå®ããaarch64-unknown-none
ã¿ãŒã²ããåãã«Rustã³ãŒãããã«ãããŸãããã®ããããã§ã¯åé¡ã«ã¯ãªããŸããããäžè¬çã«ã¯ãããšã¯èšããŸããã - ããVMäžã§å®è¡ããŠãããšãããšããã£ãã·ã¥ã³ããŒã¬ã³ã·ãŒã®åé¡ãèµ·ããããšããããŸããåé¡ãªã®ã¯VMããã£ãã·ã¥ãç¡å¹åãããŸãŸçŽæ¥ã¡ã¢ãªã«ã¢ã¯ã»ã¹ããŠããã®ã«å¯Ÿãããã¹ãã¯åãã¡ã¢ãªã«å¯ŸããŠãã£ãã·ã¥å¯èœãªãšã€ãªã¢ã¹ãæã£ãŠããŸããšããããšã§ãããã¹ããä»®ã«æ瀺çã«ã¡ã¢ãªã«ã¢ã¯ã»ã¹ããªããšããŠããææ©çãªã¢ã¯ã»ã¹ã«ãããã£ãã·ã¥ãã£ã«ãèµ·ããããšããããŸãããããªããšããã¹ãããã£ãã·ã¥ããã©ãã·ã¥ãããVMããã£ãã·ã¥ãæå¹åãããšãã«ãVMããã¹ãã®ã©ã¡ããã«ããå€æŽã倱ãããŠããŸããŸããïŒãã£ãã·ã¥ã¯ä»®æ³ã¢ãã¬ã¹ãIPAã§ã¯ãªãç©çã¢ãã¬ã¹ãããŒãšããŠã¢ã¯ã»ã¹ãããŸãïŒ
- ã¢ã©ã€ã³ãããŠããªãã¢ã¯ã»ã¹ããã©ãŒã«ãã«ãªããŸããæã
ã¯ã³ã³ãã€ã©ãã¢ã©ã€ã³ãããŠããªãã¢ã¯ã»ã¹ãçæããªãããã«
- åçŽåã®ããã«ãããŒãã³ãŒãããããŒãžããŒãã«ïŒ
idmap.S
åç §ïŒãå©çšããŸãããã®ããŒãžããŒãã«ã¯æåã®1GiBãããã€ã¹çšã«ã次ã®1GiBãDRAMçšã«ã次ã®1GiBããããªãããã€ã¹çšã«ééçã«ãããããŸããããã¯QEMUã®ã¡ã¢ãªã¬ã€ã¢ãŠãã«åèŽããŸãã - äŸå€ãã¯ã¿ïŒ
vbar_el1
ïŒãèšå®ããŸããããã«é¢ããŠã¯åŸã»ã©è©³ããèŠãŸãã - ä»æ¥ã®ååŸã«æ±ããã¹ãŠã®äŸã¯äŸå€ã¬ãã«ïŒïŒEL1ïŒã§å®è¡ãããããšãæ³å®ããŠããŸãããããå¥ã®äŸå€ã¬ãã«ã§å®è¡ããå¿
èŠãããå Žåã«ã¯ã
entry.S
ãããã«åãããŠå€æŽããå¿ èŠããããŸãã
ã€ã³ã©ã€ã³ã¢ã»ã³ããª
ææRustã³ãŒãã§ã¯æžããªãããšãè¡ãããã«ã¢ã»ã³ããªèšèªã䜿ãå¿ èŠããããŸããäŸãã°ãé»æºãèœãšãããã«ãã¡ãŒã ãŠã§ã¢ã«å¯ŸããŠHVCïŒãã€ããŒãã€ã¶ã³ãŒã«ïŒãçºè¡ããå Žåã§ãïŒ
#![no_main] #![no_std] use core::arch::asm; use core::panic::PanicInfo; mod exceptions; const PSCI_SYSTEM_OFF: u32 = 0x84000008; // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn main(_x0: u64, _x1: u64, _x2: u64, _x3: u64) { // SAFETY: this only uses the declared registers and doesn't do anything // with memory. unsafe { asm!("hvc #0", inout("w0") PSCI_SYSTEM_OFF => _, inout("w1") 0 => _, inout("w2") 0 => _, inout("w3") 0 => _, inout("w4") 0 => _, inout("w5") 0 => _, inout("w6") 0 => _, inout("w7") 0 => _, options(nomem, nostack) ); } loop {} }
ïŒããå®éã«é»æºãèœãšãããã°ã©ã ãæžãããå Žåã¯ããããã®ãã¹ãŠã®æ©èœã«å¯Ÿããã©ãããŒãæäŸããŠããsmccc
ã䜿ããšè¯ãã§ããããïŒ
- PSCI ã¯Armã®Power State Coordination Interfaceã®ããšã§ãããããã¯ã·ã¹ãã ãCPUé»åç¶æ 管çã®æ©èœãå«ãæšæºçãªã»ããã§ããããã¯å€ãã®ã·ã¹ãã ã§EL3ãã¡ãŒã ãŠã§ã¢ãšãã€ããŒãã€ã¶ã«ããå®è£ ãããŠããŸãã
0 => _
ãšããã·ã³ã¿ãã¯ã¹ã¯ãã€ã³ã©ã€ã³ã¢ã»ã³ããªãå®è¡ããåã«ã¬ãžã¹ã¿ããŒãã§åæåããå®è¡åŸã¯ãã®å€ã¯æ°ã«ããªããšããããšã瀺ããŠããŸããin
ã§ã¯ãªãinout
ã䜿ãå¿ èŠãããã®ã¯ããã®å®è¡ã§ã¬ãžã¹ã¿ã®å€ãäžæžãããŠããŸãå¯èœæ§ãããããã§ãã- This
main
function needs to be#[unsafe(no_mangle)]
andextern "C"
because it is called from our entry point inentry.S
. _x0
â_x3
ã¯ã¬ãžã¹ã¿x0
âx3
ã®å€ã§ãããæ £ç¿çã«ããŒãããŒããããã€ã¹ããªãŒãªã©ãžã®ãã€ã³ã¿ãæž¡ãã®ã«å©çšãããŠããŸããïŒextern "C"
ã«ããæå®ãããïŒaarch64 ã®é¢æ°ã³ãŒã«èŠçŽã§ã¯ã¬ãžã¹ã¿x0
âx7
ã¯æåã®ïŒåã®åŒæ°ãé¢æ°ã«æž¡ãã®ã«å©çšãããããšã«ãªã£ãŠãããããentry.S
ã¯ãããã®å€ãå€æŽããªãããã«ãã以å€ã®ç¹å¥ãªããšãããå¿ èŠã¯ãããŸããã- ãã®äŸã
src/bare-metal/aps/examples
ã«ãããŠmake qemu_psci
ãšããããšã§QEMUã«ããå®è¡ããŠã¿ãŸãããã
MMIOã«å¯Ÿããvolatileã¢ã¯ã»ã¹
- Use
pointer::read_volatile
andpointer::write_volatile
. - 絶察ã«åç §ãä¿æããŠã¯ãããŸããã
- Use
&raw
to get fields of structs without creating an intermediate reference.
- Volatileã¢ã¯ã»ã¹ïŒMMIOé åã«å¯Ÿããreadãwriteã¯å¯äœçšãããããšãããã®ã§ãã³ã³ãã€ã©ãããŒããŠã§ã¢ãå®è¡é åºãå€æŽããããè€è£œããããçç¥ãããã§ããªãããã«ããããã®ãã®ã§ãã
- éåžžã¯ãäŸãã°ããå¯å€åç §ã«å¯ŸããŠã©ã€ãããªãŒããããšãã³ã³ãã€ã©ã¯ã©ã€ãããã®ãšåãå€ããªãŒãã§èªã¿åºããããšæ³å®ããå®éã«ã¡ã¢ãªããªãŒãããå¿ èŠã¯ãªããšå€æããŸãã
- ããŒããŠã§ã¢ãžã®volatileã¢ã¯ã»ã¹ãè¡ãããã®æ¢åã®ã¯ã¬ãŒãã«ã¯åç §ãä¿æãããã®ããããŸãããããã¯å¥å šã§ã¯ãããŸãããåç §ãååšããéã¯ãã€ã§ãã³ã³ãã€ã©ããã®åç §ãå€ããŠïŒMMIOé åã«ã¢ã¯ã»ã¹ããŠïŒããŸãå¯èœæ§ããããŸãã
- Use
&raw
to get struct field pointers from a pointer to the struct. - For compatibility with old versions of Rust you can use the
addr_of!
macro instead.
UARTãã©ã€ããæžããŠã¿ãŸããã
QEMUã®'virt' ãã·ã³ã«ã¯PL011ãšããUARTãããã®ã§ãããã«å¯Ÿãããã©ã€ããæžããŠã¿ãŸãããã
const FLAG_REGISTER_OFFSET: usize = 0x18; const FR_BUSY: u8 = 1 << 3; const FR_TXFF: u8 = 1 << 5; /// PL011 UARTã®æå°ãã©ã€ãã #[derive(Debug)] pub struct Uart { base_address: *mut u8, } impl Uart { /// æå®ãããããŒã¹ã¢ãã¬ã¹ã«ååšãã /// PL011 ããã€ã¹çšã® UART ãã©ã€ãã®æ°ããã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã /// /// # å®å šæ§ /// /// æå®ãããããŒã¹ã¢ãã¬ã¹ã¯ PL011 ããã€ã¹ã® 8 ã€ã® MMIO å¶åŸ¡ã¬ãžã¹ã¿ãæããŠããªããã°ãªããŸããã /// ãããã¯ããã€ã¹ã¡ã¢ãªãšããŠããã»ã¹ã®ã¢ãã¬ã¹ç©ºéã« /// ãããã³ã°ãããä»ã®ãšã€ãªã¢ã¹ã¯ãããŸããã pub unsafe fn new(base_address: *mut u8) -> Self { Self { base_address } } /// UART ã« 1 ãã€ããæžã蟌ã¿ãŸãã pub fn write_byte(&self, byte: u8) { // TX ãããã¡ã«ç©ºããã§ãããŸã§åŸ æ©ããŸãã while self.read_flag_register() & FR_TXFF != 0 {} // SAFETY: We know that the base address points to the control // registers of a PL011 device which is appropriately mapped. unsafe { // TX ãããã¡ã«æžã蟌ã¿ãŸãã self.base_address.write_volatile(byte); } // UART ãããžãŒã§ãªããªããŸã§åŸ æ©ããŸãã while self.read_flag_register() & FR_BUSY != 0 {} } fn read_flag_register(&self) -> u8 { // SAFETY: We know that the base address points to the control // registers of a PL011 device which is appropriately mapped. unsafe { self.base_address.add(FLAG_REGISTER_OFFSET).read_volatile() } } }
Uart::new
ãã¢ã³ã»ãŒãã§ãã®ä»ã®ã¡ãœãããã»ãŒãã§ãããšããããšã«æ³šç®ããŠãã ãããããã¯ãUart::new
ã®å®å šæ§èŠæ±ãæºããããŠããïŒããªãã¡ç¹å®ã®UARTã«å¯ŸããŠäžã€ãããã©ã€ãã®ã€ã³ã¹ã¿ã³ã¹ãååšããããã®ã¢ãã¬ã¹ç©ºéã«å¯ŸããŠãšã€ãªã¢ã¹ãå šãååšããªãïŒããšããã®åŒã³åºãå ãä¿èšŒããéãããã以éã¯å¿ èŠãªäºåæ¡ä»¶ãæºããããŠãããšæ³å®ããããšãã§ãwrite_byte
ãåžžã«å®å šã«åŒã³åºãããšãã§ããããã«ãªãããšãçç±ã§ãã- éã«ïŒ
new
ãã»ãŒãã«ããŠãwrite_byte
ãã¢ã³ã»ãŒãã«ïŒããããšãã§ããŸãããããããããšwrite_byte
ã®å šåŒã³åºãç®æã«ãããŠå®å šæ§ãèæ ®ããªããã°ãªããªããªããå©äŸ¿æ§ãäœäžããŸã - ããã¯ã¢ã³ã»ãŒããªã³ãŒãã«å¯ŸããŠã»ãŒããªã©ãããŒãæ§ç¯ããå Žåã®å ±éãã¿ãŒã³ã§ãïŒå¥å šæ§ã«é¢ãã蚌æã«é¢ããåŽåãå€æ°ã®å Žæããå°æ°ã®å Žæã«éçŽããŸãã
ä»ã®ãã¬ã€ã
ããã§ã¯Debug
ãã¬ã€ããå°åºããŸããããã®ä»ã«ãããã€ãã®ãã¬ã€ããå®è£
ãããšè¯ãã§ãããã
use core::fmt::{self, Write}; impl Write for Uart { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.as_bytes() { self.write_byte(*c); } Ok(()) } } // SAFETY: `Uart` just contains a pointer to device memory, which can be // accessed from any context. unsafe impl Send for Uart {}
Write
ãå®è£ ãããšãUart
ã¿ã€ãã«å¯ŸããŠwrite!
ãšwriteln!
ãã¯ããå©çšã§ããããã«ãªããŸãã- ãã®äŸã
src/bare-metal/aps/examples
ã«ãããŠmake qemu_minimal
ãšããããšã§ãQEMUã«ããå®è¡ããŠã¿ãŸãããã
UARTãã©ã€ãã®æ¹å
å®éã®ãšããPL011ã«ã¯ãã£ãšå€ãã®ã¬ãžã¹ã¿ãããããããã«ã¢ã¯ã»ã¹ããããã«ãªãã»ããã足ããŠãã€ã³ã¿ãåŸãããšã¯ééãã«ãªãããããå¯èªæ§ãäœäžãããŸããããã«ãããã€ãã¯ããããã£ãŒã«ããªã®ã§ãæ§é åãããæ¹æ³ã§ã¢ã¯ã»ã¹ã§ããã»ããè¯ãã§ãããã
ãªãã»ãã | ã¬ãžã¹ã¿å | å¹ |
---|---|---|
0x00 | DR | 12 |
0x04 | RSR | 4 |
0x18 | FR | 9 |
0x20 | ILPR | 8 |
0x24 | IBRD | 16 |
0x28 | FBRD | 6 |
0x2c | LCR_H | 8 |
0x30 | CR | 16 |
0x34 | IFLS | 6 |
0x38 | IMSC | 11 |
0x3c | RIS | 11 |
0x40 | MIS | 11 |
0x44 | ICR | 11 |
0x48 | DMACR | 3 |
- ããã€ãã®IDã¬ãžã¹ã¿ã¯ç°¡ååã®ããã®çç¥ããŠããŸãã
ããããã©ãã°
bitflags
ã¯ã¬ãŒãã¯ããããã©ã°ãæ±ãã®ã«äŸ¿å©ã§ãã
use bitflags::bitflags; bitflags! { /// UART ãã©ã°ã¬ãžã¹ã¿ããã®ãã©ã°ã #[repr(transparent)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct Flags: u16 { /// éä¿¡å¯ã const CTS = 1 << 0; /// ããŒã¿ã»ãã ã¬ãã£ã const DSR = 1 << 1; /// ããŒã¿ãã£ãªã¢æ€åºã const DCD = 1 << 2; /// UART ã¯ããŒã¿éä¿¡ã®ããããžãŒç¶æ ã const BUSY = 1 << 3; /// åä¿¡ FIFO ã空ã const RXFE = 1 << 4; /// éä¿¡ FIFO ãæºæ¯ã const TXFF = 1 << 5; /// åä¿¡ FIFO ãæºæ¯ã const RXFF = 1 << 6; /// éä¿¡ FIFO ã空ã const TXFE = 1 << 7; /// çåŒè¡šç€ºã const RI = 1 << 8; } }
bitflags!
ãã¯ãã¯Flags(u16)
ã®ãããªæ°ããã¿ã€ããçæãããã©ã°ãèªã¿æžãããããã®å€ãã®ã¡ãœããå®è£ ãäžç·ã«æäŸããŸãã
è€æ°ã®ã¬ãžã¹ã¿
æ§é äœã䜿ã£ãŠUARTã®ã¬ãžã¹ã¿ã®ã¡ã¢ãªã¬ã€ã¢ãŠããè¡šçŸããããšãã§ããŸãã
#[repr(C, align(4))] struct Registers { dr: u16, _reserved0: [u8; 2], rsr: ReceiveStatus, _reserved1: [u8; 19], fr: Flags, _reserved2: [u8; 6], ilpr: u8, _reserved3: [u8; 3], ibrd: u16, _reserved4: [u8; 2], fbrd: u8, _reserved5: [u8; 3], lcr_h: u8, _reserved6: [u8; 3], cr: u16, _reserved7: [u8; 3], ifls: u8, _reserved8: [u8; 3], imsc: u16, _reserved9: [u8; 2], ris: u16, _reserved10: [u8; 2], mis: u16, _reserved11: [u8; 2], icr: u16, _reserved12: [u8; 2], dmacr: u8, _reserved13: [u8; 3], }
#[repr(C)]
ã¯ã³ã³ãã€ã©ã«å¯ŸããŠãCãšåãèŠåã«åŸã£ãŠæ§é äœã®ãã£ãŒã«ããå®çŸ©ãããŠããé çªã§é 眮ããããšãæ瀺ããŸããããã¯æ§é äœã®ã¬ã€ã¢ãŠããäºæž¬å¯èœã«ããããã«å¿ èŠã§ãããªããªãã°ãRustæšæºã®è¡šçŸã¯ã³ã³ãã€ã©ããã£ãŒã«ãã奜ããªããã«äžŠã³æ¿ããããšïŒä»ã«ãè²ã ãšãããŸããïŒãèš±ããŠããããã§ãã
ãã©ã€ã
æ°ããå®çŸ©ããRegisters
æ§é äœãæã
ã®ãã©ã€ãã§äœ¿ã£ãŠã¿ãŸãããã
/// PL011 UART ã®ãã©ã€ãã #[derive(Debug)] pub struct Uart { registers: *mut Registers, } impl Uart { /// æå®ãããããŒã¹ã¢ãã¬ã¹ã«ååšãã /// PL011 ããã€ã¹çšã® UART ãã©ã€ãã®æ°ããã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã /// /// # å®å šæ§ /// /// æå®ãããããŒã¹ã¢ãã¬ã¹ã¯ PL011 ããã€ã¹ã® 8 ã€ã® MMIO å¶åŸ¡ã¬ãžã¹ã¿ãæããŠããªããã°ãªããŸããã /// ãããã¯ããã€ã¹ã¡ã¢ãªãšããŠããã»ã¹ã®ã¢ãã¬ã¹ç©ºéã« /// ãããã³ã°ãããä»ã®ãšã€ãªã¢ã¹ã¯ãããŸããã pub unsafe fn new(base_address: *mut u32) -> Self { Self { registers: base_address as *mut Registers } } /// UART ã« 1 ãã€ããæžã蟌ã¿ãŸãã pub fn write_byte(&self, byte: u8) { // TX ãããã¡ã«ç©ºããã§ãããŸã§åŸ æ©ããŸãã while self.read_flag_register().contains(Flags::TXFF) {} // SAFETY: We know that self.registers points to the control registers // of a PL011 device which is appropriately mapped. unsafe { // TX ãããã¡ã«æžã蟌ã¿ãŸãã (&raw mut (*self.registers).dr).write_volatile(byte.into()); } // UART ãããžãŒã§ãªããªããŸã§åŸ æ©ããŸãã while self.read_flag_register().contains(Flags::BUSY) {} } /// ä¿çäžã®ãã€ããèªã¿åããäœãåãåã£ãŠããªãå Žåã¯`None` ã /// è¿ããŸãã pub fn read_byte(&self) -> Option<u8> { if self.read_flag_register().contains(Flags::RXFE) { None } else { // SAFETY: We know that self.registers points to the control // registers of a PL011 device which is appropriately mapped. let data = unsafe { (&raw const (*self.registers).dr).read_volatile() }; // TODO: ããã 8ïœ11 ã§ãšã©ãŒç¶æ ããã§ãã¯ããŸãã Some(data as u8) } } fn read_flag_register(&self) -> Flags { // SAFETY: We know that self.registers points to the control registers // of a PL011 device which is appropriately mapped. unsafe { (&raw const (*self.registers).fr).read_volatile() } } }
- Note the use of
&raw const
/&raw mut
to get pointers to individual fields without creating an intermediate reference, which would be unsound.
䜿çšäŸ
æã ã®ãã©ã€ãã䜿ã£ãŠãã·ãªã¢ã«ã³ã³ãœãŒã«ã«ã©ã€ããããããŠå ¥åããããã€ãããšã³ãŒããå°ããªããã°ã©ã ãæžããŠã¿ãŸãããã
#![no_main] #![no_std] mod exceptions; mod pl011; use crate::pl011::Uart; use core::fmt::Write; use core::panic::PanicInfo; use log::error; use smccc::psci::system_off; use smccc::Hvc; /// ãã©ã€ã㪠PL011 UART ã®ããŒã¹ã¢ãã¬ã¹ã const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _; // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) { // SAFETY: `PL011_BASE_ADDRESS` is the base address of a PL011 device, and // nothing else accesses that address range. let mut uart = unsafe { Uart::new(PL011_BASE_ADDRESS) }; writeln!(uart, "main({x0:#x}, {x1:#x}, {x2:#x}, {x3:#x})").unwrap(); loop { if let Some(byte) = uart.read_byte() { uart.write_byte(byte); match byte { b'\r' => { uart.write_byte(b'\n'); } b'q' => break, _ => continue, } } } writeln!(uart, "\n\nBye!").unwrap(); system_off::<Hvc>().unwrap(); }
- ã€ã³ã©ã€ã³ã¢ã»ã³ã㪠ã®äŸãšåãããã«ããã®
main
é¢æ°ã¯entry.S
ã«ããããšã³ããªãã€ã³ãããåŒã³åºãããŸãã詳现ã¯ãã¡ãã®speaker notesãåç §ããŠãã ããã - ãã®äŸã
src/bare-metal/aps/examples
ã«ãããŠmake qemu
ãšããããšã§QEMUã«ããå®è¡ããŠã¿ãŸãããã
ãã°åºå
log
ã¯ã¬ãŒããæäŸãããã°çšãã¯ãã䜿ãããšè¯ãã§ããããããã¯Log
ãã¬ã€ããå®è£
ããããšã§å¯èœã«ãªããŸãã
use crate::pl011::Uart; use core::fmt::Write; use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; use spin::mutex::SpinMutex; static LOGGER: Logger = Logger { uart: SpinMutex::new(None) }; struct Logger { uart: SpinMutex<Option<Uart>>, } impl Log for Logger { fn enabled(&self, _metadata: &Metadata) -> bool { true } fn log(&self, record: &Record) { writeln!( self.uart.lock().as_mut().unwrap(), "[{}] {}", record.level(), record.args() ) .unwrap(); } fn flush(&self) {} } /// UART ãã¬ãŒãåæåããŸãã pub fn init(uart: Uart, max_level: LevelFilter) -> Result<(), SetLoggerError> { LOGGER.uart.lock().replace(uart); log::set_logger(&LOGGER)?; log::set_max_level(max_level); Ok(()) }
LOGGER
ãset_logger
ãåŒã³åºãåã«åæåããŠããã®ã§ãlog` ã«ãããunwrapã¯ã»ãŒãã§ãã
䜿çšäŸ
䜿çšåã«loggerãåæåããå¿ èŠããããŸãã
#![no_main] #![no_std] mod exceptions; mod logger; mod pl011; use crate::pl011::Uart; use core::panic::PanicInfo; use log::{error, info, LevelFilter}; use smccc::psci::system_off; use smccc::Hvc; /// ãã©ã€ã㪠PL011 UART ã®ããŒã¹ã¢ãã¬ã¹ã const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _; // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) { // SAFETY: `PL011_BASE_ADDRESS` is the base address of a PL011 device, and // nothing else accesses that address range. let uart = unsafe { Uart::new(PL011_BASE_ADDRESS) }; logger::init(uart, LevelFilter::Trace).unwrap(); info!("main({x0:#x}, {x1:#x}, {x2:#x}, {x3:#x})"); assert_eq!(x1, 42); system_off::<Hvc>().unwrap(); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { error!("{info}"); system_off::<Hvc>().unwrap(); loop {} }
- æã ã®ãããã¯ãã³ãã©ããããã¯ã®è©³çŽ°ã«ã€ããŠãã°åºåã§ããããã«ãªã£ãããšã«æ³šç®ããŠãã ããã
- ãã®äŸã
src/bare-metal/aps/examples
ã«ãããŠmake qemu_logger
ãšããããšã§QEMUã«ããå®è¡ããŠã¿ãŸãããã
äŸå€
AArch64ã¯16ãšã³ããªãæã€äŸå€ãã¯ã¿ãŒããŒãã«ãå®çŸ©ããŠããããããã¯ïŒã€ã®ã¹ããŒãïŒçŸåšã®ELã§SP0å©çšãçŸåšã®ELã§SPxå©çšãäœäœã®ELã§AArch64ãäœäœã®ELã§AArch32ïŒã«ãããïŒã€ã®ã¿ã€ãã®äŸå€ïŒåæãIRQãFIQãSErrorïŒã«å¯Ÿå¿ããŸããããã§ã¯Rustã³ãŒãã®åŒã³åºãåã«æ®çºã¬ãžã¹ã¿ã®å€ãã¹ã¿ãã¯ã«éé¿ããããã«ãã¯ã¿ãŒããŒãã«ãã¢ã»ã³ããªèšèªã§å®è£ ããŠããŸãïŒ
use log::error; use smccc::psci::system_off; use smccc::Hvc; // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) { error!("sync_exception_current"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn irq_current(_elr: u64, _spsr: u64) { error!("irq_current"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn fiq_current(_elr: u64, _spsr: u64) { error!("fiq_current"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn serr_current(_elr: u64, _spsr: u64) { error!("serr_current"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn sync_lower(_elr: u64, _spsr: u64) { error!("sync_lower"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn irq_lower(_elr: u64, _spsr: u64) { error!("irq_lower"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn fiq_lower(_elr: u64, _spsr: u64) { error!("fiq_lower"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn serr_lower(_elr: u64, _spsr: u64) { error!("serr_lower"); system_off::<Hvc>().unwrap(); }
- ELã¯äŸå€ã¬ãã«ã§ããæ¬æ¥ã®ååŸã«æ±ã£ããã¹ãŠã®äŸã¯EL1ã§å®è¡ãããŠããŸãã
- ç°¡ååã®ããã«ãããã§ã¯çŸåšã®ELäŸå€ã«ãããSP0ãšSPïœã®éããäœäœã®ELã¬ãã«ã«ãããAArch32ãšAArch64ã®éããåºå¥ããŠããŸããã
- ããã§ã¯ãããã®äŸå€ãçºçããªãã¯ããªã®ã§ããã äŸå€ã«é¢ãããã°ãåºåããé»æºãèœãšããŠããŸãã
- äŸå€ãã³ãã©ãšã¡ã€ã³ã®å®è¡ã³ã³ããã¹ãã¯ç°ãªãã¹ã¬ããã®ãããªãã®ã ãšèããããšãã§ããŸããã¡ããã©ã¹ã¬ããéã®å
±æãšåãããã«ã
Send
ãšSync
ã«ããäœãå ±æããããå¶åŸ¡ããããšãã§ããŸããäŸãã°ãäŸå€ãã³ãã©ãšããã°ã©ã ã®ä»ã®ã³ã³ããã¹ãã§ãšããå€ãå ±æãããå Žåã«ããããããSend
ã§ããSync
ã§ãªããã°ãMutex
ã®ãããªãã®ã§ã©ããããŠãstaticã«å®çŸ©ããªããã°ãªããŸããã
ä»ã®ãããžã§ã¯ã
- oreboot
- "coreboot without the C".
- ã¢ãŒããã¯ãã£ã¯x86ãaarch64ãªãã³ã«RISC-VããµããŒãã
- èªèº«ã§å€ãã®ãã©ã€ããæ±ããã«LinuxBootã«äŸåã
- Rust RaspberryPi OS ã®ãã¥ãŒããªã¢ã«
- Initialisation, UART driver, simple bootloader, JTAG, exception levels, exception handling, page tables.
- ãã£ãã·ã¥ã¡ã³ããã³ã¹ãšRustã®åæåã«é¢ããŠã¡ãã£ãšçããããšãããããã®ã§ã補åã³ãŒãã§ç䌌ããã«ã¯å¿ ãããè¯ãäŸã§ã¯ãããŸããã
cargo-call-stack
- ã¹ã¿ãã¯ã®æ倧䜿çšéã«é¢ããéç解æã
- RaspberryPi OS ãã¥ãŒããªã¢ã«ã¯MMUããã£ãã·ã¥ãæå¹åããåã«Rustã³ãŒããå®è¡ããŠããŸããããã«ãããäŸãã°ã¹ã¿ãã¯ã¡ã¢ãªãreadãããwriteãããããããšã«ãªããŸãããããïŒ
- MMUãšãã£ãã·ã¥ãæå¹åããŠããªããšãã¢ã©ã€ã³ãããŠããªãã¢ã¯ã»ã¹ã¯ãã©ãŒã«ããåŒãèµ·ãããŸãããã®ãã¥ãŒããªã¢ã«ã§ã¯ãã³ã³ãã€ã©ãã¢ã©ã€ã³ãããŠããªãã¢ã¯ã»ã¹ãçæããªã
+strict-align
ãªãã·ã§ã³ãã»ããããaarch64-unknown-none
ãã¿ãŒã²ãããšããŠãã«ãããŠããã®ã§å€§äžå€«ãªã¯ãã§ãããäžè¬çã«ã¯å€§äžå€«ãšã¯éããŸããã - ããVMäžã§å®è¡ããŠãããšãããšããã£ãã·ã¥ã³ããŒã¬ã³ã·ãŒã®åé¡ãèµ·ããããšããããŸããåé¡ãªã®ã¯VMããã£ãã·ã¥ãç¡å¹åãããŸãŸçŽæ¥ã¡ã¢ãªã«ã¢ã¯ã»ã¹ããŠããã®ã«å¯Ÿãããã¹ãã¯åãã¡ã¢ãªã«å¯ŸããŠãã£ãã·ã¥å¯èœãªãšã€ãªã¢ã¹ãæã£ãŠããŸããšããããšã§ãããã¹ããä»®ã«æ瀺çã«ã¡ã¢ãªã«ã¢ã¯ã»ã¹ããªããšããŠããææ©çãªã¢ã¯ã»ã¹ã«ãããã£ãã·ã¥ãã£ã«ãèµ·ããããšãããããããªããšVMããã¹ãã®ã©ã¡ããã«ããå€æŽã倱ãããŠããŸããŸãããã®ïŒãã€ããŒãã€ã¶ãªãã§çŽæ¥ããŒããŠã§ã¢ã§å®è¡ããïŒå Žåã«ã¯åé¡ã«ã¯ãªããŸããããäžè¬çã«ã¯è¯ããªããã¿ãŒã³ã§ãã
- MMUãšãã£ãã·ã¥ãæå¹åããŠããªããšãã¢ã©ã€ã³ãããŠããªãã¢ã¯ã»ã¹ã¯ãã©ãŒã«ããåŒãèµ·ãããŸãããã®ãã¥ãŒããªã¢ã«ã§ã¯ãã³ã³ãã€ã©ãã¢ã©ã€ã³ãããŠããªãã¢ã¯ã»ã¹ãçæããªã
䟿å©ã¯ã¬ãŒã
ãã¢ã¡ã¿ã«ããã°ã©ãã³ã°ã«ãããŠå ±éã«çºçããåé¡ã«å¯Ÿãã解ãäžããã¯ã¬ãŒãã«ã€ããŠããã€ã玹ä»ããŸãã
zerocopy
ïŒFuchsiaã®ïŒzerocopy
ã¯ã¬ãŒãã¯ãã€ãã·ãŒã±ã³ã¹ãšãã®ä»ã®åã®å€æãå®å
šã«è¡ãããã®ãã¬ã€ãããã¯ããæäŸããŸãã
use zerocopy::{Immutable, IntoBytes}; #[repr(u32)] #[derive(Debug, Default, Immutable, IntoBytes)] enum RequestType { #[default] In = 0, Out = 1, Flush = 4, } #[repr(C)] #[derive(Debug, Default, Immutable, IntoBytes)] struct VirtioBlockRequest { request_type: RequestType, reserved: u32, sector: u64, } fn main() { let request = VirtioBlockRequest { request_type: RequestType::Flush, sector: 42, ..Default::default() }; assert_eq!( request.as_bytes(), &[4, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0] ); }
ããã¯ïŒvolatile readãwriteã䜿çšããŠããªãããïŒMMIOã«ã¯é©ããŠãŸããããäŸãã°DMAã®ãããªããŒããŠã§ã¢ãšå ±æããããŒã¿æ§é ãããã¯å€éšã€ã³ã¿ãã§ãŒã¹ãéããŠéä¿¡ããããŒã¿æ§é ãæ±ãã«å Žåã«ã¯æçšã§ãã
FromBytes
ã¯ãããªããã€ããã¿ãŒã³ãæå¹ãªå€ãšãªãåã«å¯ŸããŠå®è£ ããããšãã§ããä¿¡çšã§ããªããã€ãã·ãŒã±ã³ã¹ããã®å®å šãªå€æãå¯èœã«ããŸããRequestType
ã¯u32åã®ãã¹ãŠã®å€ãæå¹ãªenumå€ãšããŠå®çŸ©ããŠããªãã®ã§ããã¹ãŠã®ãã€ããã¿ãŒã³ãæå¹ãšã¯ãªããããããã«å¯ŸããFromBytes
ã®å°åºã¯ãã§ãŒã«ããã§ããããzerocopy::byteorder
ã¯ãã€ããªãŒããæ°ã«ããæ°å€ããªããã£ãã«é¢ããåãæäŸããŸãã- ãã®äŸã
src/bare-metal/useful-crates/zerocopy-example/
ã«ãããŠcargo run
ãšãšããããšã§å®è¡ããŠã¿ãŸããããïŒPlaygroundã§ã¯ãã®äŸãäŸåããã¯ã¬ãŒããå©çšã§ããªãããå®è¡ã§ããŸããïŒ
aarch64-paging
aarch64-paging
ã¯ã¬ãŒãã¯AArch64ä»®æ³ã¡ã¢ãªã·ã¹ãã ã¢ãŒããã¯ãã£ã«åã£ãããŒãžããŒãã«ã®çæãå¯èœã«ããŸãã
use aarch64_paging::{ idmap::IdMap, paging::{Attributes, MemoryRegion}, }; const ASID: usize = 1; const ROOT_LEVEL: usize = 1; // ä»®æ³ç©çåäžãšãªãæ°ããããŒãžããŒãã«ãäœæããŸãã let mut idmap = IdMap::new(ASID, ROOT_LEVEL); // 2 MiB ã®ã¡ã¢ãªé åãèªã¿åãå°çšãšããŠãããã³ã°ããŸãã idmap.map_range( &MemoryRegion::new(0x80200000, 0x80400000), Attributes::NORMAL | Attributes::NON_GLOBAL | Attributes::READ_ONLY, ).unwrap(); // `TTBR0_EL1` ãèšå®ããŠããŒãžããŒãã«ãæå¹ã«ããŸãã idmap.activate();
- çŸæç¹ã§ã¯EL1ãããµããŒããããŠããŸããããä»ã®äŸå€ã¬ãã«ã®ãµããŒããç°¡åã«è¿œå ã§ããã¯ãã§ãã
- ããã¯Androidã§Protected VM Firmwareã®ããã«å©çšãããŠããŸãã
- ãã®äŸã¯æ¬ç©ã®ããŒããŠã§ã¢ãQEMUãå¿ èŠãšããã®ã§ãç°¡åã«ã¯å®è¡ã§ããŸããã
buddy_system_allocator
buddy_system_allocator
ã¯ãµãŒãããŒãã£ã®ã¯ã¬ãŒãã§ãåºæ¬çãªããã£ã·ã¹ãã ã¢ããŒã±ãŒã¿ãå®è£
ããŠããŸãããã®ã¯ã¬ãŒãã¯GlobalAlloc
ãå®è£
ãã LockedHeap
ã«ããïŒ ä»¥åèŠãããã«ïŒæšæºã®alloc
ã¯ã¬ãŒããå©çšå¯èœã«ããããã«äœ¿ããŸãããå¥ã®ã¢ãã¬ã¹ç©ºéãã¢ãã±ãŒãããããã«ã䜿ããŸããäŸãã°ãPCI BARã«å¯ŸããMMIOé åãã¢ãã±ãŒããããå Žåã«ã¯ä»¥äžã®ããã«ã§ããŸãïŒ
use buddy_system_allocator::FrameAllocator; use core::alloc::Layout; fn main() { let mut allocator = FrameAllocator::<32>::new(); allocator.add_frame(0x200_0000, 0x400_0000); let layout = Layout::from_size_align(0x100, 0x100).unwrap(); let bar = allocator .alloc_aligned(layout) .expect("Failed to allocate 0x100 byte MMIO region"); println!("Allocated 0x100 byte MMIO region at {:#x}", bar); }
- PCI BARã¯åžžã«ãµã€ãºãšåãã¢ã©ã€ã³ã«ãªããŸãã
- ãã®äŸã
src/bare-metal/useful-crates/allocator-example/
ã«ãããŠcargo run
ãšããããšã§å®è¡ããŠã¿ãŸããããïŒPlaygroundã§ã¯ãã®äŸãäŸåããã¯ã¬ãŒããå©çšã§ããªãããå®è¡ã§ããŸããïŒ
tinyvec
æã«ã¯Vec
ã®ããã«ãªãµã€ãºã§ããé åãããŒãã䜿ããã«ç¢ºä¿ããããšæãããšããããŸããtinyvec
ã¯éçã«ç¢ºä¿ããŸãã¯ã¹ã¿ãã¯äžã«ç¢ºä¿ããé
åãŸãã¯ã¹ã©ã€ã¹ãå²åœé åãšãããã¯ã¿ãæäŸããŸãããã®å®è£
ã§ã¯ãããã€ã®èŠçŽ ã䜿ãããŠãããã管çããã確ä¿ããã以äžã«äœ¿ãããšãããšãããã¯ããŸãã
use tinyvec::{array_vec, ArrayVec}; fn main() { let mut numbers: ArrayVec<[u32; 5]> = array_vec!(42, 66); println!("{numbers:?}"); numbers.push(7); println!("{numbers:?}"); numbers.remove(1); println!("{numbers:?}"); }
tinyvec
ã¯åæåã®ããã«èŠçŽ ãšãªãã¿ã€ããDefault
ãå®è£ ããããšãå¿ èŠãšããŸãã- Rust Playgroundã¯
tinyvec
ãå å ããŠããã®ã§ããªã³ã©ã€ã³ã§ãã®äŸãå®è¡ããããšãã§ããŸãã
spin
std::sync
ãæäŸããstd::sync::Mutex
ãšãã®ä»ã®åæããªããã£ãã¯core
ãŸãã¯alloc
ã§ã¯å©çšã§ããŸããããšãªããšãäŸãã°ç°ãªãCPUéã§ã®ç¶æ
å
±æã®ããã®ãåæãå
éšå¯å€æ§ã¯ã©ã®ããã«å®çŸãããè¯ãã®ã§ããããïŒ
spin
ã¯ã¬ãŒãã¯ãããã®å€ãã®ããªããã£ããšç䟡ãªã¹ãã³ããã¯ããŒã¹ã®ãã®ãæäŸããŸãã
use spin::mutex::SpinMutex; static counter: SpinMutex<u32> = SpinMutex::new(0); fn main() { println!("count: {}", counter.lock()); *counter.lock() += 2; println!("count: {}", counter.lock()); }
- å²ã蟌ã¿ãã³ãã©ã§ããã¯ãååŸããå Žåã«ã¯ãããããã¯ãåŒãèµ·ãããªãããã«æ°ãã€ããŠãã ããã
spin
also has a ticket lock mutex implementation; equivalents ofRwLock
,Barrier
andOnce
fromstd::sync
; andLazy
for lazy initialisation.once_cell
ã¯ã¬ãŒããspin::once::Once
ãšã¯å°ãç°ãªãã¢ãããŒãã®é 延åæåã®ããã®æçšãªåãããã€ãæã£ãŠããŸãã- Rust Playgroundã¯
spin
ãå å ããŠããã®ã§ããã®äŸã¯ãªã³ã©ã€ã³ã§å®è¡ã§ããŸãã
Androidäžã®ãã¢ã¡ã¿ã«
AOSPã«ãããŠãã¢ã¡ã¿ã«Rustãã€ããªããã«ãããããã«ã¯ãRustã³ãŒãããã«ãããããã®rust_ffi_static
ãšããSoongã«ãŒã«ããªã³ã«ã¹ã¯ãªãããšããã䜿ã£ãŠãã€ããªãçæããããã®cc_binary
ãšããã«ãŒã«ãããã«ELFãå®è¡å¯èœãªåœ¢åŒã®çãã€ããªã«å€æããraw_binary
ãšããã«ãŒã«ãå¿
èŠã§ãã
rust_ffi_static {
name: "libvmbase_example",
defaults: ["vmbase_ffi_defaults"],
crate_name: "vmbase_example",
srcs: ["src/main.rs"],
rustlibs: [
"libvmbase",
],
}
cc_binary {
name: "vmbase_example",
defaults: ["vmbase_elf_defaults"],
srcs: [
"idmap.S",
],
static_libs: [
"libvmbase_example",
],
linker_scripts: [
"image.ld",
":vmbase_sections",
],
}
raw_binary {
name: "vmbase_example_bin",
stem: "vmbase_example.bin",
src: ":vmbase_example",
enabled: false,
target: {
android_arm64: {
enabled: true,
},
},
}
vmbase
For VMs running under crosvm on aarch64, the vmbase library provides a linker script and useful defaults for the build rules, along with an entry point, UART console logging and more.
#![no_main] #![no_std] use vmbase::{main, println}; main!(main); pub fn main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) { println!("Hello world"); }
main!
ãšãããã¯ãã¯ã¡ã€ã³é¢æ°ãæå®ãããã®ã§ãæå®ãããé¢æ°ã¯vmbase
ã®ãšã³ããªãã€ã³ãããåŒã³åºãããããšã«ãªããŸããvmbase
ã®ãšã³ããªãã€ã³ãã¯ã³ã³ãœãŒã«ã®åæåãè¡ããã¡ã€ã³é¢æ°ããªã¿ãŒã³ããå Žåã«ã¯PSCI_SYSTEM_OFF ãçºè¡ãVMãã·ã£ããããŠã³ããŸãã
ç·Žç¿åé¡
PL031 ãªã¢ã«ã¿ã€ã ã¯ãã㯠ããã€ã¹çšã®ãã©ã€ããäœæããŸãã
æŒç¿ã®çµäºåŸã¯ãæäŸãããŠãã ãœãªã¥ãŒã·ã§ã³ ã確èªããŠãã ããã
RTC ãã©ã€ã
QEMU aarch64 virt ãã·ã³ã® 0x9010000 ã«ã¯ãPL031 ãªã¢ã«ã¿ã€ã ã¯ããã¯ãæèŒãããŠããŸãããã®æŒç¿ã§ã¯ããã®ãã©ã€ããäœæããå¿ èŠããããŸãã
- ããã䜿çšããŠçŸåšã®æå»ãã·ãªã¢ã« ã³ã³ãœãŒã«ã«åºåããŸããæ¥æã®åœ¢åŒã«ã¯
chrono
ã¯ã¬ãŒãã䜿çšã§ããŸãã - äžèŽã¬ãžã¹ã¿ãšæªå å·¥ã®å²ã蟌ã¿ã¹ããŒã¿ã¹ã䜿çšããŠãæå®æå»ïŒããšãã° 3 ç§åŸïŒãŸã§ããžãŒãŠã§ã€ãããŸãïŒã«ãŒãå
ã§
core::hint::spin_loop
ãåŒã³åºããŸãïŒã - æéãããå Žåã¯ãRTC ã®äžèŽã«ãã£ãŠçæãããå²ã蟌ã¿ãæå¹ã«ããŠåŠçããŸãã
arm-gic
ã¯ã¬ãŒãã§æäŸãããŠãããã©ã€ãã䜿çšããŠãArm æ±çšå²ã蟌ã¿ã³ã³ãããŒã©ãèšå®ããŠæ§ããŸããã- RTC å²ã蟌ã¿ã䜿çšããŸãããã®å²ã蟌ã¿ã¯ GIC ã«
IntId::spi(2)
ãšããŠæ¥ç¶ãããŠããŸãã - å²ã蟌ã¿ãæå¹ã«ãããšã
arm_gic::wfi()
ã䜿çšããŠã³ã¢ãã¹ãªãŒããããããšãã§ããŸããããã«ãããã³ã¢ã¯å²ã蟌ã¿ãåãããŸã§ã¹ãªãŒãç¶æ ã«ãªããŸãã
- RTC å²ã蟌ã¿ã䜿çšããŸãããã®å²ã蟌ã¿ã¯ GIC ã«
æŒç¿ãã³ãã¬ãŒã ãããŠã³ããŒãããrtc
ãã£ã¬ã¯ããªã§ä»¥äžã®ãã¡ã€ã«ãæ¢ããŸãã
src/main.rs:
#![no_main] #![no_std] mod exceptions; mod logger; mod pl011; use crate::pl011::Uart; use arm_gic::gicv3::GicV3; use core::panic::PanicInfo; use log::{error, info, trace, LevelFilter}; use smccc::psci::system_off; use smccc::Hvc; /// Base addresses of the GICv3. const GICD_BASE_ADDRESS: *mut u64 = 0x800_0000 as _; const GICR_BASE_ADDRESS: *mut u64 = 0x80A_0000 as _; /// Base address of the primary PL011 UART. const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _; // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) { // SAFETY: `PL011_BASE_ADDRESS` is the base address of a PL011 device, and // nothing else accesses that address range. let uart = unsafe { Uart::new(PL011_BASE_ADDRESS) }; logger::init(uart, LevelFilter::Trace).unwrap(); info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3); // SAFETY: `GICD_BASE_ADDRESS` and `GICR_BASE_ADDRESS` are the base // addresses of a GICv3 distributor and redistributor respectively, and // nothing else accesses those address ranges. let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, GICR_BASE_ADDRESS) }; gic.setup(); // TODO: Create instance of RTC driver and print current time. // TODO: Wait for 3 seconds. system_off::<Hvc>().unwrap(); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { error!("{info}"); system_off::<Hvc>().unwrap(); loop {} }
src/exceptions.rsïŒãã®æŒç¿ã® 3 çªç®ã®ããŒãã§ã®ã¿å€æŽããå¿ èŠããããŸãïŒ:
#![allow(unused)] fn main() { // Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use arm_gic::gicv3::GicV3; use log::{error, info, trace}; use smccc::psci::system_off; use smccc::Hvc; // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) { error!("sync_exception_current"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn irq_current(_elr: u64, _spsr: u64) { trace!("irq_current"); let intid = GicV3::get_and_acknowledge_interrupt().expect("No pending interrupt"); info!("IRQ {intid:?}"); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn fiq_current(_elr: u64, _spsr: u64) { error!("fiq_current"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn serr_current(_elr: u64, _spsr: u64) { error!("serr_current"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn sync_lower(_elr: u64, _spsr: u64) { error!("sync_lower"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn irq_lower(_elr: u64, _spsr: u64) { error!("irq_lower"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn fiq_lower(_elr: u64, _spsr: u64) { error!("fiq_lower"); system_off::<Hvc>().unwrap(); } // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn serr_lower(_elr: u64, _spsr: u64) { error!("serr_lower"); system_off::<Hvc>().unwrap(); } }
src/logger.rsïŒå€æŽããå¿ èŠã¯ãããŸããïŒ:
#![allow(unused)] fn main() { // Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ANCHOR: main use crate::pl011::Uart; use core::fmt::Write; use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; use spin::mutex::SpinMutex; static LOGGER: Logger = Logger { uart: SpinMutex::new(None) }; struct Logger { uart: SpinMutex<Option<Uart>>, } impl Log for Logger { fn enabled(&self, _metadata: &Metadata) -> bool { true } fn log(&self, record: &Record) { writeln!( self.uart.lock().as_mut().unwrap(), "[{}] {}", record.level(), record.args() ) .unwrap(); } fn flush(&self) {} } /// Initialises UART logger. pub fn init(uart: Uart, max_level: LevelFilter) -> Result<(), SetLoggerError> { LOGGER.uart.lock().replace(uart); log::set_logger(&LOGGER)?; log::set_max_level(max_level); Ok(()) } }
src/pl011.rsïŒå€æŽããå¿ èŠã¯ãããŸããïŒ:
#![allow(unused)] fn main() { // Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #![allow(unused)] use core::fmt::{self, Write}; // ANCHOR: Flags use bitflags::bitflags; bitflags! { /// Flags from the UART flag register. #[repr(transparent)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct Flags: u16 { /// Clear to send. const CTS = 1 << 0; /// Data set ready. const DSR = 1 << 1; /// Data carrier detect. const DCD = 1 << 2; /// UART busy transmitting data. const BUSY = 1 << 3; /// Receive FIFO is empty. const RXFE = 1 << 4; /// Transmit FIFO is full. const TXFF = 1 << 5; /// Receive FIFO is full. const RXFF = 1 << 6; /// Transmit FIFO is empty. const TXFE = 1 << 7; /// Ring indicator. const RI = 1 << 8; } } // ANCHOR_END: Flags bitflags! { /// Flags from the UART Receive Status Register / Error Clear Register. #[repr(transparent)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct ReceiveStatus: u16 { /// Framing error. const FE = 1 << 0; /// Parity error. const PE = 1 << 1; /// Break error. const BE = 1 << 2; /// Overrun error. const OE = 1 << 3; } } // ANCHOR: Registers #[repr(C, align(4))] struct Registers { dr: u16, _reserved0: [u8; 2], rsr: ReceiveStatus, _reserved1: [u8; 19], fr: Flags, _reserved2: [u8; 6], ilpr: u8, _reserved3: [u8; 3], ibrd: u16, _reserved4: [u8; 2], fbrd: u8, _reserved5: [u8; 3], lcr_h: u8, _reserved6: [u8; 3], cr: u16, _reserved7: [u8; 3], ifls: u8, _reserved8: [u8; 3], imsc: u16, _reserved9: [u8; 2], ris: u16, _reserved10: [u8; 2], mis: u16, _reserved11: [u8; 2], icr: u16, _reserved12: [u8; 2], dmacr: u8, _reserved13: [u8; 3], } // ANCHOR_END: Registers // ANCHOR: Uart /// Driver for a PL011 UART. #[derive(Debug)] pub struct Uart { registers: *mut Registers, } impl Uart { /// Constructs a new instance of the UART driver for a PL011 device at the /// given base address. /// /// # Safety /// /// The given base address must point to the MMIO control registers of a /// PL011 device, which must be mapped into the address space of the process /// as device memory and not have any other aliases. pub unsafe fn new(base_address: *mut u32) -> Self { Self { registers: base_address as *mut Registers } } /// Writes a single byte to the UART. pub fn write_byte(&self, byte: u8) { // Wait until there is room in the TX buffer. while self.read_flag_register().contains(Flags::TXFF) {} // SAFETY: We know that self.registers points to the control registers // of a PL011 device which is appropriately mapped. unsafe { // Write to the TX buffer. (&raw mut (*self.registers).dr).write_volatile(byte.into()); } // Wait until the UART is no longer busy. while self.read_flag_register().contains(Flags::BUSY) {} } /// Reads and returns a pending byte, or `None` if nothing has been /// received. pub fn read_byte(&self) -> Option<u8> { if self.read_flag_register().contains(Flags::RXFE) { None } else { // SAFETY: We know that self.registers points to the control // registers of a PL011 device which is appropriately mapped. let data = unsafe { (&raw const (*self.registers).dr).read_volatile() }; // TODO: Check for error conditions in bits 8-11. Some(data as u8) } } fn read_flag_register(&self) -> Flags { // SAFETY: We know that self.registers points to the control registers // of a PL011 device which is appropriately mapped. unsafe { (&raw const (*self.registers).fr).read_volatile() } } } // ANCHOR_END: Uart impl Write for Uart { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.as_bytes() { self.write_byte(*c); } Ok(()) } } // Safe because it just contains a pointer to device memory, which can be // accessed from any context. unsafe impl Send for Uart {} }
Cargo.toml (å€æŽã¯äžèŠãªã¯ãã§ã):
[workspace]
[package]
name = "rtc"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
arm-gic = "0.1.1"
bitflags = "2.6.0"
chrono = { version = "0.4.38", default-features = false }
log = "0.4.22"
smccc = "0.1.1"
spin = "0.9.8"
[build-dependencies]
cc = "1.1.31"
build.rsïŒå€æŽããå¿ èŠã¯ãããŸããïŒ:
// Copyright 2023 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use cc::Build; use std::env; fn main() { env::set_var("CROSS_COMPILE", "aarch64-none-elf"); env::set_var("CC", "clang"); Build::new() .file("entry.S") .file("exceptions.S") .file("idmap.S") .compile("empty") }
entry.SïŒå€æŽããå¿ èŠã¯ãããŸããïŒ:
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.macro adr_l, reg:req, sym:req
adrp \reg, \sym
add \reg, \reg, :lo12:\sym
.endm
.macro mov_i, reg:req, imm:req
movz \reg, :abs_g3:\imm
movk \reg, :abs_g2_nc:\imm
movk \reg, :abs_g1_nc:\imm
movk \reg, :abs_g0_nc:\imm
.endm
.set .L_MAIR_DEV_nGnRE, 0x04
.set .L_MAIR_MEM_WBWA, 0xff
.set .Lmairval, .L_MAIR_DEV_nGnRE | (.L_MAIR_MEM_WBWA << 8)
/* 4 KiB granule size for TTBR0_EL1. */
.set .L_TCR_TG0_4KB, 0x0 << 14
/* 4 KiB granule size for TTBR1_EL1. */
.set .L_TCR_TG1_4KB, 0x2 << 30
/* Disable translation table walk for TTBR1_EL1, generating a translation fault instead. */
.set .L_TCR_EPD1, 0x1 << 23
/* Translation table walks for TTBR0_EL1 are inner sharable. */
.set .L_TCR_SH_INNER, 0x3 << 12
/*
* Translation table walks for TTBR0_EL1 are outer write-back read-allocate write-allocate
* cacheable.
*/
.set .L_TCR_RGN_OWB, 0x1 << 10
/*
* Translation table walks for TTBR0_EL1 are inner write-back read-allocate write-allocate
* cacheable.
*/
.set .L_TCR_RGN_IWB, 0x1 << 8
/* Size offset for TTBR0_EL1 is 2**39 bytes (512 GiB). */
.set .L_TCR_T0SZ_512, 64 - 39
.set .Ltcrval, .L_TCR_TG0_4KB | .L_TCR_TG1_4KB | .L_TCR_EPD1 | .L_TCR_RGN_OWB
.set .Ltcrval, .Ltcrval | .L_TCR_RGN_IWB | .L_TCR_SH_INNER | .L_TCR_T0SZ_512
/* Stage 1 instruction access cacheability is unaffected. */
.set .L_SCTLR_ELx_I, 0x1 << 12
/* SP alignment fault if SP is not aligned to a 16 byte boundary. */
.set .L_SCTLR_ELx_SA, 0x1 << 3
/* Stage 1 data access cacheability is unaffected. */
.set .L_SCTLR_ELx_C, 0x1 << 2
/* EL0 and EL1 stage 1 MMU enabled. */
.set .L_SCTLR_ELx_M, 0x1 << 0
/* Privileged Access Never is unchanged on taking an exception to EL1. */
.set .L_SCTLR_EL1_SPAN, 0x1 << 23
/* SETEND instruction disabled at EL0 in aarch32 mode. */
.set .L_SCTLR_EL1_SED, 0x1 << 8
/* Various IT instructions are disabled at EL0 in aarch32 mode. */
.set .L_SCTLR_EL1_ITD, 0x1 << 7
.set .L_SCTLR_EL1_RES1, (0x1 << 11) | (0x1 << 20) | (0x1 << 22) | (0x1 << 28) | (0x1 << 29)
.set .Lsctlrval, .L_SCTLR_ELx_M | .L_SCTLR_ELx_C | .L_SCTLR_ELx_SA | .L_SCTLR_EL1_ITD | .L_SCTLR_EL1_SED
.set .Lsctlrval, .Lsctlrval | .L_SCTLR_ELx_I | .L_SCTLR_EL1_SPAN | .L_SCTLR_EL1_RES1
/**
* This is a generic entry point for an image. It carries out the operations required to prepare the
* loaded image to be run. Specifically, it zeroes the bss section using registers x25 and above,
* prepares the stack, enables floating point, and sets up the exception vector. It preserves x0-x3
* for the Rust entry point, as these may contain boot parameters.
*/
.section .init.entry, "ax"
.global entry
entry:
/* Load and apply the memory management configuration, ready to enable MMU and caches. */
adrp x30, idmap
msr ttbr0_el1, x30
mov_i x30, .Lmairval
msr mair_el1, x30
mov_i x30, .Ltcrval
/* Copy the supported PA range into TCR_EL1.IPS. */
mrs x29, id_aa64mmfr0_el1
bfi x30, x29, #32, #4
msr tcr_el1, x30
mov_i x30, .Lsctlrval
/*
* Ensure everything before this point has completed, then invalidate any potentially stale
* local TLB entries before they start being used.
*/
isb
tlbi vmalle1
ic iallu
dsb nsh
isb
/*
* Configure sctlr_el1 to enable MMU and cache and don't proceed until this has completed.
*/
msr sctlr_el1, x30
isb
/* Disable trapping floating point access in EL1. */
mrs x30, cpacr_el1
orr x30, x30, #(0x3 << 20)
msr cpacr_el1, x30
isb
/* Zero out the bss section. */
adr_l x29, bss_begin
adr_l x30, bss_end
0: cmp x29, x30
b.hs 1f
stp xzr, xzr, [x29], #16
b 0b
1: /* Prepare the stack. */
adr_l x30, boot_stack_end
mov sp, x30
/* Set up exception vector. */
adr x30, vector_table_el1
msr vbar_el1, x30
/* Call into Rust code. */
bl main
/* Loop forever waiting for interrupts. */
2: wfi
b 2b
exceptions.SïŒå€æŽããå¿ èŠã¯ãããŸããïŒ:
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Saves the volatile registers onto the stack. This currently takes 14
* instructions, so it can be used in exception handlers with 18 instructions
* left.
*
* On return, x0 and x1 are initialised to elr_el2 and spsr_el2 respectively,
* which can be used as the first and second arguments of a subsequent call.
*/
.macro save_volatile_to_stack
/* Reserve stack space and save registers x0-x18, x29 & x30. */
stp x0, x1, [sp, #-(8 * 24)]!
stp x2, x3, [sp, #8 * 2]
stp x4, x5, [sp, #8 * 4]
stp x6, x7, [sp, #8 * 6]
stp x8, x9, [sp, #8 * 8]
stp x10, x11, [sp, #8 * 10]
stp x12, x13, [sp, #8 * 12]
stp x14, x15, [sp, #8 * 14]
stp x16, x17, [sp, #8 * 16]
str x18, [sp, #8 * 18]
stp x29, x30, [sp, #8 * 20]
/*
* Save elr_el1 & spsr_el1. This such that we can take nested exception
* and still be able to unwind.
*/
mrs x0, elr_el1
mrs x1, spsr_el1
stp x0, x1, [sp, #8 * 22]
.endm
/**
* Restores the volatile registers from the stack. This currently takes 14
* instructions, so it can be used in exception handlers while still leaving 18
* instructions left; if paired with save_volatile_to_stack, there are 4
* instructions to spare.
*/
.macro restore_volatile_from_stack
/* Restore registers x2-x18, x29 & x30. */
ldp x2, x3, [sp, #8 * 2]
ldp x4, x5, [sp, #8 * 4]
ldp x6, x7, [sp, #8 * 6]
ldp x8, x9, [sp, #8 * 8]
ldp x10, x11, [sp, #8 * 10]
ldp x12, x13, [sp, #8 * 12]
ldp x14, x15, [sp, #8 * 14]
ldp x16, x17, [sp, #8 * 16]
ldr x18, [sp, #8 * 18]
ldp x29, x30, [sp, #8 * 20]
/* Restore registers elr_el1 & spsr_el1, using x0 & x1 as scratch. */
ldp x0, x1, [sp, #8 * 22]
msr elr_el1, x0
msr spsr_el1, x1
/* Restore x0 & x1, and release stack space. */
ldp x0, x1, [sp], #8 * 24
.endm
/**
* This is a generic handler for exceptions taken at the current EL while using
* SP0. It behaves similarly to the SPx case by first switching to SPx, doing
* the work, then switching back to SP0 before returning.
*
* Switching to SPx and calling the Rust handler takes 16 instructions. To
* restore and return we need an additional 16 instructions, so we can implement
* the whole handler within the allotted 32 instructions.
*/
.macro current_exception_sp0 handler:req
msr spsel, #1
save_volatile_to_stack
bl \handler
restore_volatile_from_stack
msr spsel, #0
eret
.endm
/**
* This is a generic handler for exceptions taken at the current EL while using
* SPx. It saves volatile registers, calls the Rust handler, restores volatile
* registers, then returns.
*
* This also works for exceptions taken from EL0, if we don't care about
* non-volatile registers.
*
* Saving state and jumping to the Rust handler takes 15 instructions, and
* restoring and returning also takes 15 instructions, so we can fit the whole
* handler in 30 instructions, under the limit of 32.
*/
.macro current_exception_spx handler:req
save_volatile_to_stack
bl \handler
restore_volatile_from_stack
eret
.endm
.section .text.vector_table_el1, "ax"
.global vector_table_el1
.balign 0x800
vector_table_el1:
sync_cur_sp0:
current_exception_sp0 sync_exception_current
.balign 0x80
irq_cur_sp0:
current_exception_sp0 irq_current
.balign 0x80
fiq_cur_sp0:
current_exception_sp0 fiq_current
.balign 0x80
serr_cur_sp0:
current_exception_sp0 serr_current
.balign 0x80
sync_cur_spx:
current_exception_spx sync_exception_current
.balign 0x80
irq_cur_spx:
current_exception_spx irq_current
.balign 0x80
fiq_cur_spx:
current_exception_spx fiq_current
.balign 0x80
serr_cur_spx:
current_exception_spx serr_current
.balign 0x80
sync_lower_64:
current_exception_spx sync_lower
.balign 0x80
irq_lower_64:
current_exception_spx irq_lower
.balign 0x80
fiq_lower_64:
current_exception_spx fiq_lower
.balign 0x80
serr_lower_64:
current_exception_spx serr_lower
.balign 0x80
sync_lower_32:
current_exception_spx sync_lower
.balign 0x80
irq_lower_32:
current_exception_spx irq_lower
.balign 0x80
fiq_lower_32:
current_exception_spx fiq_lower
.balign 0x80
serr_lower_32:
current_exception_spx serr_lower
idmap.S (you shouldn't need to change this):
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.set .L_TT_TYPE_BLOCK, 0x1
.set .L_TT_TYPE_PAGE, 0x3
.set .L_TT_TYPE_TABLE, 0x3
/* Access flag. */
.set .L_TT_AF, 0x1 << 10
/* Not global. */
.set .L_TT_NG, 0x1 << 11
.set .L_TT_XN, 0x3 << 53
.set .L_TT_MT_DEV, 0x0 << 2 // MAIR #0 (DEV_nGnRE)
.set .L_TT_MT_MEM, (0x1 << 2) | (0x3 << 8) // MAIR #1 (MEM_WBWA), inner shareable
.set .L_BLOCK_DEV, .L_TT_TYPE_BLOCK | .L_TT_MT_DEV | .L_TT_AF | .L_TT_XN
.set .L_BLOCK_MEM, .L_TT_TYPE_BLOCK | .L_TT_MT_MEM | .L_TT_AF | .L_TT_NG
.section ".rodata.idmap", "a", %progbits
.global idmap
.align 12
idmap:
/* level 1 */
.quad .L_BLOCK_DEV | 0x0 // 1 GiB of device mappings
.quad .L_BLOCK_MEM | 0x40000000 // 1 GiB of DRAM
.fill 254, 8, 0x0 // 254 GiB of unmapped VA space
.quad .L_BLOCK_DEV | 0x4000000000 // 1 GiB of device mappings
.fill 255, 8, 0x0 // 255 GiB of remaining VA space
image.ldïŒå€æŽããå¿ èŠã¯ãããŸããïŒ:
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Code will start running at this symbol which is placed at the start of the
* image.
*/
ENTRY(entry)
MEMORY
{
image : ORIGIN = 0x40080000, LENGTH = 2M
}
SECTIONS
{
/*
* Collect together the code.
*/
.init : ALIGN(4096) {
text_begin = .;
*(.init.entry)
*(.init.*)
} >image
.text : {
*(.text.*)
} >image
text_end = .;
/*
* Collect together read-only data.
*/
.rodata : ALIGN(4096) {
rodata_begin = .;
*(.rodata.*)
} >image
.got : {
*(.got)
} >image
rodata_end = .;
/*
* Collect together the read-write data including .bss at the end which
* will be zero'd by the entry code.
*/
.data : ALIGN(4096) {
data_begin = .;
*(.data.*)
/*
* The entry point code assumes that .data is a multiple of 32
* bytes long.
*/
. = ALIGN(32);
data_end = .;
} >image
/* Everything beyond this point will not be included in the binary. */
bin_end = .;
/* The entry point code assumes that .bss is 16-byte aligned. */
.bss : ALIGN(16) {
bss_begin = .;
*(.bss.*)
*(COMMON)
. = ALIGN(16);
bss_end = .;
} >image
.stack (NOLOAD) : ALIGN(4096) {
boot_stack_begin = .;
. += 40 * 4096;
. = ALIGN(4096);
boot_stack_end = .;
} >image
. = ALIGN(4K);
PROVIDE(dma_region = .);
/*
* Remove unused sections from the image.
*/
/DISCARD/ : {
/* The image loads itself so doesn't need these sections. */
*(.gnu.hash)
*(.hash)
*(.interp)
*(.eh_frame_hdr)
*(.eh_frame)
*(.note.gnu.build-id)
}
}
MakefileïŒå€æŽããå¿ èŠã¯ãããŸããïŒ:
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
.PHONY: build qemu_minimal qemu qemu_logger
all: rtc.bin
build:
cargo build
rtc.bin: build
cargo objcopy -- -O binary $@
qemu: rtc.bin
qemu-system-aarch64 -machine virt,gic-version=3 -cpu max -serial mon:stdio -display none -kernel $< -s
clean:
cargo clean
rm -f *.bin
.cargo/config.toml (å€æŽã¯äžèŠãªã¯ãã§ã):
[build]
target = "aarch64-unknown-none"
rustflags = ["-C", "link-arg=-Timage.ld"]
make qemu
ã«ããQEMU ã§ã³ãŒããå®è¡ããŸãã
ãã¢ã¡ã¿ã«Rust PM
RTC ãã©ã€ã
ïŒæŒç¿ã«æ»ãïŒ
main.rs:
#![no_main] #![no_std] mod exceptions; mod logger; mod pl011; mod pl031; use crate::pl031::Rtc; use arm_gic::gicv3::{IntId, Trigger}; use arm_gic::{irq_enable, wfi}; use chrono::{TimeZone, Utc}; use core::hint::spin_loop; use crate::pl011::Uart; use arm_gic::gicv3::GicV3; use core::panic::PanicInfo; use log::{error, info, trace, LevelFilter}; use smccc::psci::system_off; use smccc::Hvc; /// GICv3 ã®ããŒã¹ã¢ãã¬ã¹ã const GICD_BASE_ADDRESS: *mut u64 = 0x800_0000 as _; const GICR_BASE_ADDRESS: *mut u64 = 0x80A_0000 as _; /// ãã©ã€ã㪠PL011 UART ã®ããŒã¹ã¢ãã¬ã¹ã const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _; /// PL031 RTC ã®ããŒã¹ã¢ãã¬ã¹ã const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _; /// PL031 RTC ã䜿çšãã IRQã const PL031_IRQ: IntId = IntId::spi(2); // SAFETY: There is no other global function of this name. #[unsafe(no_mangle)] extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) { // SAFETY: `PL011_BASE_ADDRESS` is the base address of a PL011 device, and // nothing else accesses that address range. let uart = unsafe { Uart::new(PL011_BASE_ADDRESS) }; logger::init(uart, LevelFilter::Trace).unwrap(); info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3); // SAFETY: `GICD_BASE_ADDRESS` and `GICR_BASE_ADDRESS` are the base // addresses of a GICv3 distributor and redistributor respectively, and // nothing else accesses those address ranges. let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, GICR_BASE_ADDRESS) }; gic.setup(); // SAFETY: `PL031_BASE_ADDRESS` is the base address of a PL031 device, and // nothing else accesses that address range. let mut rtc = unsafe { Rtc::new(PL031_BASE_ADDRESS) }; let timestamp = rtc.read(); let time = Utc.timestamp_opt(timestamp.into(), 0).unwrap(); info!("RTC: {time}"); GicV3::set_priority_mask(0xff); gic.set_interrupt_priority(PL031_IRQ, 0x80); gic.set_trigger(PL031_IRQ, Trigger::Level); irq_enable(); gic.enable_interrupt(PL031_IRQ, true); // å²ã蟌ã¿ãªã㧠3 ç§éåŸ æ©ããŸãã let target = timestamp + 3; rtc.set_match(target); info!("Waiting for {}", Utc.timestamp_opt(target.into(), 0).unwrap()); trace!( "matched={}, interrupt_pending={}", rtc.matched(), rtc.interrupt_pending() ); while !rtc.matched() { spin_loop(); } trace!( "matched={}, interrupt_pending={}", rtc.matched(), rtc.interrupt_pending() ); info!("Finished waiting"); // å²ã蟌ã¿ãŸã§ããã« 3 ç§åŸ ã¡ãŸãã let target = timestamp + 6; info!("Waiting for {}", Utc.timestamp_opt(target.into(), 0).unwrap()); rtc.set_match(target); rtc.clear_interrupt(); rtc.enable_interrupt(true); trace!( "matched={}, interrupt_pending={}", rtc.matched(), rtc.interrupt_pending() ); while !rtc.interrupt_pending() { wfi(); } trace!( "matched={}, interrupt_pending={}", rtc.matched(), rtc.interrupt_pending() ); info!("Finished waiting"); system_off::<Hvc>().unwrap(); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { error!("{info}"); system_off::<Hvc>().unwrap(); loop {} }
pl031.rs:
#![allow(unused)] fn main() { #[repr(C, align(4))] struct Registers { /// ããŒã¿ã¬ãžã¹ã¿ dr: u32, /// äžèŽã¬ãžã¹ã¿ mr: u32, /// èªã¿èŸŒã¿ã¬ãžã¹ã¿ lr: u32, /// å¶åŸ¡ã¬ãžã¹ã¿ cr: u8, _reserved0: [u8; 3], /// å²ã蟌ã¿ãã¹ã¯ã»ãããŸãã¯ã¯ãªã¢ã¬ãžã¹ã¿ imsc: u8, _reserved1: [u8; 3], /// æªå å·¥ã®å²ã蟌ã¿ã¹ããŒã¿ã¹ ris: u8, _reserved2: [u8; 3], /// ãã¹ã¯ãããå²ã蟌ã¿ã¹ããŒã¿ã¹ mis: u8, _reserved3: [u8; 3], /// å²ã蟌ã¿ã¯ãªã¢ã¬ãžã¹ã¿ icr: u8, _reserved4: [u8; 3], } /// PL031 ãªã¢ã«ã¿ã€ã ã¯ããã¯çšã®ãã©ã€ãã #[derive(Debug)] pub struct Rtc { registers: *mut Registers, } impl Rtc { /// æå®ãããããŒã¹ã¢ãã¬ã¹ã« /// PL031 ããã€ã¹çšã® RTC ãã©ã€ãã®æ°ããã€ã³ã¹ã¿ã³ã¹ãäœæããŸãã /// /// # å®å šæ§ /// /// æå®ãããããŒã¹ã¢ãã¬ã¹ã¯ PL031 ããã€ã¹ã® MMIO å¶åŸ¡ã¬ãžã¹ã¿ãæããŠããå¿ èŠããããŸãã /// ãããã¯ããã€ã¹ã¡ã¢ãªãšããŠããã»ã¹ã®ã¢ãã¬ã¹ç©ºéã« /// ãããã³ã°ãããä»ã®ãšã€ãªã¢ã¹ã¯ãããŸããã pub unsafe fn new(base_address: *mut u32) -> Self { Self { registers: base_address as *mut Registers } } /// çŸåšã® RTC å€ãèªã¿åããŸãã pub fn read(&self) -> u32 { // SAFETY: We know that self.registers points to the control registers // of a PL031 device which is appropriately mapped. unsafe { (&raw const (*self.registers).dr).read_volatile() } } /// äžèŽå€ãæžã蟌ã¿ãŸããRTC å€ãããã«äžèŽãããšãå²ã蟌ã¿ãçæãããŸã /// ïŒå²ã蟌ã¿ãæå¹ã«ãªã£ãŠããå ŽåïŒã pub fn set_match(&mut self, value: u32) { // SAFETY: We know that self.registers points to the control registers // of a PL031 device which is appropriately mapped. unsafe { (&raw mut (*self.registers).mr).write_volatile(value) } } /// å²ã蟌ã¿ãæå¹ã«ãªã£ãŠãããã©ããã«é¢ä¿ãªããäžèŽã¬ãžã¹ã¿ã RTC å€ãš /// äžèŽãããã©ãããè¿ããŸãã pub fn matched(&self) -> bool { // SAFETY: We know that self.registers points to the control registers // of a PL031 device which is appropriately mapped. let ris = unsafe { (&raw const (*self.registers).ris).read_volatile() }; (ris & 0x01) != 0 } /// çŸåšä¿çäžã®å²ã蟌ã¿ããããã©ãããè¿ããŸãã /// /// ãã㯠`matched` ã true ãè¿ããå²ã蟌ã¿ããã¹ã¯ãããŠããå Žåã«ã®ã¿ /// true ã«ãªããŸãã pub fn interrupt_pending(&self) -> bool { // SAFETY: We know that self.registers points to the control registers // of a PL031 device which is appropriately mapped. let ris = unsafe { (&raw const (*self.registers).mis).read_volatile() }; (ris & 0x01) != 0 } /// å²ã蟌ã¿ãã¹ã¯ãèšå®ãŸãã¯ã¯ãªã¢ããŸãã /// /// ãã¹ã¯ã true ã®å Žåãå²ã蟌ã¿ã¯æå¹ã«ãªããŸããfalse ã®å Žåã /// å²ã蟌ã¿ã¯ç¡å¹ã«ãªããŸãã pub fn enable_interrupt(&mut self, mask: bool) { let imsc = if mask { 0x01 } else { 0x00 }; // SAFETY: We know that self.registers points to the control registers // of a PL031 device which is appropriately mapped. unsafe { (&raw mut (*self.registers).imsc).write_volatile(imsc) } } /// ä¿çäžã®å²ã蟌ã¿ãããã°ã¯ãªã¢ããŸãã pub fn clear_interrupt(&mut self) { // SAFETY: We know that self.registers points to the control registers // of a PL031 device which is appropriately mapped. unsafe { (&raw mut (*self.registers).icr).write_volatile(0x01) } } } // SAFETY: `Rtc` just contains a pointer to device memory, which can be // accessed from any context. unsafe impl Send for Rtc {} }
Rustã§ã®äžŠè¡æ§ãžãããã
Rustã¯ãã¥ãŒããã¯ã¹ãšãã£ãã«ãçšããŠOSã¹ã¬ãããæ±ã䞊è¡æ§ãååã«ãµããŒãããŠããŸãã
Rustã®åã·ã¹ãã ã¯å€ãã®äžŠè¡æ§ã«ãŸã€ãããã°ãã³ã³ãã€ã«æã®ãã°ã«ãšã©ãããšããç¹ã§ãéèŠãªåœ¹å²ãæãããŸããããã¯æã« fearless concurrency ïŒãæããªã䞊è¡æ§ãïŒ ãšåŒã°ããŸãããªããªããã³ã³ãã€ã©ã«å®è¡æã§ã®æ£ãããä¿èšŒããããšããŸãããŠããããã§ãã
ã¹ã±ãžã¥ãŒã«
Including 10 minute breaks, this session should take about 3 hours and 20 minutes. It contains:
Segment | Duration |
---|---|
ã¹ã¬ãã | 30 minutes |
ãã£ãã« | 20 minutes |
SendãšSync | 15 minutes |
ç¶æ å ±æ | 30 minutes |
ç·Žç¿åé¡ | 1 hour and 10 minutes |
- Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc.
- The type system gives us safety for concurrency without any special features.
- The same tools that help with "concurrent" access in a single thread (e.g., a called function that might mutate an argument or save references to it to read later) save us from multi-threading issues.
ã¹ã¬ãã
This segment should take about 30 minutes. It contains:
Slide | Duration |
---|---|
ãã¬ãŒã³ãªã¹ã¬ãã | 15 minutes |
ã¹ã³ãŒãä»ãã¹ã¬ãã | 15 minutes |
ãã¬ãŒã³ãªã¹ã¬ãã
Rustã®ã¹ã¬ããã¯ä»ã®èšèªã®ã¹ã¬ãããšäŒŒãæåãããŸã:
use std::thread; use std::time::Duration; fn main() { thread::spawn(|| { for i in 0..10 { println!("Count in thread: {i}!"); thread::sleep(Duration::from_millis(5)); } }); for i in 0..5 { println!("Main thread: {i}"); thread::sleep(Duration::from_millis(5)); } }
- Spawning new threads does not automatically delay program termination at the end of
main
. - ã¹ã¬ãããããã¯ã¯äºãã«ç¬ç«ã§ãã
- Panics can carry a payload, which can be unpacked with
Any::downcast_ref
.
- Panics can carry a payload, which can be unpacked with
-
Run the example.
- 5ms timing is loose enough that main and spawned threads stay mostly in lockstep.
- Notice that the program ends before the spawned thread reaches 10!
- This is because
main
ends the program and spawned threads do not make it persist.- Compare to
pthreads
/C++std::thread
/boost::thread
if desired.
- Compare to
-
How do we wait around for the spawned thread to complete?
-
thread::spawn
returns aJoinHandle
. Look at the docs.JoinHandle
has a.join()
method that blocks.
-
Use
let handle = thread::spawn(...)
and laterhandle.join()
to wait for the thread to finish and have the program count all the way to 10. -
Now what if we want to return a value?
-
Look at docs again:
thread::spawn
's closure returnsT
JoinHandle
.join()
returnsthread::Result<T>
-
Use the
Result
return value fromhandle.join()
to get access to the returned value. -
Ok, what about the other case?
- Trigger a panic in the thread. Note that this doesn't panic
main
. - Access the panic payload. This is a good time to talk about
Any
.
- Trigger a panic in the thread. Note that this doesn't panic
-
Now we can return values from threads! What about taking inputs?
- Capture something by reference in the thread closure.
- An error message indicates we must move it.
- Move it in, see we can compute and then return a derived value.
-
If we want to borrow?
- Main kills child threads when it returns, but another function would just return and leave them running.
- That would be stack use-after-return, which violates memory safety!
- How do we avoid this? See next slide.
ã¹ã³ãŒãä»ãã¹ã¬ãã
éåžžã®ã¹ã¬ããã¯ãããã®ç°å¢ããåçšããããšã¯ã§ããŸãã:
use std::thread; fn foo() { let s = String::from("Hello"); thread::spawn(|| { println!("Length: {}", s.len()); }); } fn main() { foo(); }
ãããããã®ããã«ã¹ã³ãŒãä»ãã¹ã¬ããã䜿ãããšãã§ããŸã:
use std::thread; fn foo() { let s = String::from("Hello"); thread::scope(|scope| { scope.spawn(|| { println!("Length: {}", s.len()); }); }); } fn main() { foo(); }
- ãã®çç±ã¯ãé¢æ°
thread::scope
ãå®äºãããšããå šãŠã®ã¹ã¬ããã¯joinãããããšãä¿èšŒãããŠããã®ã§ãã¹ã¬ãããåçšããããŒã¿ãè¿ãããšãã§ããããã§ãã - éåžžã®Rustã®åçšã®ã«ãŒã«ãé©çšãããŸã:ãäžã€ã®ã¹ã¬ããããã¥ãŒã¿ãã«ã§åçšããããšããŸãã¯ä»»æã®æ°ã®ã¹ã¬ããããã€ãã¥ãŒã¿ãã«ã§åçšããããšã
ãã£ãã«
This segment should take about 20 minutes. It contains:
Slide | Duration |
---|---|
éä¿¡åŽ(Senders)ãšåä¿¡åŽ(Receivers) | 10 minutes |
Unboundedãã£ãã« | 2 minutes |
Boundedãã£ãã« | 10 minutes |
éä¿¡åŽ(Senders)ãšåä¿¡åŽ(Receivers)
Rust channels have two parts: a Sender<T>
and a Receiver<T>
. The two parts are connected via the channel, but you only see the end-points.
use std::sync::mpsc; fn main() { let (tx, rx) = mpsc::channel(); tx.send(10).unwrap(); tx.send(20).unwrap(); println!("Received: {:?}", rx.recv()); println!("Received: {:?}", rx.recv()); let tx2 = tx.clone(); tx2.send(30).unwrap(); println!("Received: {:?}", rx.recv()); }
Unboundedãã£ãã«
You get an unbounded and asynchronous channel with mpsc::channel()
:
use std::sync::mpsc; use std::thread; use std::time::Duration; fn main() { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let thread_id = thread::current().id(); for i in 0..10 { tx.send(format!("Message {i}")).unwrap(); println!("{thread_id:?}: sent Message {i}"); } println!("{thread_id:?}: done"); }); thread::sleep(Duration::from_millis(100)); for msg in rx.iter() { println!("Main: got {msg}"); } }
Boundedãã£ãã«
With bounded (synchronous) channels, send()
can block the current thread:
use std::sync::mpsc; use std::thread; use std::time::Duration; fn main() { let (tx, rx) = mpsc::sync_channel(3); thread::spawn(move || { let thread_id = thread::current().id(); for i in 0..10 { tx.send(format!("Message {i}")).unwrap(); println!("{thread_id:?}: sent Message {i}"); } println!("{thread_id:?}: done"); }); thread::sleep(Duration::from_millis(100)); for msg in rx.iter() { println!("Main: got {msg}"); } }
- Calling
send()
will block the current thread until there is space in the channel for the new message. The thread can be blocked indefinitely if there is nobody who reads from the channel. - A call to
send()
will abort with an error (that is why it returnsResult
) if the channel is closed. A channel is closed when the receiver is dropped. - A bounded channel with a size of zero is called a "rendezvous channel". Every send will block the current thread until another thread calls
recv()
.
Send
ãšSync
This segment should take about 15 minutes. It contains:
Slide | Duration |
---|---|
ããŒã«ãŒãã¬ã€ã | 2 minutes |
Send | 2 minutes |
Sync | 2 minutes |
äŸ | 10 minutes |
ããŒã«ãŒãã¬ã€ã
How does Rust know to forbid shared access across threads? The answer is in two traits:
Send
: ã¹ã¬ããå¢çããŸããã§ã®åT
ã®ã ãŒããå®å šã«è¡ããå ŽåãåT
ã¯Send
ã§ãããSync
: ã¹ã¬ããå¢çããŸããã§&T
ã®ã ãŒããå®å šã«è¡ããå ŽåãåT
ã¯Sync
ã§ããã
Send
and Sync
are unsafe traits. The compiler will automatically derive them for your types as long as they only contain Send
and Sync
types. You can also implement them manually when you know it is valid.
- ãããã®ãã¬ã€ãã¯ãããåãç¹å®ã®ã¹ã¬ããã»ãŒãã®ç¹æ§ãæã£ãŠããããšã瀺ãããŒã«ãŒãšèããããšãã§ããŸãã
- ãããã¯éåžžã®ãã¬ã€ããšåãããã«ããžã§ããªãã¯å¢çã®äžã§å©çšããããšãã§ããŸãã
Send
å
T
ã®å€ãå®å šã«å¥ã®ã¹ã¬ããã«ã ãŒãã§ããå ŽåãåT
ã¯Send
ã§ããã
æææš©ãå¥ã®ã¹ã¬ããã«ã ãŒããããšããããšã¯ããã¹ãã©ã¯ã¿ ããã®ã¹ã¬ããã§å®è¡ããããšããããšã§ããã€ãŸããããã¹ã¬ããã§ã¢ãã±ãŒããããå€ãå¥ã®ã¹ã¬ããã§è§£æŸããŠãè¯ãããšããã®ãå€æåºæºã«ãªããŸãã
äŸãæãããšãSQLiteã©ã€ãã©ãªãžã®ã³ãã¯ã·ã§ã³ã¯ãäžã€ã®ã¹ã¬ããããã®ã¿ã¢ã¯ã»ã¹ãããå¿ èŠããããŸãã
Sync
å
T
ã®å€ãè€æ°ã®ã¹ã¬ããããåæã«ã¢ã¯ã»ã¹ããŠãå®å šãªå ŽåãåT
ã¯Sync
ã§ããã
ããæ£ç¢ºã«ã¯ã以äžã®ãããªå®çŸ©ã§ãïŒ
&T
ãSend
ã§ããå Žåããã€ãã®å Žåã«éããT
ã¯Sync
ã§ãã
ããã¯ã€ãŸãããããåã®å ±æãã¹ã¬ããã»ãŒãã§ããã°ããã®åç §ãã¹ã¬ããéã§åãæž¡ãããšãã¹ã¬ããã»ãŒãã§ããããšããããšãæçã«è¡šãããã®ã§ãã
ãªããªããããåãSyncã§ããå ŽåãããŒã¿ç«¶åãä»ã®åæã®åé¡ãªã©ã®ãªã¹ã¯ãªãã«ãã®åãè€æ°ã®ã¹ã¬ããéã§å ±æã§ãããã®åãå¥ã®ã¹ã¬ããã«ã ãŒãããŠãå®å šã ããã§ãããŸããåãžã®åç §ã¯å¥ã®ã¹ã¬ããã«ã ãŒãããŠãå®å šã§ããããã¯ããããåç §ããããŒã¿ã¯ä»»æã®ã¹ã¬ããããå®å šã«ã¢ã¯ã»ã¹ããããšãã§ããããã§ãã
äŸ
Send + Sync
èŠãããã»ãšãã©ã®åã¯Send + Sync
ã§ãïŒ
i8
ãf32
ãbool
ãchar
ã&str
ãªã©(T1, T2)
ã[T; N]
ã&[T]
ãstruct { x: T }
ãªã©String
ãOption<T>
ãVec<T>
ãBox<T>
ãªã©Arc<T>
: ã¢ãããã¯åç §ã«ãŠã³ãã«ãããæ瀺çã«ã¹ã¬ããã»ãŒããMutex<T>
: å éšããã¯ã«ããæ瀺çã«ã¹ã¬ããã»ãŒããmpsc::Sender<T>
: As of 1.72.0.AtomicBool
,AtomicU8
, âŠ: ç¹å¥ãªã¢ãããã¯åœä»€ãå©çšã
ãžã§ããªã¯ã¹ã¯ãåãã©ã¡ã¿ãSend + Sync
ã§ãããšããéåžžã¯Send + Sync
ã§ãã
Send + !Sync
ãããã®åã¯å¥ã®ã¹ã¬ããã«ã ãŒãããããšãã§ããŸããããã®ãããªã ãŒãã¯ã¹ã¬ããã»ãŒãã§ã¯ãããŸãããéåžžã¯å éšå¯å€æ§ããã®åå ã§ãïŒ
mpsc::Receiver<T>
Cell<T>
RefCell<T>
!Send + Sync
These types are safe to access (via shared references) from multiple threads, but they cannot be moved to another thread:
MutexGuard<T: Sync>
: Uses OS level primitives which must be deallocated on the thread which created them. However, an already-locked mutex can have its guarded variable read by any thread with which the guard is shared.
!Send + !Sync
ãã®ãããªåã¯ã¹ã¬ããã»ãŒãã§ã¯ãªããããå¥ã®ã¹ã¬ããã«ã ãŒãããããšã¯ã§ããŸããïŒ
Rc<T>
: ããããã®Rc<T>
ã¯RcBox<T>
ãžã®åç §ãæã£ãŠããŸããããã¯ãã¢ãããã¯ã§ãªãåç §ã«ãŠã³ããæã£ãŠããŸãã*const T
,*mut T
: Rust ã¯ãçãã€ã³ã¿ãŒã¯åæå®è¡æ§ã«é¢ããç¹å¥ãªèæ ®äºé ãããå¯èœæ§ãããããšãä»®å®ããŠããŸãã
ç¶æ å ±æ
This segment should take about 30 minutes. It contains:
Slide | Duration |
---|---|
Arc | 5 minutes |
Mutex | 15 minutes |
äŸ | 10 minutes |
Arc
Arc<T>
ã¯èªã¿åãå°çšã®å
±æã¢ã¯ã»ã¹ãArc::clone
ã«ããå¯èœã«ããŸãïŒ
use std::sync::Arc; use std::thread; fn main() { let v = Arc::new(vec![10, 20, 30]); let mut handles = Vec::new(); for _ in 0..5 { let v = Arc::clone(&v); handles.push(thread::spawn(move || { let thread_id = thread::current().id(); println!("{thread_id:?}: {v:?}"); })); } handles.into_iter().for_each(|h| h.join().unwrap()); println!("v: {v:?}"); }
Arc
ã¯"Atomic Reference Counted"ã®ç¥ã§ãã¢ãããã¯æäœãå©çšãããšããç¹ã§ãRc
ãã¹ã¬ããå®å šã«ãªã£ãããŒãžã§ã³ã®ãããªãã®ã§ããArc<T>
ã¯Clone
ãå®è£ ããŸãããã®ããšã¯T
ãClone
ãå®è£ ããããªãã«é¢ä¿ãããŸãããT
ãSend
ãšSync
ã®äž¡æ¹ãå®è£ ããŠããå Žåã§ããã€ãã®å Žåã«éããArc<T>
ã¯äž¡è ãå®è£ ããŸããArc::clone()
ã«ã¯ã¢ãããã¯æäœã®ã³ã¹ããããããŸãããã ããã®åŸã¯ãT
ã®å©çšã«é¢ããã³ã¹ãã¯ããããŸããã- åç
§ãµã€ã¯ã«ã«æ°ãã€ããŠãã ããã
Arc
ã«ã¯åç §ãµã€ã¯ã«ãæ€ç¥ããããã®ã¬ããŒãžã³ã¬ã¯ã¿ã¯ãããŸãããstd::sync::Weak
ã圹ç«ã¡ãŸãã
Mutex
Mutex<T>
ensures mutual exclusion and allows mutable access to T
behind a read-only interface (another form of interior mutability):
use std::sync::Mutex; fn main() { let v = Mutex::new(vec![10, 20, 30]); println!("v: {:?}", v.lock().unwrap()); { let mut guard = v.lock().unwrap(); guard.push(40); } println!("v: {:?}", v.lock().unwrap()); }
impl<T: Send> Sync for Mutex<T>
ã®ãã©ã³ã±ããå®è£
ãããããšã«æ³šç®ããŠãã ããã
- Rustã«ããã
Mutex
ãšã¯ãä¿è·ãããããŒã¿ã§ããããã£ãäžã€ã®èŠçŽ ããæ§æãããã³ã¬ã¯ã·ã§ã³ã®ãããªãã®ã§ãã- ä¿è·ãããããŒã¿ã«ã¢ã¯ã»ã¹ããåã«ããã¥ãŒããã¯ã¹ã確ä¿ãå¿ããããšã¯ãããŸããã
&Mutex<T>
ããããã¯ãååŸããããšã§ã&mut T
ãåŸãããšãã§ããŸãããã®MutexGuard
ã¯&mut T
ãä¿æãããŠããããã¯ãããé·ãåç¶ããªãããšãä¿èšŒããŸããMutex<T>
implements bothSend
andSync
if and only ifT
implementsSend
.- èªã¿æžãã®ããã¯ã®å Žåã«å¯Ÿå¿ãããã®ããããŸãïŒ
RwLock
ã - ãªã
lock()
ã¯Result
ãè¿ãã®ã§ãããïŒ- Mutex
ãä¿æããã¹ã¬ããããããã¯ãèµ·ãããå Žåãä¿è·ãã¹ãããŒã¿ãæŽåæ§ã®æ¬ ããç¶æ ã«ããå¯èœæ§ãäŒããããã
Mutexã¯ããã€ãŸã³ããããïŒ"poisoned"ïŒç¶æ ã«ãªããŸãããã€ãŸã³ãããMutexã«å¯ŸããŠ
lock()ãã³ãŒã«ãããšã[
PoisonError](https://doc.rust-lang.org/std/sync/struct.PoisonError.html)ãšãšãã«å€±æããŸãã
into_inner()` ãçšããããšã§ããã®ãšã©ãŒã«ãããŠããšããããããŒã¿ãå埩ããããšã¯ã§ããŸãã
- Mutex
äŸ
Arc
ãš Mutex
ã®åäœãèŠãŠã¿ãŸãããïŒ
use std::thread; // use std::sync::{Arc, Mutex}; fn main() { let v = vec![10, 20, 30]; let handle = thread::spawn(|| { v.push(10); }); v.push(1000); handle.join().unwrap(); println!("v: {v:?}"); }
èãããã察åŠæ³ïŒ
use std::sync::{Arc, Mutex}; use std::thread; fn main() { let v = Arc::new(Mutex::new(vec![10, 20, 30])); let v2 = Arc::clone(&v); let handle = thread::spawn(move || { let mut v2 = v2.lock().unwrap(); v2.push(10); }); { let mut v = v.lock().unwrap(); v.push(1000); } handle.join().unwrap(); println!("v: {v:?}"); }
泚ç®ãããšããç®æïŒ
v
ã¯Arc
ãšMutex
ã®äž¡æ¹ã§ã©ãããããŠããŸãããªããªãããããã®é¢å¿ã¯äºãã«ç¬ç«ãªãã®ã§ããããã§ããMutex
ãArc
ã§ã©ããããããšã¯ãã¹ã¬ããéã§ãã¥ãŒã¿ãã«ãªç¶æ ãå ±æããããã«ããèŠããããã¿ãŒã³ã§ãã
v: Arc<_>
ã¯å¥ã®ã¹ã¬ããã«ã ãŒããããåã«ãv2
ãšããŠã¯ããŒã³ãããå¿ èŠããããŸããmove
ãã©ã ãåŒã«è¿œå ãããããšã«æ³šæããŠãã ããã- ãããã¯ã¯
LockGuard
ã®ã¹ã³ãŒããå¯èœãªéãçããããã«å°å ¥ãããŠããŸãã
ç·Žç¿åé¡
This segment should take about 1 hour and 10 minutes. It contains:
Slide | Duration |
---|---|
é£äºããå²åŠè | 20 minutes |
ãã«ãã¹ã¬ããã»ãªã³ã¯ãã§ãã«ãŒ | 20 minutes |
解ç | 30 minutes |
é£äºããå²åŠè
é£äºããå²åŠè ã®åé¡ã¯ã䞊è¡æ§ã«é¢ããå€å žçãªåé¡ã§ãã
5 人ã®å²åŠè ãåãããŒãã«ã§é£äºãããŠããŸããããããã®å²åŠè ãããŒãã«ã®å®äœçœ®ã«åº§ããç¿ã®éã«ã¯ãã©ãŒã¯ã 1 æ¬çœ®ãããŠããŸããæäŸãããæçã¯ã¹ãã²ããã£ã§ã2 æ¬ã®ãã©ãŒã¯ã§é£ã¹ãå¿ èŠããããŸããå²åŠè ã¯æ玢ãšé£äºã亀äºã«ç¹°ãè¿ãããšããã§ããŸãããããã«ãå²åŠè ã¯å·Šå³äž¡æ¹ã®ãã©ãŒã¯ãæã£ãŠããå Žåã«ã®ã¿ãã¹ãã²ããã£ãé£ã¹ãããšãã§ããŸãããããã£ãŠã2 ã€ã®ãã©ãŒã¯ã¯ãäž¡é£ã®å²åŠè ãé£ã¹ãã®ã§ã¯ãªãèããŠããå Žåã«ã®ã¿äœ¿çšã§ããŸããããããã®å²åŠè ã¯ãé£ã¹çµãã£ãåŸãäž¡æ¹ã®ãã©ãŒã¯ã眮ããŸãã
ãã®æŒç¿ã§ã¯ãããŒã«ã«ã® Cargo ã€ã³ã¹ããŒã«ãå¿
èŠã§ãã以äžã®ã³ãŒãã src/main.rs
ãšãããã¡ã€ã«ã«ã³ããŒãã空æ¬ãåããŠãcargo run
ããããããã¯ããªãããšã確èªããŸãã
use std::sync::{mpsc, Arc, Mutex}; use std::thread; use std::time::Duration; struct Fork; struct Philosopher { name: String, // left_fork: ... // right_fork: ... // thoughts: ... } impl Philosopher { fn think(&self) { self.thoughts .send(format!("Eureka! {} has a new idea!", &self.name)) .unwrap(); } fn eat(&self) { // Pick up forks... println!("{} is eating...", &self.name); thread::sleep(Duration::from_millis(10)); } } static PHILOSOPHERS: &[&str] = &["Socrates", "Hypatia", "Plato", "Aristotle", "Pythagoras"]; fn main() { // ãã©ãŒã¯ãäœæãã // å²åŠè ãäœæãã // ããããã®å²åŠè ãæ玢ãšé£äºã 100 åè¡ãããã«ãã // å²åŠè ã®æ玢ãåºåãã }
次㮠Cargo.toml
ã䜿çšã§ããŸãã
[package]
name = "dining-philosophers"
version = "0.1.0"
edition = "2021"
ãã«ãã¹ã¬ããã»ãªã³ã¯ãã§ãã«ãŒ
æ°ãã«èº«ã«ä»ããç¥èã掻ãããŠããã«ãã¹ã¬ãã ãªã³ã¯ ãã§ãã«ãŒãäœæããŸãããããŸãããŠã§ãããŒãžäžã®ãªã³ã¯ãæå¹ãã©ããã確èªããå¿ èŠããããŸããåããã¡ã€ã³ã®ä»ã®ããŒãžãååž°çã«ãã§ãã¯ãããã¹ãŠã®ããŒãžã®æ€èšŒãå®äºãããŸã§ãã®åŠçãç¹°ãè¿ããŸãã
For this, you will need an HTTP client such as reqwest
. You will also need a way to find links, we can use scraper
. Finally, we'll need some way of handling errors, we will use thiserror
.
Create a new Cargo project and reqwest
it as a dependency with:
cargo new link-checker
cd link-checker
cargo add --features blocking,rustls-tls reqwest
cargo add scraper
cargo add thiserror
cargo add
ãerror: no such subcommand
ã§å€±æããå Žåã¯ãCargo.toml
ãã¡ã€ã«ãæåã§ç·šéããŠãã ãããäžèšã®äŸåé¢ä¿ãè¿œå ããŸãã
cargo add
ã®åŒã³åºãã«ãããCargo.toml
ãã¡ã€ã«ã¯æ¬¡ã®ããã«æŽæ°ãããŸãã
[package]
name = "link-checker"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
reqwest = { version = "0.11.12", features = ["blocking", "rustls-tls"] }
scraper = "0.13.0"
thiserror = "1.0.37"
ããã§ãã¹ã¿ãŒãããŒãžãããŠã³ããŒãã§ããããã«ãªããŸãããhttps://www.google.org/
ã®ãããªå°èŠæš¡ãªãµã€ãã§è©ŠããŠã¿ãŸãããã
src/main.rs
ãã¡ã€ã«ã¯æ¬¡ã®ããã«ãªããŸãã
use reqwest::blocking::Client; use reqwest::Url; use scraper::{Html, Selector}; use thiserror::Error; #[derive(Error, Debug)] enum Error { #[error("request error: {0}")] ReqwestError(#[from] reqwest::Error), #[error("bad http response: {0}")] BadResponse(String), } #[derive(Debug)] struct CrawlCommand { url: Url, extract_links: bool, } fn visit_page(client: &Client, command: &CrawlCommand) -> Result<Vec<Url>, Error> { println!("Checking {:#}", command.url); let response = client.get(command.url.clone()).send()?; if !response.status().is_success() { return Err(Error::BadResponse(response.status().to_string())); } let mut link_urls = Vec::new(); if !command.extract_links { return Ok(link_urls); } let base_url = response.url().to_owned(); let body_text = response.text()?; let document = Html::parse_document(&body_text); let selector = Selector::parse("a").unwrap(); let href_values = document .select(&selector) .filter_map(|element| element.value().attr("href")); for href in href_values { match base_url.join(href) { Ok(link_url) => { link_urls.push(link_url); } Err(err) => { println!("On {base_url:#}: ignored unparsable {href:?}: {err}"); } } } Ok(link_urls) } fn main() { let client = Client::new(); let start_url = Url::parse("https://www.google.org").unwrap(); let crawl_command = CrawlCommand{ url: start_url, extract_links: true }; match visit_page(&client, &crawl_command) { Ok(links) => println!("Links: {links:#?}"), Err(err) => println!("Could not extract links: {err:#}"), } }
src/main.rs
å
ã®ã³ãŒããã次ã®ã³ãã³ãã§å®è¡ããŸãã
cargo run
ã¿ã¹ã¯
- ã¹ã¬ããã䜿çšããŠãªã³ã¯ãåæã«ãã§ãã¯ããŸããã€ãŸãããã§ãã¯ãã URL ããã£ã³ãã«ã«éä¿¡ããããã€ãã®ã¹ã¬ããã§åæã« URL ã確èªããŸãã
- ãããæ¡åŒµããŠã
www.google.org
ãã¡ã€ã³ã®ãã¹ãŠã®ããŒãžãããªã³ã¯ãååž°çã«æœåºããŸãããµã€ãããããã¯ãããªãããã«ãããŒãžæ°ã®äžéã 100 çšåºŠã«èšå®ããŸãã
解ç
é£äºããå²åŠè
use std::sync::{mpsc, Arc, Mutex}; use std::thread; use std::time::Duration; struct Fork; struct Philosopher { name: String, left_fork: Arc<Mutex<Fork>>, right_fork: Arc<Mutex<Fork>>, thoughts: mpsc::SyncSender<String>, } impl Philosopher { fn think(&self) { self.thoughts .send(format!("Eureka! {} has a new idea!", &self.name)) .unwrap(); } fn eat(&self) { println!("{} is trying to eat", &self.name); let _left = self.left_fork.lock().unwrap(); let _right = self.right_fork.lock().unwrap(); println!("{} is eating...", &self.name); thread::sleep(Duration::from_millis(10)); } } static PHILOSOPHERS: &[&str] = &["Socrates", "Hypatia", "Plato", "Aristotle", "Pythagoras"]; fn main() { let (tx, rx) = mpsc::sync_channel(10); let forks = (0..PHILOSOPHERS.len()) .map(|_| Arc::new(Mutex::new(Fork))) .collect::<Vec<_>>(); for i in 0..forks.len() { let tx = tx.clone(); let mut left_fork = Arc::clone(&forks[i]); let mut right_fork = Arc::clone(&forks[(i + 1) % forks.len()]); // ãããããã¯ãé¿ããããã«ãã©ããã§å¯Ÿç§°æ§ã // 厩ãå¿ èŠããããŸããäžèšã®ã³ãŒãã§ã¯ã // é åãéæŸããããšãªã2ã€ã®ãã©ãŒã¯ã亀æããŸãã if i == forks.len() - 1 { std::mem::swap(&mut left_fork, &mut right_fork); } let philosopher = Philosopher { name: PHILOSOPHERS[i].to_string(), thoughts: tx, left_fork, right_fork, }; thread::spawn(move || { for _ in 0..100 { philosopher.eat(); philosopher.think(); } }); } drop(tx); for thought in rx { println!("{thought}"); } }
Link Checker
use std::sync::{mpsc, Arc, Mutex}; use std::thread; use reqwest::blocking::Client; use reqwest::Url; use scraper::{Html, Selector}; use thiserror::Error; #[derive(Error, Debug)] enum Error { #[error("request error: {0}")] ReqwestError(#[from] reqwest::Error), #[error("bad http response: {0}")] BadResponse(String), } #[derive(Debug)] struct CrawlCommand { url: Url, extract_links: bool, } fn visit_page(client: &Client, command: &CrawlCommand) -> Result<Vec<Url>, Error> { println!("Checking {:#}", command.url); let response = client.get(command.url.clone()).send()?; if !response.status().is_success() { return Err(Error::BadResponse(response.status().to_string())); } let mut link_urls = Vec::new(); if !command.extract_links { return Ok(link_urls); } let base_url = response.url().to_owned(); let body_text = response.text()?; let document = Html::parse_document(&body_text); let selector = Selector::parse("a").unwrap(); let href_values = document .select(&selector) .filter_map(|element| element.value().attr("href")); for href in href_values { match base_url.join(href) { Ok(link_url) => { link_urls.push(link_url); } Err(err) => { println!("On {base_url:#}: ignored unparsable {href:?}: {err}"); } } } Ok(link_urls) } struct CrawlState { domain: String, visited_pages: std::collections::HashSet<String>, } impl CrawlState { fn new(start_url: &Url) -> CrawlState { let mut visited_pages = std::collections::HashSet::new(); visited_pages.insert(start_url.as_str().to_string()); CrawlState { domain: start_url.domain().unwrap().to_string(), visited_pages } } /// æå®ãããããŒãžå ã®ãªã³ã¯ãæœåºãããã©ããã決å®ããŸãã fn should_extract_links(&self, url: &Url) -> bool { let Some(url_domain) = url.domain() else { return false; }; url_domain == self.domain } /// æå®ãããããŒãžã蚪åæžã¿ãšããŠããŒã¯ãããã§ã«èšªåæžã¿ã§ããã° /// false ãè¿ããŸãã fn mark_visited(&mut self, url: &Url) -> bool { self.visited_pages.insert(url.as_str().to_string()) } } type CrawlResult = Result<Vec<Url>, (Url, Error)>; fn spawn_crawler_threads( command_receiver: mpsc::Receiver<CrawlCommand>, result_sender: mpsc::Sender<CrawlResult>, thread_count: u32, ) { let command_receiver = Arc::new(Mutex::new(command_receiver)); for _ in 0..thread_count { let result_sender = result_sender.clone(); let command_receiver = command_receiver.clone(); thread::spawn(move || { let client = Client::new(); loop { let command_result = { let receiver_guard = command_receiver.lock().unwrap(); receiver_guard.recv() }; let Ok(crawl_command) = command_result else { // éä¿¡è ããããããããŸãããä»åŸã³ãã³ãã¯åä¿¡ãããŸããã break; }; let crawl_result = match visit_page(&client, &crawl_command) { Ok(link_urls) => Ok(link_urls), Err(error) => Err((crawl_command.url, error)), }; result_sender.send(crawl_result).unwrap(); } }); } } fn control_crawl( start_url: Url, command_sender: mpsc::Sender<CrawlCommand>, result_receiver: mpsc::Receiver<CrawlResult>, ) -> Vec<Url> { let mut crawl_state = CrawlState::new(&start_url); let start_command = CrawlCommand { url: start_url, extract_links: true }; command_sender.send(start_command).unwrap(); let mut pending_urls = 1; let mut bad_urls = Vec::new(); while pending_urls > 0 { let crawl_result = result_receiver.recv().unwrap(); pending_urls -= 1; match crawl_result { Ok(link_urls) => { for url in link_urls { if crawl_state.mark_visited(&url) { let extract_links = crawl_state.should_extract_links(&url); let crawl_command = CrawlCommand { url, extract_links }; command_sender.send(crawl_command).unwrap(); pending_urls += 1; } } } Err((url, error)) => { bad_urls.push(url); println!("Got crawling error: {:#}", error); continue; } } } bad_urls } fn check_links(start_url: Url) -> Vec<Url> { let (result_sender, result_receiver) = mpsc::channel::<CrawlResult>(); let (command_sender, command_receiver) = mpsc::channel::<CrawlCommand>(); spawn_crawler_threads(command_receiver, result_sender, 16); control_crawl(start_url, command_sender, result_receiver) } fn main() { let start_url = reqwest::Url::parse("https://www.google.org").unwrap(); let bad_urls = check_links(start_url); println!("Bad URLs: {:#?}", bad_urls); }
ãããã
ãAsyncãã¯è€æ°ã®ã¿ã¹ã¯ã䞊è¡åŠçããã䞊è¡æ§ã¢ãã«ã§ããããããã®ã¿ã¹ã¯ã¯ãããã¯ããããŸã§å®è¡ããããããŠæ¬¡ã«é²ãããšã®ã§ããä»ã®ã¿ã¹ã¯ã«åãæ¿ããããšã«ããå®çŸãããŸãããã®ã¢ãã«ã¯éãããæ°ã®ã¹ã¬ããäžã§ããå€ãã®ã¿ã¹ã¯ãå®è¡ããããšãå¯èœã«ããŸãããªããªããã¿ã¹ã¯ããšã®ãªãŒããŒãããã¯éåžžã¯ãšãŠãäœããå¹ççã«å®è¡å¯èœãªI/Oãç¹å®ããããã«å¿ èŠãªããªããã£ããOSãæäŸããŠãããããã§ãã
Rustã®éåæçãªæäœã¯ãfutureãã«åºã¥ããŠããŠãããã¯å°æ¥ã«å®äºãããããããªãäœæ¥ãè¡šããŠããŸããFutureã¯ãã¿ã¹ã¯ãå®äºããããšãç¥ãããã·ã°ãã«ãåŸããããŸã§ããŒãªã³ã°ãããŸãã
Futureã¯éåæçãªã©ã³ã¿ã€ã ã«ããããŒãªã³ã°ãããŸããã©ã³ã¿ã€ã ã«ã¯ããã€ãã®éžæè¢ããããŸãã
ä»ã®èšèªãšã®æ¯èŒ
-
Pythonã«ã¯äŒŒããããªã¢ãã«ã
asyncio
ãšããŠæèŒãããŠããŸããããããããã§ã®Future
åã¯ã³ãŒã«ããã¯ã«åºã¥ããã®ã§ãã£ãŠãããŒãªã³ã°ã«ãããã®ã§ã¯ãããŸãããPythonã®éåæããã°ã©ã ã¯ãã«ãŒãããå¿ èŠãšããRustã®ã©ã³ã¿ã€ã ã«äŒŒãŠããŸãã -
JavaScriptã®
Promise
ã¯äŒŒãŠãããã®ã®ãããããŸãããã³ãŒã«ããã¯ã«åºã¥ããŸãã ãã®èšèªã®ã©ã³ã¿ã€ã ã¯ã€ãã³ãã«ãŒãã«ããå®è£ ãããŠãããããå€ãã®Promise解決ã®è©³çŽ°ã¯é ãããŠããŸãã
ã¹ã±ãžã¥ãŒã«
Including 10 minute breaks, this session should take about 3 hours and 20 minutes. It contains:
Segment | Duration |
---|---|
Asyncã®åºç€ | 30 minutes |
ãã£ãã«ãšå¶åŸ¡ãã㌠| 20 minutes |
èœãšãç©Ž | 55 minutes |
ç·Žç¿åé¡ | 1 hour and 10 minutes |
Asyncã®åºç€
This segment should take about 30 minutes. It contains:
Slide | Duration |
---|---|
async/await | 10 minutes |
Future | 4 minutes |
ã©ã³ã¿ã€ã | 10 minutes |
ã¿ã¹ã¯ | 10 minutes |
async
/await
ãããŸãã«ã¯ãRustã®éåæã³ãŒãã¯ã»ãšãã©ãéåžžã®ãé次çãªã³ãŒãã®ããã«èŠããŸã:
use futures::executor::block_on; async fn count_to(count: i32) { for i in 0..count { println!("Count is: {i}!"); } } async fn async_main(count: i32) { count_to(count).await; } fn main() { block_on(async_main(10)); }
èŠç¹ïŒ
-
ããã¯æ§æã瀺ãããã®åçŽåãããäŸã§ããããšã«æ³šæããŠãã ãããé·ãå®è¡ããããæäœãæ¬ç©ã®äžŠè¡åŠçã¯ããã«ã¯å«ãŸããŸããã
-
The "async" keyword is syntactic sugar. The compiler replaces the return type with a future.
-
ã³ã³ãã€ã©ã«å¯ŸããŠãè¿ãããfutureã®å€ããã®åŸã©ãæ±ãã¹ãããšãããè¿œå ã®æ瀺ãå«ããªãéãã
main
ãasyncã«ããããšã¯ã§ããŸããã -
You need an executor to run async code.
block_on
blocks the current thread until the provided future has run to completion. -
.await
ã¯éåæçã«ä»ã®æäœã®å®äºãåŸ ã¡ãŸããblock_on
ãšã¯ç°ãªãã.await
ã¯çŸåšã®ã¹ã¬ããããããã¯ããŸããã -
.await
can only be used inside anasync
function (or block; these are introduced later).
Future
Future
ã¯ãã¬ã€ãã§ããããŸã å®äºããŠãªããããããªãæäœãè¡šçŸãããªããžã§ã¯ãã«ããå®è£
ãããŸããFutureã¯ããŒãªã³ã°ãããããšããããpoll
ã¯Poll
ãè¿ããŸãã
#![allow(unused)] fn main() { use std::pin::Pin; use std::task::Context; pub trait Future { type Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; } pub enum Poll<T> { Ready(T), Pending, } }
éåæã®é¢æ°ã¯impl Future
ãè¿ããŸããèªåã§å®çŸ©ããåã«å¯ŸããŠFuture
ãå®è£
ããããšãïŒããŸããªãããšã§ããïŒå¯èœã§ããäŸãã°ãtokio::spawn
ããè¿ãããJoinHandle
ã¯Future
ãå®è£
ããããšã«ãããjoinããããšãå¯èœã«ããŠããŸãã
Futureã«é©çšããã.await
ããŒã¯ãŒãã¯ããã®Futureã®æºåãã§ãããŸã§ãçŸåšã®éåæã®é¢æ°ã®äžæåæ¢ãèµ·ããããããŠãã®åºåãè©äŸ¡ããŸãã
-
Future
ãšPoll
ã®åã¯ãŸãã«ç€ºãããããã«å®è£ ãããŸã; ããã¥ã¡ã³ãã®å ·äœçãªå®è£ ãèŠãã«ã¯ãªã³ã¯ãã¯ãªãã¯ããŠãã ããã -
Pin
ãšContext
ã«ã€ããŠã¯è©³ããã¯æ±ããŸããããªããªããæ°ããéåæã®ããªããã£ããäœãããããéåæã®ã³ãŒããæžãããšã«æã ã¯éç¹ã眮ãã€ããã ããã§ããç°¡æœã«ã¯ä»¥äžã§èª¬æãããŸãïŒ-
Context
ã¯ãç¹å®ã®ã€ãã³ããçºçããæã«ãFutureãèªåèªèº«ãåã³ããŒãªã³ã°ãããããã«ã¹ã±ãžã¥ãŒã«ããããšãå¯èœã«ããŸãã -
Pin
ã¯futureãžã®ãã€ã³ã¿ãæå¹ã§ããç¶ããããã«ãFutureãã¡ã¢ãªã®äžã§ç§»åãããªãããšã確å®ã«ããŸããããã¯ãåç §ã.await
ã®åŸã«æå¹ã§ããç¶ããããã«å¿ èŠã§ãã
-
ã©ã³ã¿ã€ã
_runtime_ã¯éåæãªæŒç®ïŒreactorïŒã®ãµããŒããæäŸãããŸããfutureãå®è¡ããããšïŒexecutorïŒãæ åœããŠããŸããRustã«ã¯ããã«ãã€ã³ãã®ã©ã³ã¿ã€ã ã¯ãããŸããããããã€ãã®ã©ã³ã¿ã€ã ã®éžæè¢ããããŸã:
- Tokio: performant, with a well-developed ecosystem of functionality like Hyper for HTTP or Tonic for gRPC.
- async-std: aims to be a "std for async", and includes a basic runtime in
async::task
. - smol: simple and lightweight
ããã€ãã®ãã巚倧ãªã¢ããªã±ãŒã·ã§ã³ã¯ãç¬èªã®ã©ã³ã¿ã€ã ãåããŠããŸããäŸãã°Fuchsiaã¯ãã®ãããªãã®ããã§ã«åããŠããŸãã
-
äžã§æããããã©ã³ã¿ã€ã ã®ãã¡ãTokioã®ã¿ãRustãã¬ã€ã°ã©ãŠã³ãã§ãµããŒããããŠããŸãããã®ãã¬ã€ã°ã©ãŠã³ãã§ã¯ãããªãå ¥åºåæäœãèš±å¯ãããŠããªãããã倧æµã®èå³æ·±ãéåæã®ããããã¯ããã¬ã€ã°ã©ãŠã³ãã§å®è¡ããããšã¯ã§ããŸããã
-
Futureã¯ãããŒãªã³ã°ãè¡ããšã°ãŒãã¥ãŒã¿ã®ååšãªãã«ã¯äœãè¡ããªãïŒå ¥åºåæäœããå§ããªãïŒãšããç¹ã§ãæ æ°ãã§ããäŸãã°ãããã¯ããšã°ãŒãã¥ãŒã¿ããªããšãæåŸãŸã§å®è¡ãããJavaScriptã®Promiseãšã¯ç°ãªããŸãã
Tokio
Tokio provides:
- éåæã®ã³ãŒããå®è¡ããããã®ãã«ãã¹ã¬ããã®ã©ã³ã¿ã€ã ã
- æšæºã©ã€ãã©ãªã®éåæããŒãžã§ã³ã
- 倧ããªã©ã€ãã©ãªã®ãšã³ã·ã¹ãã ã
use tokio::time; async fn count_to(count: i32) { for i in 0..count { println!("Count in task: {i}!"); time::sleep(time::Duration::from_millis(5)).await; } } #[tokio::main] async fn main() { tokio::spawn(count_to(10)); for i in 0..5 { println!("Main task: {i}"); time::sleep(time::Duration::from_millis(5)).await; } }
-
tokio::main
ã®ãã¯ãã«ãããmain
ã®éåæåŠçãäœãããšãã§ããŸãã -
spawn
é¢æ°ã¯æ°ãã䞊è¡ã®ãã¿ã¹ã¯ããäœæããŸãã -
泚æïŒ
spawn
ã¯Future
ãåŒæ°ã«åããããcount_to
ã«å¯ŸããŠ.await
ãåŒã¶ããšã¯ãããŸããã
ãããªãæ¢æ±:
-
ã©ãããŠ
count_to
ã¯ïŒéåžžã¯ïŒ10ã«èŸ¿ãçããªãã®ã§ããããïŒããã¯éåæåŠçã®ãã£ã³ã»ã«ã®äŸã§ããtokio::spawn
ã¯å®äºãŸã§åŸ æ©ããããã®ãã³ãã©ãè¿ããŸãã -
ããã»ã¹ãæ°ããäœã代ããã«ã
count_to(10).await
ãè©ŠããŠã¿ãŠãã ããã -
tokio::spawn
ããè¿ãããã¿ã¹ã¯ãåŸ æ©ããŠã¿ãŠãã ããã
ã¿ã¹ã¯
Rust ã«ã¯ã軜éã®ã¹ã¬ãã圢åŒã®äžçš®ã§ããã¿ã¹ã¯ã·ã¹ãã ããããŸãã
ã¿ã¹ã¯ã«ã¯ãåäžã®ãããã¬ãã«ã®futureããããããã¯ãšã°ãŒãã¥ãŒã¿ãå
ã«é²ãããã«ããŒãªã³ã°ãã察象ãšãªããŸãããã®futureã«ã¯äžã€ãŸãã¯è€æ°ã®futureããã¹ããããŠããããšãããããããã¬ãã«ã®futureã®poll
ã¡ãœãããããŒãªã³ã°ããããšã«ãªãã倧ãŸãã«ã¯ã³ãŒã«ã¹ã¿ãã¯ã«å¯Ÿå¿ãããšèšããŸããã¿ã¹ã¯ã«ããã䞊è¡åŠçã¯ãäŸãã°ç«¶åã¿ã€ããŒãå
¥åºåæäœãªã©ãè€æ°ã®åã®futureãããŒãªã³ã°ããããšã«ããå¯èœã«ãªããŸãã
use tokio::io::{self, AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpListener; #[tokio::main] async fn main() -> io::Result<()> { let listener = TcpListener::bind("127.0.0.1:0").await?; println!("listening on port {}", listener.local_addr()?.port()); loop { let (mut socket, addr) = listener.accept().await?; println!("connection from {addr:?}"); tokio::spawn(async move { socket.write_all(b"Who are you?\n").await.expect("socket error"); let mut buf = vec![0; 1024]; let name_size = socket.read(&mut buf).await.expect("socket error"); let name = std::str::from_utf8(&buf[..name_size]).unwrap().trim(); let reply = format!("Thanks for dialing in, {name}!\n"); socket.write_all(reply.as_bytes()).await.expect("socket error"); }); } }
ãã®äŸãæºåããsrc/main.rs
ã«ã³ããŒããŠãããããå®è¡ããŠã¿ãŸãããã
nc ã telnet ãªã©ã® TCP æ¥ç¶ããŒã«ã䜿çšããŠæ¥ç¶ããŠã¿ãŠãã ããã
-
äŸã®ãµãŒããŒãã©ã®ãããªç¶æ ã®æã«ãããã€ãã®ã¯ã©ã€ã¢ã³ããšæ¥ç¶ãããç¶æ ã«ããã®ãããå¯èŠåããããã«åè¬è ã«æ瀺ããŠãã ãããã©ããªã¿ã¹ã¯ãååšããŠããŸããïŒãããã®futureã¯äœã§ããïŒ
-
This is the first time we've seen an
async
block. This is similar to a closure, but does not take any arguments. Its return value is a Future, similar to anasync fn
. -
mainã®asyncãããã¯ãé¢æ°ã«ãªãã¡ã¯ã¿ããŠã
?
ã䜿ã£ããšã©ãŒãã³ããªã³ã°ãæ¹åããŠã¿ãŸãããã
ãã£ãã«ãšå¶åŸ¡ãããŒ
This segment should take about 20 minutes. It contains:
Slide | Duration |
---|---|
Asyncãã£ãã« | 10 minutes |
Join | 4 minutes |
Select | 5 minutes |
Asyncãã£ãã«
Several crates have support for asynchronous channels. For instance tokio
:
use tokio::sync::mpsc; async fn ping_handler(mut input: mpsc::Receiver<()>) { let mut count: usize = 0; while let Some(_) = input.recv().await { count += 1; println!("Received {count} pings so far."); } println!("ping_handler complete"); } #[tokio::main] async fn main() { let (sender, receiver) = mpsc::channel(32); let ping_handler_task = tokio::spawn(ping_handler(receiver)); for i in 0..10 { sender.send(()).await.expect("Failed to send ping."); println!("Sent {} pings so far.", i + 1); } drop(sender); ping_handler_task.await.expect("Something went wrong in ping handler task."); }
-
ãã£ãã«ãµã€ãºã
3
ã«å€ããŠã¿ãŠããããã©ã®ããã«åŠçã«åœ±é¿ããã確èªããŠã¿ãŸãããã -
Overall, the interface is similar to the
sync
channels as seen in the morning class. -
std::mem::drop
ã®åŒã³åºããé€ããŠã¿ãŸããããäœãèµ·ããã§ããããïŒããã¯ãªãã§ããããïŒ -
Flumeã¯ã¬ãŒãã«ã¯
sync
ãšasync
ãsend
ãšrecv
ã®äž¡æ¹ãå®è£ ãããã£ãã«ããããŸãã ããã¯å ¥åºåãšéãCPUã®åŠçã®ã¿ã¹ã¯ã®äž¡æ¹ãå«ããè€éãªã¢ããªã±ãŒã·ã§ã³ã§äŸ¿å©ã§ãã -
async
ãã£ãã«ãæ±ãããšã奜ãŸããããã®ã¯ããã£ãã«ãšç¹ããããã«ããè€éãªã³ã³ãããŒã«ãããŒãäœãããã«ããã£ãã«ãä»ã®future
ãšç¹ããããããšã§ãã
Join
Joinãšããæäœã§ã¯ãfutureã®éåã®æºåãæŽããŸã§åŸ
æ©ãããã®åŸã«çµæããŸãšããŠè¿ããŸããããã¯JavaScriptã«ããã Promise.all
ãPythonã«ãããasyncio.gather
ã«äŒŒãŠããŸãã
use anyhow::Result; use futures::future; use reqwest; use std::collections::HashMap; async fn size_of_page(url: &str) -> Result<usize> { let resp = reqwest::get(url).await?; Ok(resp.text().await?.len()) } #[tokio::main] async fn main() { let urls: [&str; 4] = [ "https://google.com", "https://httpbin.org/ip", "https://play.rust-lang.org/", "BAD_URL", ]; let futures_iter = urls.into_iter().map(size_of_page); let results = future::join_all(futures_iter).await; let page_sizes_dict: HashMap<&str, Result<usize>> = urls.into_iter().zip(results.into_iter()).collect(); println!("{page_sizes_dict:?}"); }
ãã®äŸãæºåããsrc/main.rs
ã«ã³ããŒããŠãããããå®è¡ããŠã¿ãŸãããã
-
è€æ°ã®äºãã«çŽ ãªåã®futureã«å¯ŸããŠã¯ã
std::future::join!
ãå©çšã§ããŸããããããããã€ã®futureãã³ã³ãã€ã«æã«ååšããŠããã®ããææ¡ããŠããå¿ èŠããããŸããããã¯çŸåšfutures
ã¯ã¬ãŒãã«ãããŸãããè¿ããã¡ã«std::future
ã«çµ±åãããäºå®ã§ãã -
The risk of
join
is that one of the futures may never resolve, this would cause your program to stall. -
ãŸãã
join_all
ãšjoin!
ãçµã¿åãããããšãã§ããŸããããã¯ãäŸãã°ããŒã¿ããŒã¹ã®ã¯ãšãªãšäžç·ã«httpãµãŒãã¹ãžã®å šãŠã®ãªã¯ãšã¹ããjoinããå Žåã§ããfutureã«futures::join!
ãçšããŠãtokio::time::sleep
ãè¿œå ããŠã¿ãŠãã ãããããã¯ïŒæ¬¡ã®ãã£ãã¿ãŒã§èª¬æãããselect!
ãå¿ èŠãšããïŒã¿ã€ã ã¢ãŠãã§ã¯ãããŸããããjoin!
ã®è¯ãå®æŒãšãªã£ãŠããŸãã
Select
Selectãšããæäœã§ã¯ãfutureã®éåã®ãã¡ãããããïŒã€ã®æºåãæŽããŸã§åŸ
æ©ãããã®futureãæäŸããçµæã«å¯ŸããŠå¿çããŸããããã¯JavaScriptã«ãããPromise.race
ã«äŒŒãŠããŸãããŸããPythonã«ããã asyncio.wait(task_set, return_when=asyncio.FIRST_COMPLETED)
ãšæ¯ã¹ãããšãã§ããŸãã
Similar to a match statement, the body of select!
has a number of arms, each of the form pattern = future => statement
. When a future
is ready, its return value is destructured by the pattern
. The statement
is then run with the resulting variables. The statement
result becomes the result of the select!
macro.
use tokio::sync::mpsc; use tokio::time::{sleep, Duration}; #[tokio::main] async fn main() { let (tx, mut rx) = mpsc::channel(32); let listener = tokio::spawn(async move { tokio::select! { Some(msg) = rx.recv() => println!("got: {msg}"), _ = sleep(Duration::from_millis(50)) => println!("timeout"), }; }); sleep(Duration::from_millis(10)).await; tx.send(String::from("Hello!")).await.expect("Failed to send greeting"); listener.await.expect("Listener failed"); }
-
The
listener
async block here is a common form: wait for some async event, or for a timeout. Change thesleep
to sleep longer to see it fail. Why does thesend
also fail in this situation? -
select!
is also often used in a loop in "actor" architectures, where a task reacts to events in a loop. That has some pitfalls, which will be discussed in the next segment.
èœãšãç©Ž
Async / await provides convenient and efficient abstraction for concurrent asynchronous programming. However, the async/await model in Rust also comes with its share of pitfalls and footguns. We illustrate some of them in this chapter.
This segment should take about 55 minutes. It contains:
Slide | Duration |
---|---|
ãšã°ãŒãã¥ãŒã¿ã®ããããã³ã° | 10 minutes |
Pin | 20 minutes |
Asyncãã¬ã€ã | 5 minutes |
ãã£ã³ã»ã« | 20 minutes |
ãšã°ãŒãã¥ãŒã¿ã®ãããã¯
ã»ãšãã©ã®éåæã©ã³ã¿ã€ã ã¯ãIO ã¿ã¹ã¯ã®åæå®è¡ã®ã¿ãèš±å¯ããŸããã€ãŸããCPU ãããã¯ã¿ã¹ã¯ã¯ãšã°ãŒãã¥ãŒã¿ããããã¯ããä»ã®ã¿ã¹ã¯ã®å®è¡ã劚ããŸããç°¡åãªåé¿çã¯ãå¯èœã§ããã°éåæã®åçã®ã¡ãœããã䜿çšããããšã§ãã
use futures::future::join_all; use std::time::Instant; async fn sleep_ms(start: &Instant, id: u64, duration_ms: u64) { std::thread::sleep(std::time::Duration::from_millis(duration_ms)); println!( "future {id} slept for {duration_ms}ms, finished after {}ms", start.elapsed().as_millis() ); } #[tokio::main(flavor = "current_thread")] async fn main() { let start = Instant::now(); let sleep_futures = (1..=10).map(|t| sleep_ms(&start, t, t * 10)); join_all(sleep_futures).await; }
-
ã³ãŒããç¶ããŠãã¹ãªãŒããåæã§ã¯ãªãé£ç¶ããŠçºçããããšã確èªããŸãã
-
"current_thread"
ãã¬ãŒããŒã¯ããã¹ãŠã®ã¿ã¹ã¯ã 1 ã€ã®ã¹ã¬ããã«é 眮ããŸããããã«ããã圱é¿ã¯ããæ確ã«ãªããŸããããã°ã¯ãŸã ãã«ãã¹ã¬ãã ãã¬ãŒããŒã«ååšããŸãã -
std::thread::sleep
ãtokio::time::sleep
ã«åãæ¿ããŠããã®çµæãåŸ ã¡ãŸãã -
ãã 1 ã€ã®ä¿®æ£çã¯ã
tokio::task::spawn_blocking
ã䜿çšããããšã§ããããã¯ãå®éã®ã¹ã¬ãããçæãããšã°ãŒãã¥ãŒã¿ããããã¯ããã«ãã®ãã³ãã«ã Future ã«å€æããŸãã -
ã¿ã¹ã¯ã¯ OS ã¹ã¬ãããšã¯ã¿ãªãã¹ãã§ã¯ãããŸãããããã㯠1 察 1 ã«å¯Ÿå¿ããŠããããã»ãšãã©ã®ãšã°ãŒãã¥ãŒã¿ã¯ãåäžã® OS ã¹ã¬ããã§å€ãã®ã¿ã¹ã¯ãå®è¡ããããšãèš±å¯ããŸããããã¯ãFFI ãä»ããŠä»ã®ã©ã€ãã©ãªãšããåãããå Žåã«ç¹ã«åé¡ãšãªããŸããFFI ã§ã¯ããã®ã©ã€ãã©ãªã¯ã¹ã¬ãã ããŒã«ã« ã¹ãã¬ãŒãžã«äŸåããŠããããç¹å®ã® OS ã¹ã¬ããïŒCUDA ãªã©ïŒã«ãããã³ã°ãããŠããå¯èœæ§ãããããã§ãããã®ãããªå Žåã¯
tokio::task::spawn_blocking
ã䜿çšããããšãããããããŸãã -
åæãã¥ãŒããã¯ã¹ã¯æ éã«äœ¿çšããŠãã ããã
.await
ã§ãã¥ãŒããã¯ã¹ãä¿æãããšãå¥ã®ã¿ã¹ã¯ããããã¯ããããã®ã¿ã¹ã¯ãåãã¹ã¬ããã§å®è¡ãããå¯èœæ§ããããŸãã
Pin
éåæãããã¯ãšé¢æ°ã¯ãFuture
ãã¬ã€ããå®è£
ããåãè¿ããŸããè¿ãããåã¯ãããŒã«ã«å€æ°ã Future ã®å
éšã«æ ŒçŽãããããŒã¿ã«å€æããã³ã³ãã€ã©å€æã®çµæã§ãã
ãããã®å€æ°ã®äžéšã¯ãä»ã®ããŒã«ã«å€æ°ãžã®ãã€ã³ã¿ãä¿æã§ããŸãããããã®ãã€ã³ã¿ãç¡å¹ã«ãªããããFutureãå¥ã®ã¡ã¢ãªäœçœ®ã«ç§»åããªãã§ãã ããã
ã¡ã¢ãªå
ã® Future åã移åããã®ãé²ãã«ã¯ãåºå®ããããã€ã³ã¿ã®ã¿ãä»ããŠããŒãªã³ã°ããããã«ããŸããPin
ã¯åç
§ã®ã©ãããŒã§ãåç
§å
ã®ã€ã³ã¹ã¿ã³ã¹ãå¥ã®ã¡ã¢ãªäœçœ®ã«ç§»åãããªãã¬ãŒã·ã§ã³ããã¹ãŠçŠæ¢ããŸãã
use tokio::sync::{mpsc, oneshot}; use tokio::task::spawn; use tokio::time::{sleep, Duration}; // äœæ¥ã¢ã€ãã ããã®å Žåãæå®ãããæéã ãã¹ãªãŒããã // `respond_on` ãã£ã³ãã«ã§ã¡ãã»ãŒãžãè¿ããŸãã #[derive(Debug)] struct Work { input: u32, respond_on: oneshot::Sender<u32>, } // ãã¥ãŒäžã®åŠçããªãã¹ã³ããŠå®è¡ããã¯ãŒã«ãŒã async fn worker(mut work_queue: mpsc::Receiver<Work>) { let mut iterations = 0; loop { tokio::select! { Some(work) = work_queue.recv() => { sleep(Duration::from_millis(10)).await; // Pretend to work. work.respond_on .send(work.input * 1000) .expect("failed to send response"); iterations += 1; } // TODO: 100 ããªç§ããšã®å埩åŠçã®åæ°ãã¬ããŒã } } } // åŠçããªã¯ãšã¹ãããåŠçãå®äºãããŸã§åŸ æ©ãããªã¯ãšã¹ãå ã async fn do_work(work_queue: &mpsc::Sender<Work>, input: u32) -> u32 { let (tx, rx) = oneshot::channel(); work_queue .send(Work { input, respond_on: tx }) .await .expect("failed to send on work queue"); rx.await.expect("failed waiting for response") } #[tokio::main] async fn main() { let (tx, rx) = mpsc::channel(10); spawn(worker(rx)); for i in 0..100 { let resp = do_work(&tx, i).await; println!("work result for iteration {i}: {resp}"); } }
-
ããã¯ã¢ã¯ã¿ãŒã®ãã¿ãŒã³ã®äžäŸã§ããã¢ã¯ã¿ãŒã¯éåžžãã«ãŒãå ã§
select!
ãåŒã³åºããŸãã -
ããã¯ãããŸã§ã®ã¬ãã¹ã³ã®äžéšããŸãšãããã®ã§ãã®ã§ãæéããããŠåŸ©ç¿ããŠãã ããã
-
_ = sleep(Duration::from_millis(100)) => { println!(..) }
ãselect!
ã«è¿œå ããã ãã§ã¯ãå®è¡ãããŸããããªãã§ããããïŒ -
代ããã«ã
loop
ã®å€åŽã§ããã® Future ãå«ãtimeout_fut
ãè¿œå ããŸãã#![allow(unused)] fn main() { let timeout_fut = sleep(Duration::from_millis(100)); loop { select! { .., _ = timeout_fut => { println!(..); }, } } }
-
ããã§ãããŸããããŸãããã³ã³ãã€ã«ãšã©ãŒã«ããããã«ã
select!
å ã®timeout_fut
ã«&mut
ãè¿œå ããŠç§»åãåé¿ããŠãããBox::pin
ã䜿çšããŸãã#![allow(unused)] fn main() { let mut timeout_fut = Box::pin(sleep(Duration::from_millis(100))); loop { select! { .., _ = &mut timeout_fut => { println!(..); }, } } }
-
This compiles, but once the timeout expires it is
Poll::Ready
on every iteration (a fused future would help with this). Update to resettimeout_fut
every time it expires:#![allow(unused)] fn main() { let mut timeout_fut = Box::pin(sleep(Duration::from_millis(100))); loop { select! { _ = &mut timeout_fut => { println!(..); timeout_fut = Box::pin(sleep(Duration::from_millis(100))); }, } } }
-
-
Box ã§ããŒãã«å²ãåœãŠãŸããå Žåã«ãã£ãŠã¯
std::pin::pin!
ïŒæè¿å®å®åãããã°ããã§ãå€ãã³ãŒãã§ã¯å€ãã®å Žåã«tokio::pin!
ã䜿çšããŸãïŒã䜿çšã§ããŸãããåå²ãåœãŠããã Future ã«äœ¿çšããããšã¯å°é£ã§ãã -
å¥ã®æ¹æ³ãšããŠã¯ã
pin
ããŸã£ãã䜿çšããã«ã100 ããªç§ããšã«oneshot
ãã£ãã«ã«éä¿¡ããå¥ã®ã¿ã¹ã¯ãçæãããšããæ¹æ³ããããŸãã -
ããèªäœãžã®ãã€ã³ã¿ãå«ãããŒã¿ã¯ãèªå·±åç §ãšåŒã°ããŸããéåžžãRust åçšãã§ãã«ãŒã¯ãåç §ãåç §å ã®ããŒã¿ããé·ãåç¶ã§ããªããããèªå·±åç §ããŒã¿ã®ç§»åãé²ããŸãããã ããéåæãããã¯ãšé¢æ°ã®ã³ãŒãå€æã¯ãåçšãã§ãã«ãŒã«ãã£ãŠæ€èšŒãããŸããã
-
Pin
ã¯åç §ã®ã©ãããŒã§ããåºå®ããããã€ã³ã¿ã䜿çšããŠããªããžã§ã¯ãããã®å Žæãã移åããããšã¯ã§ããŸããããã ããåºå®ãããŠããªããã€ã³ã¿ãä»ããŠç§»åããããšã¯å¯èœã§ãã -
Future
ãã¬ã€ãã®poll
ã¡ãœããã¯ã&mut Self
ã§ã¯ãªãPin<&mut Self>
ã䜿çšããŠã€ã³ã¹ã¿ã³ã¹ãåç §ããŸããåºå®ããããã€ã³ã¿ã§ã®ã¿åŒã³åºãããšãã§ããã®ã¯ãã®ããã§ãã
Asyncãã¬ã€ã
Async methods in traits are were stabilized in the 1.75 release. This required support for using return-position impl Trait
in traits, as the desugaring for async fn
includes -> impl Future<Output = ...>
.
However, even with the native support, there are some pitfalls around async fn
:
-
Return-position
impl Trait
captures all in-scope lifetimes (so some patterns of borrowing cannot be expressed). -
Async traits cannot be used with trait objects (
dyn Trait
support).
The async_trait crate provides a workaround for dyn
support through a macro, with some caveats:
use async_trait::async_trait; use std::time::Instant; use tokio::time::{sleep, Duration}; #[async_trait] trait Sleeper { async fn sleep(&self); } struct FixedSleeper { sleep_ms: u64, } #[async_trait] impl Sleeper for FixedSleeper { async fn sleep(&self) { sleep(Duration::from_millis(self.sleep_ms)).await; } } async fn run_all_sleepers_multiple_times( sleepers: Vec<Box<dyn Sleeper>>, n_times: usize, ) { for _ in 0..n_times { println!("Running all sleepers..."); for sleeper in &sleepers { let start = Instant::now(); sleeper.sleep().await; println!("Slept for {} ms", start.elapsed().as_millis()); } } } #[tokio::main] async fn main() { let sleepers: Vec<Box<dyn Sleeper>> = vec![ Box::new(FixedSleeper { sleep_ms: 50 }), Box::new(FixedSleeper { sleep_ms: 100 }), ]; run_all_sleepers_multiple_times(sleepers, 5).await; }
-
async_trait
ã¯ç°¡åã«äœ¿çšã§ããŸãããããŒãå²ãåœãŠã䜿çšããŠãããå®çŸããŠããŸãããã®ããŒãå²ãåœãŠã«ã¯ãããã©ãŒãã³ã¹ ãªãŒããŒãããã䌎ããŸãã -
The challenges in language support for
async trait
are too deep to describe in-depth in this class. See this blog post by Niko Matsakis if you are interested in digging deeper. See also these keywords:- RPIT: short for return-position
impl Trait
. - RPITIT: short for return-position
impl Trait
in trait (RPIT in trait).
- RPIT: short for return-position
-
Try creating a new sleeper struct that will sleep for a random amount of time and adding it to the
Vec
.
ãã£ã³ã»ã«
Future ããããããããšããã® Future ãå床ããŒãªã³ã°ããããšã¯ã§ããŸãããããã¯ãã£ã³ã»ã«ãšåŒã°ããã©ã® await
ãã€ã³ãã§ãçºçããå¯èœæ§ããããŸãããã®ãããFuture ããã£ã³ã»ã«ãããå Žåã§ããã·ã¹ãã ãæ£åžžã«åäœããããã«ããŠããå¿
èŠããããŸããããšãã°ããããããã¯ãããŒã¿ã®æ¶å€±ããã£ãŠã¯ãªããŸããã
use std::io; use std::time::Duration; use tokio::io::{AsyncReadExt, AsyncWriteExt, DuplexStream}; struct LinesReader { stream: DuplexStream, } impl LinesReader { fn new(stream: DuplexStream) -> Self { Self { stream } } async fn next(&mut self) -> io::Result<Option<String>> { let mut bytes = Vec::new(); let mut buf = [0]; while self.stream.read(&mut buf[..]).await? != 0 { bytes.push(buf[0]); if buf[0] == b'\n' { break; } } if bytes.is_empty() { return Ok(None); } let s = String::from_utf8(bytes) .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "not UTF-8"))?; Ok(Some(s)) } } async fn slow_copy(source: String, mut dest: DuplexStream) -> io::Result<()> { for b in source.bytes() { dest.write_u8(b).await?; tokio::time::sleep(Duration::from_millis(10)).await } Ok(()) } #[tokio::main] async fn main() -> io::Result<()> { let (client, server) = tokio::io::duplex(5); let handle = tokio::spawn(slow_copy("hi\nthere\n".to_owned(), client)); let mut lines = LinesReader::new(server); let mut interval = tokio::time::interval(Duration::from_millis(60)); loop { tokio::select! { _ = interval.tick() => println!("tick!"), line = lines.next() => if let Some(l) = line? { print!("{}", l) } else { break }, } } handle.await.unwrap()?; Ok(()) }
-
ã³ã³ãã€ã©ã§ã¯ãã£ã³ã»ã«å®å šæ§ã確ä¿ã§ããŸãããAPI ããã¥ã¡ã³ããèªã¿ã
async fn
ãä¿æããç¶æ ãèæ ®ããå¿ èŠããããŸãã -
panic
ã?
ãšã¯ç°ãªãããã£ã³ã»ã«ã¯ïŒãšã©ãŒåŠçã§ã¯ãªãïŒéåžžã®å¶åŸ¡ãããŒã®äžéšã§ãã -
ãã®äŸã§ã¯ãæååã®äžéšã倱ãããŠããŸãã
-
tick()
åå²ãå ã«çµäºãããã³ã«ãnext()
ãšãã®buf
ããããããããŸãã -
buf
ãæ§é äœã®äžéšã«ããããšã§ãLinesReader
ã«ãã£ã³ã»ã«å®å šæ§ãæãããããšãã§ããŸãã#![allow(unused)] fn main() { struct LinesReader { stream: DuplexStream, bytes: Vec<u8>, buf: [u8; 1], } impl LinesReader { fn new(stream: DuplexStream) -> Self { Self { stream, bytes: Vec::new(), buf: [0] } } async fn next(&mut self) -> io::Result<Option<String>> { // buf ãš bytes ã®å é ã« self ãä»å ããŸãã // ... let raw = std::mem::take(&mut self.bytes); let s = String::from_utf8(raw) .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "not UTF-8"))?; // ... } } }
-
-
Interval::tick
ã¯ããã£ãã¯ããé ä¿¡æžã¿ããã©ããã远跡ããŠãããããå®å šã«ãã£ã³ã»ã«ã§ããŸãã -
AsyncReadExt::read
ã¯ãããŒã¿ãè¿ãããããŒã¿ãèªã¿åããªããã®ããããã§ãããããå®å šã«ãã£ã³ã»ã«ã§ããŸãã -
AsyncBufReadExt::read_line
ã¯ãã®äŸãšé¡äŒŒããŠãããå®å šã«ãã£ã³ã»ã«ã§ããŸããã詳现ãšä»£æ¿æ¹æ³ã«ã€ããŠã¯ãããã¥ã¡ã³ããã芧ãã ããã
ç·Žç¿åé¡
This segment should take about 1 hour and 10 minutes. It contains:
Slide | Duration |
---|---|
é£äºããå²åŠè | 20 minutes |
ãããŒããã£ã¹ãã»ãã£ããã¢ã㪠| 30 minutes |
解ç | 20 minutes |
Dining Philosophers --- Async
See dining philosophers for a description of the problem.
åãšåæ§ã«ããã®æŒç¿ã§ãããŒã«ã«ã® Cargo ã€ã³ã¹ããŒã« ãå¿
èŠã§ãã以äžã®ã³ãŒãã src/main.rs
ãšãããã¡ã€ã«ã«ã³ããŒãã空æ¬ãåããŠãcargo run
ããããããã¯ããªãããšã確èªããŸãã
use std::sync::Arc; use tokio::sync::{mpsc, Mutex}; use tokio::time; struct Fork; struct Philosopher { name: String, // left_fork: ... // right_fork: ... // thoughts: ... } impl Philosopher { async fn think(&self) { self.thoughts .send(format!("Eureka! {} has a new idea!", &self.name)) .await .unwrap(); } async fn eat(&self) { // Keep trying until we have both forks println!("{} is eating...", &self.name); time::sleep(time::Duration::from_millis(5)).await; } } static PHILOSOPHERS: &[&str] = &["Socrates", "Hypatia", "Plato", "Aristotle", "Pythagoras"]; #[tokio::main] async fn main() { // ãã©ãŒã¯ãäœæãã // å²åŠè ãäœæãã // å²åŠè ãæ玢ãšé£äºãè¡ãããã«ãã // å²åŠè ã®æ玢ãåºåãã }
ä»åã¯éåæ Rust ã䜿çšãããããtokio
äŸåé¢ä¿ãå¿
èŠã«ãªããŸãã次㮠Cargo.toml
ã䜿çšã§ããŸãã
[package]
name = "dining-philosophers-async-dine"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1.26.0", features = ["sync", "time", "macros", "rt-multi-thread"] }
ãŸããä»åºŠã¯ tokio
ã¯ã¬ãŒãã® Mutex
ã¢ãžã¥ãŒã«ãš mpsc
ã¢ãžã¥ãŒã«ã䜿çšããå¿
èŠãããããšã«ã泚æããŠãã ããã
- å®è£ ãã·ã³ã°ã«ã¹ã¬ããã«ã§ããŸããïŒ
ãããŒããã£ã¹ãã»ãã£ããã¢ããª
ãã®æŒç¿ã§ã¯ãæ°ãã«èº«ã«ä»ããç¥èã掻ãããŠãããŒããã£ã¹ã ãã£ãã ã¢ããªãå®è£ ããŸããã¯ã©ã€ã¢ã³ããæ¥ç¶ããŠã¡ãã»ãŒãžãå ¬éãããã£ãã ãµãŒããŒããããŸããã¯ã©ã€ã¢ã³ãã¯æšæºå ¥åãããŠãŒã¶ãŒ ã¡ãã»ãŒãžãèªã¿åãããµãŒããŒã«éä¿¡ããŸãããã£ãã ãµãŒããŒã¯åä¿¡ããåã¡ãã»ãŒãžããã¹ãŠã®ã¯ã©ã€ã¢ã³ãã«ãããŒããã£ã¹ãããŸãã
ãã®ããã«ããµãŒããŒäžã® ãããŒããã£ã¹ã ãã£ã³ãã« ã䜿çšããã¯ã©ã€ã¢ã³ããšãµãŒããŒéã®éä¿¡ã«ã¯ tokio_websockets
ã䜿çšããŸãã
æ°ãã Cargo ãããžã§ã¯ããäœæãã次ã®äŸåé¢ä¿ãè¿œå ããŸãã
Cargo.toml:
[package]
name = "chat-async"
version = "0.1.0"
edition = "2021"
[dependencies]
futures-util = { version = "0.3.31", features = ["sink"] }
http = "1.1.0"
tokio = { version = "1.41.0", features = ["full"] }
tokio-websockets = { version = "0.10.1", features = ["client", "fastrand", "server", "sha1_smol"] }
å¿ èŠãª API
tokio
ãš tokio_websockets
ã®ä»¥äžã®é¢æ°ãå¿
èŠã«ãªããŸããå°ãæéããã㊠API ã«å¯Ÿããç解ãæ·±ããŠãã ããã
WebSocketStream
ã«ãã£ãŠå®è£ ããã StreamExt::next(): Websocket Stream ããã®ã¡ãã»ãŒãžãéåæã§èªã¿åããŸããWebSocketStream
ã«ãã£ãŠå®è£ ããã SinkExt::send(): Websocket Stream äžã§ã¡ãã»ãŒãžãéåæã§éä¿¡ããŸãã- Lines::next_line(): æšæºå ¥åããã®ãŠãŒã¶ãŒ ã¡ãã»ãŒãžãéåæã§èªã¿åããŸãã
- Sender::subscribe(): ãããŒããã£ã¹ã ãã£ã³ãã«ããµãã¹ã¯ã©ã€ãããŸãã
2 ã€ã®ãã€ããª
éåžžãCargo ãããžã§ã¯ãã«å«ããããšãã§ããã®ã¯ 1 ã€ã®ãã€ããªãš 1 ã€ã® src/main.rs
ãã¡ã€ã«ã®ã¿ã§ãããã®ãããžã§ã¯ãã«ã¯ 2 ã€ã®ãã€ããªãå¿
èŠã§ãã1 ã€ã¯ã¯ã©ã€ã¢ã³ãçšããã 1 ã€ã¯ãµãŒããŒçšã§ãã2 ã€ã®ç¬ç«ãã Cargo ãããžã§ã¯ããäœæããããšãã§ããŸãããããã§ã¯ 1 ã€ã® Cargo ãããžã§ã¯ãã« 2 ã€ã®ãã€ããªãå
¥ããŸãããã®ããã«ã¯ãã¯ã©ã€ã¢ã³ããšãµãŒããŒã®ã³ãŒãã src/bin
ã«é
眮ããå¿
èŠããããŸãïŒããã¥ã¡ã³ã ãã芧ãã ããïŒã
次ã®ãµãŒããŒãšã¯ã©ã€ã¢ã³ãã®ã³ãŒãããããããsrc/bin/server.rs
ãš src/bin/client.rs
ã«ã³ããŒããŸããããã§ã®ã¿ã¹ã¯ã¯ã以äžã§èª¬æããããã«ããããã®ãã¡ã€ã«ãå®æãããããšã§ãã
src/bin/server.rs:
use futures_util::sink::SinkExt; use futures_util::stream::StreamExt; use std::error::Error; use std::net::SocketAddr; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::broadcast::{channel, Sender}; use tokio_websockets::{Message, ServerBuilder, WebSocketStream}; async fn handle_connection( addr: SocketAddr, mut ws_stream: WebSocketStream<TcpStream>, bcast_tx: Sender<String>, ) -> Result<(), Box<dyn Error + Send + Sync>> { // TODO: ãã³ãã«ã€ããŠã¯ã以äžã®ã¿ã¹ã¯ã®èª¬æãã芧ãã ããã } #[tokio::main] async fn main() -> Result<(), Box<dyn Error + Send + Sync>> { let (bcast_tx, _) = channel(16); let listener = TcpListener::bind("127.0.0.1:2000").await?; println!("listening on port 2000"); loop { let (socket, addr) = listener.accept().await?; println!("New connection from {addr:?}"); let bcast_tx = bcast_tx.clone(); tokio::spawn(async move { // æªå 工㮠TCP ã¹ããªãŒã ã WebSocket ã«ã©ããããŸãã let ws_stream = ServerBuilder::new().accept(socket).await?; handle_connection(addr, ws_stream, bcast_tx).await }); } }
src/bin/client.rs:
use futures_util::stream::StreamExt; use futures_util::SinkExt; use http::Uri; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio_websockets::{ClientBuilder, Message}; #[tokio::main] async fn main() -> Result<(), tokio_websockets::Error> { let (mut ws_stream, _) = ClientBuilder::from_uri(Uri::from_static("ws://127.0.0.1:2000")) .connect() .await?; let stdin = tokio::io::stdin(); let mut stdin = BufReader::new(stdin).lines(); // TODO: ãã³ãã«ã€ããŠã¯ã以äžã®ã¿ã¹ã¯ã®èª¬æãã芧ãã ããã }
ãã€ããªã®å®è¡
次ã®ã³ãã³ãã§ãµãŒããŒãå®è¡ããŸãã
cargo run --bin server
次ã®ã³ãã³ãã§ã¯ã©ã€ã¢ã³ããå®è¡ããŸãã
cargo run --bin client
ã¿ã¹ã¯
src/bin/server.rs
ã«handle_connection
é¢æ°ãå®è£ ããŸãã- ãã³ã: 2 ã€ã®ã¿ã¹ã¯ãé£ç¶ã«ãŒãã§åæã«å®è¡ããã«ã¯ã
tokio::select!
ã䜿çšããŸãã1 ã€ã®ã¿ã¹ã¯ã¯ãã¯ã©ã€ã¢ã³ãããã¡ãã»ãŒãžãåä¿¡ããŠãããŒããã£ã¹ãããŸãããã 1 ã€ã®ã¿ã¹ã¯ã¯ããµãŒããŒã§åä¿¡ããã¡ãã»ãŒãžãã¯ã©ã€ã¢ã³ãã«éä¿¡ããŸãã
- ãã³ã: 2 ã€ã®ã¿ã¹ã¯ãé£ç¶ã«ãŒãã§åæã«å®è¡ããã«ã¯ã
src/bin/client.rs
ã®ã¡ã€ã³é¢æ°ãå®æãããŸãã- ãã³ã: åã®äŸãšåæ§ã«ã
tokio::select!
ãé£ç¶ã«ãŒãã§äœ¿çšããïŒ1ïŒæšæºå ¥åãããŠãŒã¶ãŒ ã¡ãã»ãŒãžãèªã¿åã£ãŠãµãŒããŒã«éä¿¡ããã¿ã¹ã¯ãšãïŒ2ïŒãµãŒããŒããã¡ãã»ãŒãžãåä¿¡ããŠãŠãŒã¶ãŒã«è¡šç€ºããã¿ã¹ã¯ãåæã«å®è¡ããŸãã
- ãã³ã: åã®äŸãšåæ§ã«ã
- çç¥å¯: å®äºããããã¡ãã»ãŒãžã®éä¿¡è 以å€ã®ãã¹ãŠã®ã¯ã©ã€ã¢ã³ãã«ã¡ãã»ãŒãžããããŒããã£ã¹ãããããã«ã³ãŒããå€æŽããŸãã
解ç
Dining Philosophers --- Async
use std::sync::Arc; use tokio::sync::{mpsc, Mutex}; use tokio::time; struct Fork; struct Philosopher { name: String, left_fork: Arc<Mutex<Fork>>, right_fork: Arc<Mutex<Fork>>, thoughts: mpsc::Sender<String>, } impl Philosopher { async fn think(&self) { self.thoughts .send(format!("Eureka! {} has a new idea!", &self.name)) .await .unwrap(); } async fn eat(&self) { // Keep trying until we have both forks // Pick up forks... let _left_fork = self.left_fork.lock().await; let _right_fork = self.right_fork.lock().await; println!("{} is eating...", &self.name); time::sleep(time::Duration::from_millis(5)).await; // ããã§ããã¯ããããããããŸãã } } static PHILOSOPHERS: &[&str] = &["Socrates", "Hypatia", "Plato", "Aristotle", "Pythagoras"]; #[tokio::main] async fn main() { // ãã©ãŒã¯ãäœæãã let mut forks = vec![]; (0..PHILOSOPHERS.len()).for_each(|_| forks.push(Arc::new(Mutex::new(Fork)))); // å²åŠè ãäœæãã let (philosophers, mut rx) = { let mut philosophers = vec![]; let (tx, rx) = mpsc::channel(10); for (i, name) in PHILOSOPHERS.iter().enumerate() { let mut left_fork = Arc::clone(&forks[i]); let mut right_fork = Arc::clone(&forks[(i + 1) % PHILOSOPHERS.len()]); if i == PHILOSOPHERS.len() - 1 { std::mem::swap(&mut left_fork, &mut right_fork); } philosophers.push(Philosopher { name: name.to_string(), left_fork, right_fork, thoughts: tx.clone(), }); } (philosophers, rx) // tx ã¯ããã§ãããããããã®ã§ãåŸã§æ瀺çã«åé€ããå¿ èŠã¯ãããŸããã }; // å²åŠè ãæ玢ãšé£äºãè¡ãããã«ãã for phil in philosophers { tokio::spawn(async move { for _ in 0..100 { phil.think().await; phil.eat().await; } }); } // å²åŠè ã®æ玢ãåºåãã while let Some(thought) = rx.recv().await { println!("Here is a thought: {thought}"); } }
ãããŒããã£ã¹ãã»ãã£ããã¢ããª
src/bin/server.rs:
use futures_util::sink::SinkExt; use futures_util::stream::StreamExt; use std::error::Error; use std::net::SocketAddr; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::broadcast::{channel, Sender}; use tokio_websockets::{Message, ServerBuilder, WebSocketStream}; async fn handle_connection( addr: SocketAddr, mut ws_stream: WebSocketStream<TcpStream>, bcast_tx: Sender<String>, ) -> Result<(), Box<dyn Error + Send + Sync>> { ws_stream .send(Message::text("Welcome to chat! Type a message".to_string())) .await?; let mut bcast_rx = bcast_tx.subscribe(); // (1) `ws_stream` ããã¡ãã»ãŒãžãåä¿¡ããŠãããŒããã£ã¹ãããã¿ã¹ã¯ãšã // ïŒ2ïŒ`bcast_rx` ã§ã¡ãã»ãŒãžãåä¿¡ããŠã¯ã©ã€ã¢ã³ãã«éä¿¡ãããã¿ã¹ã¯ã // åæã«å®è¡ããããã®é£ç¶ã«ãŒãã loop { tokio::select! { incoming = ws_stream.next() => { match incoming { Some(Ok(msg)) => { if let Some(text) = msg.as_text() { println!("From client {addr:?} {text:?}"); bcast_tx.send(text.into())?; } } Some(Err(err)) => return Err(err.into()), None => return Ok(()), } } msg = bcast_rx.recv() => { ws_stream.send(Message::text(msg?)).await?; } } } } #[tokio::main] async fn main() -> Result<(), Box<dyn Error + Send + Sync>> { let (bcast_tx, _) = channel(16); let listener = TcpListener::bind("127.0.0.1:2000").await?; println!("listening on port 2000"); loop { let (socket, addr) = listener.accept().await?; println!("New connection from {addr:?}"); let bcast_tx = bcast_tx.clone(); tokio::spawn(async move { // æªå 工㮠TCP ã¹ããªãŒã ã WebSocket ã«ã©ããããŸãã let ws_stream = ServerBuilder::new().accept(socket).await?; handle_connection(addr, ws_stream, bcast_tx).await }); } }
src/bin/client.rs:
use futures_util::stream::StreamExt; use futures_util::SinkExt; use http::Uri; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio_websockets::{ClientBuilder, Message}; #[tokio::main] async fn main() -> Result<(), tokio_websockets::Error> { let (mut ws_stream, _) = ClientBuilder::from_uri(Uri::from_static("ws://127.0.0.1:2000")) .connect() .await?; let stdin = tokio::io::stdin(); let mut stdin = BufReader::new(stdin).lines(); // ã¡ãã»ãŒãžã®åæéåä¿¡ã®ããã®ç¶ç¶çãªã«ãŒãã loop { tokio::select! { incoming = ws_stream.next() => { match incoming { Some(Ok(msg)) => { if let Some(text) = msg.as_text() { println!("From server: {}", text); } }, Some(Err(err)) => return Err(err.into()), None => return Ok(()), } } res = stdin.next_line() => { match res { Ok(None) => return Ok(()), Ok(Some(line)) => ws_stream.send(Message::text(line.to_string())).await?, Err(err) => return Err(err.into()), } } } } }
ããããšãããããŸããïŒ
Comprehensive Rust ðŠ! ãåè¬ããã ãããããšãããããŸããã
ãããŸã§å€ãã®ããšãåŠãã§ããŸãããããã®ã³ãŒã¹ã¯å®ç§ã§ã¯ãªããããééããèŠã€ããå Žåãæ¹åã®ã¢ã€ãã¢ãããå Žå㯠GitHub ã§ãç¥ãããã ãããçããããã®ãã£ãŒãããã¯ããåŸ ã¡ããŠããŸãã
çšèªé
以äžã¯ãRust ã®å€ãã®çšèªãç°¡åã«å®çŸ©ããããšãç®çãšããçšèªéã§ãã翻蚳æã«çšèªãè±èªã®åæã«é¢é£ä»ããã®ã«ã圹ç«ã¡ãŸãã
- allocate:
Dynamic memory allocation on the heap. - åŒæ°ïŒargumentïŒ:
é¢æ°ãŸãã¯ã¡ãœããã«æž¡ãããæ å ±ã - associated type:
A type associated with a specific trait. Useful for defining the relationship between types. - ãã¢ã¡ã¿ã« RustïŒBare-metal RustïŒ:
äœã¬ãã«ã® Rust éçºãå€ãã®å Žåããªãã¬ãŒãã£ã³ã° ã·ã¹ãã ã®ãªãã·ã¹ãã ã«ãããã€ãããŸãããã¢ã¡ã¿ã« Rust ãã芧ãã ããã - block:
See Blocks and scope. - borrow:
See Borrowing. - åçšãã§ãã«ãŒïŒborrow checkerïŒ:
Rust ã³ã³ãã€ã©ã®äžéšããã¹ãŠã®åçšãæå¹ãã©ããããã§ãã¯ããŸãã - äžãã£ãïŒbraceïŒ:
{
and}
ããããã¯ãåºåããŸãã - ãã«ãïŒbuildïŒ:
ãœãŒã¹ã³ãŒããå®è¡å¯èœãªã³ãŒããŸãã¯äœ¿çšå¯èœãªããã°ã©ã ã«å€æããããã»ã¹ã - åŒã³åºãïŒcallïŒ:
é¢æ°ãŸãã¯ã¡ãœãããåŒã³åºããŸãã - ãã£ã³ãã«ïŒchannelïŒ:
ã¹ã¬ããé ã§ã¡ãã»ãŒãžãå®å šã«æž¡ãããã«äœ¿çšãããŸãã - Comprehensive Rust ðŠ:
ãã®ã³ãŒã¹ã¯ããŸãšã㊠Comprehensive Rust ðŠ ãšåŒã³ãŸãã - åæå®è¡ïŒconcurrencyïŒ:
è€æ°ã®ã¿ã¹ã¯ãŸãã¯ããã»ã¹ãåæã«å®è¡ããããšãæããŸãã - Concurrency in Rust:
See Concurrency in Rust. - å®æ°ïŒconstantïŒ:
ããã°ã©ã ã®å®è¡äžã«å€æŽãããªãå€ã - å¶åŸ¡ãããŒïŒcontrol flowïŒ:
åã ã®ã¹ããŒãã¡ã³ããŸãã¯åœä»€ãããã°ã©ã å ã§å®è¡ãããé åºã - ã¯ã©ãã·ã¥ïŒcrashïŒ:
äºæããªãå¶åŸ¡äžèœãªãšã©ãŒãŸãã¯çµäºã - åæåïŒenumerationïŒ:
è€æ°ã®ååä»ãå®æ°ã®ãã¡ã® 1 ã€ãä¿æããããŒã¿åãé¢é£ããã¿ãã«ãŸãã¯æ§é äœã䌎ãå ŽåããããŸãã - ãšã©ãŒïŒerrorïŒ:
æ³å®ãããåäœããéžè±ãããäºæããªãæ¡ä»¶ãŸãã¯çµæã - ãšã©ãŒåŠçïŒerror handlingïŒ:
ããã°ã©ã ã®å®è¡äžã«çºçãããšã©ãŒã管çããããã«å¯Ÿå¿ããããã»ã¹ã - æŒç¿ïŒexercise:ïŒ:
ããã°ã©ãã³ã° ã¹ãã«ã®åäžãšãã¹ããç®çãšããã¿ã¹ã¯ãŸãã¯åé¡ã - é¢æ°ïŒfunctionïŒ:
ç¹å®ã®ã¿ã¹ã¯ãå®è¡ããåå©çšå¯èœãªã³ãŒããããã¯ã - ã¬ããŒãž ã³ã¬ã¯ã¿ïŒgarbage collectorïŒ:
䜿çšãããªããªã£ããªããžã§ã¯ããå æããŠããã¡ã¢ãªãèªåçã«è§£æŸããã¡ã«ããºã ã - ãžã§ããªã¯ã¹ïŒgenericsïŒ:
åã®ãã¬ãŒã¹ãã«ãã䜿çšããŠã³ãŒããèšè¿°ããããŸããŸãªããŒã¿åã§ã³ãŒããåå©çšã§ããããã«ããæ©èœã - äžå€ïŒimmutableïŒ:
äœæåŸã«å€æŽã§ããªãããšã - çµ±åãã¹ãïŒintegration testïŒ:
ã·ã¹ãã ã®ããŸããŸãªéšåãã³ã³ããŒãã³ãéã®çžäºäœçšãæ€èšŒãããã¹ãã®äžçš®ã - ããŒã¯ãŒãïŒkeywordïŒ:
ç¹å®ã®æå³ãæã¡ãèå¥åãšããŠäœ¿çšã§ããªããããã°ã©ãã³ã°èšèªã®äºçŽèªã - ã©ã€ãã©ãªïŒlibraryïŒ:
ããã°ã©ã ã§äœ¿çšã§ããããªã³ã³ãã€ã«æžã¿ã®ã«ãŒãã³ãŸãã¯ã³ãŒãã®ã³ã¬ã¯ã·ã§ã³ã - ãã¯ãïŒmacroïŒ:
Rust ãã¯ãã¯ååã«!
ãå«ããããšã§èªèã§ããŸãããã¯ãã¯ãéåžžã®é¢æ°ã§ã¯äžååãªå Žåã«äœ¿çšãããŸããå žåçãªäŸãformat!
ã§ããããã¯å¯å€é·åŒæ°ãåããŸãããRust é¢æ°ã§ã¯ãµããŒããããŠããŸããã main
é¢æ°ïŒmain
functionïŒ:
Rust ããã°ã©ã ã®å®è¡ã¯main
é¢æ°ã§éå§ãããŸãã- äžèŽïŒmatchïŒ:
åŒã®å€ã«å¯Ÿãããã¿ãŒã³ ãããã³ã°ãå¯èœã«ãããRust ã®å¶åŸ¡ãããŒæ§é ã - ã¡ã¢ãªãªãŒã¯ïŒmemory leakïŒ:
ããã°ã©ã ã§äžèŠã«ãªã£ãã¡ã¢ãªã®è§£æŸã«å€±æããã¡ã¢ãªäœ¿çšéãåŸã ã«å¢å ããç¶æ³ã - ã¡ãœããïŒmethodïŒ:
Rust ã®ãªããžã§ã¯ããŸãã¯åã«é¢é£ä»ããããé¢æ°ã - ã¢ãžã¥ãŒã«ïŒmoduleïŒ:
é¢æ°ãåããã¬ã€ããªã©ã®å®çŸ©ãå«ãåå空éãRust ã§ã³ãŒããæŽçããããã«äœ¿çšãããŸãã - 移åïŒmoveïŒ:
Rust ã§ããå€æ°ããå¥ã®å€æ°ã«å€ã®æææš©ã移åããããšã - å¯å€ïŒmutableïŒ:
宣èšåŸã®å€æ°ã®å€æŽãå¯èœã«ãã Rust ã®ããããã£ã - æææš©ïŒownershipïŒ:
å€ã«é¢é£ä»ããããã¡ã¢ãªã®ç®¡çãã³ãŒãã®ã©ã®éšåãæ ãããå®çŸ©ãã Rust ã®æŠå¿µã - ãããã¯ïŒpanicïŒ:
ããã°ã©ã ã®çµäºãåŒãèµ·ãããRust ã®å埩äžèœãªãšã©ãŒç¶æ ã - ãã©ã¡ãŒã¿ïŒparameterïŒ:
é¢æ°ãŸãã¯ã¡ãœãããåŒã³åºããããšãã«æž¡ãããå€ã - ãã¿ãŒã³ïŒpatternïŒ:
Rust ã®åŒãšç §åã§ããå€ããªãã©ã«ãæ§é äœã®çµã¿åããã - ãã€ããŒãïŒpayloadïŒ:
ã¡ãã»ãŒãžãã€ãã³ãããŸãã¯ããŒã¿æ§é äœã§ä¿æãããããŒã¿ãŸãã¯æ å ±ã - ããã°ã©ã ïŒprogramïŒ:
ç¹å®ã®ã¿ã¹ã¯ãå®è¡ããããç¹å®ã®åé¡ã解決ãããããããã«ã³ã³ãã¥ãŒã¿ãå®è¡ã§ããäžé£ã®åœä»€ã - ããã°ã©ãã³ã°èšèªïŒprogramming languageïŒ:
ã³ã³ãã¥ãŒã¿ã«åœä»€ãäŒããããã«äœ¿çšãããæ£åŒãªã·ã¹ãã ïŒRust ãªã©ïŒã - ã¬ã·ãŒãïŒreceiverïŒ:
ã¡ãœãããåŒã³åºãããã€ã³ã¹ã¿ã³ã¹ãè¡šã Rust ã¡ãœããã®æåã®ãã©ã¡ãŒã¿ã - åç
§ã«ãŠã³ãïŒreference countingïŒ:
ãªããžã§ã¯ããžã®åç §ã®æ°ããã©ããã³ã°ããã«ãŠã³ãããŒãã«ãªããšãªããžã§ã¯ãã®å²ãåœãŠã解é€ããã¡ã¢ãªç®¡çæè¡ã - æ»ãå€ïŒreturnïŒ:
é¢æ°ããè¿ãããå€ã瀺ãããã«äœ¿çšããã Rust ã®ããŒã¯ãŒãã - Rust:
å®å šæ§ãããã©ãŒãã³ã¹ãåæå®è¡ã«éç¹ã眮ããã·ã¹ãã ããã°ã©ãã³ã°èšèªã - Rust Fundamentals:
Days 1 to 4 of this course. - Android ã§ã® RustïŒRust in AndroidïŒ:
Android ã§ã® Rust ãã芧ãã ããã - Chromium ã§ã® RustïŒRust in ChromiumïŒ:
Chromium ã§ã® Rust ãã芧ãã ããã - å®å
šïŒsafeïŒ:
Rust ã®æææš©ãšåçšã«é¢ããã«ãŒã«ã«åŸã£ãŠãã¡ã¢ãªé¢é£ã®ãšã©ãŒãé²æ¢ããã³ãŒããæããŸãã - ã¹ã³ãŒãïŒscopeïŒ:
å€æ°ãæå¹ãã€äœ¿çšå¯èœãªããã°ã©ã ã®é åã - æšæºã©ã€ãã©ãªïŒstandard libraryïŒ:
Rust ã®å¿ é æ©èœãæäŸããã¢ãžã¥ãŒã«ã®ã³ã¬ã¯ã·ã§ã³ã - éçïŒstaticïŒ:
éçãªå€æ°ã'static
ã©ã€ãã¿ã€ã ãæã€ã¢ã€ãã ãå®çŸ©ããããã«äœ¿çšããã Rust ã®ããŒã¯ãŒãã - string:
A data type storing textual data. See Strings for more. - æ§é äœïŒstructïŒ:
ç°ãªãåã®å€æ°ã 1 ã€ã®ååã§ã°ã«ãŒãåãã Rust ã®è€åããŒã¿åã - ãã¹ãïŒtestïŒ:
ä»ã®é¢æ°ã®æ£ããããã¹ãããé¢æ°ãå«ã Rust ã¢ãžã¥ãŒã«ã - ã¹ã¬ããïŒthreadïŒ:
åæå®è¡ãå¯èœã«ãããããã°ã©ã å ã®ç¬ç«ããå®è¡ã·ãŒã±ã³ã¹ã - ã¹ã¬ããã»ãŒãïŒthread safetyïŒ:
ãã«ãã¹ã¬ããç°å¢ã§æ£ããåäœãä¿èšŒããããã°ã©ã ã®ç¹æ§ã - ãã¬ã€ãïŒtraitïŒ:
æªç¥ã®åã«å¯ŸããŠå®çŸ©ãããã¡ãœããã®ã³ã¬ã¯ã·ã§ã³ãRust ã§ããªã¢ãŒãã£ãºã ãå®çŸããæ¹æ³ãæäŸããŸãã - ãã¬ã€ãå¢çïŒtrait boundïŒ:
ç¹å®ã®ãã¬ã€ããå®è£ ããããã«åãèŠæ±ã§ããæœè±¡åã - ã¿ãã«ïŒtupleïŒ:
ããŸããŸãªåã®å€æ°ãå«ãè€åããŒã¿åãã¿ãã« ãã£ãŒã«ãã«ã¯ååããªããåºæ°ã§ã¢ã¯ã»ã¹ããŸãã - åïŒtypeïŒ:
Rust ã®ç¹å®ã®çš®é¡ã®å€ã«å¯ŸããŠã©ã®ãªãã¬ãŒã·ã§ã³ãå®è¡ã§ããããæå®ããåé¡ã - åæšè«ïŒtype inferenceïŒ:
å€æ°ãŸãã¯åŒã®åãæšæž¬ãã Rust ã³ã³ãã€ã©ã®æ©èœã - æªå®çŸ©ã®åäœïŒundefined behaviorïŒ:
çµæãæå®ãããŠããªã Rust ã®ã¢ã¯ã·ã§ã³ãŸãã¯æ¡ä»¶ãå€ãã®å Žåãããã°ã©ã ã®äºæž¬äžèœãªåäœãåŒãèµ·ãããŸãã - å
±çšäœïŒunionïŒ:
ç°ãªãåã®å€ãäžåºŠã« 1 ã€ã ãä¿æã§ããããŒã¿åã - åäœãã¹ãïŒunit testïŒ:
Rust ã«ã¯ãå°èŠæš¡ãªåäœãã¹ããšå€§èŠæš¡ãªçµ±åãã¹ããå®è¡ããããã®çµã¿èŸŒã¿ãµããŒããä»å±ããŠããŸããåäœãã¹ã ãã芧ãã ããã - ãŠãããåïŒunit typeïŒ:
ããŒã¿ãä¿æããªãåãã¡ã³ããŒã®ãªãã¿ãã«ãšããŠèšè¿°ãããŸãã - unsafe:
The subset of Rust which allows you to trigger undefined behavior. See Unsafe Rust. - å€æ°ïŒvariableïŒ:
ããŒã¿ãæ ŒçŽããã¡ã¢ãªã®å Žæãå€æ°ã¯ã¹ã³ãŒãå ã§æå¹ã§ãã
Rust ã®ãã®ä»ã®ãªãœãŒã¹
Rust ã³ãã¥ããã£ã¯ãé«å質ãªç¡æã®ãªãœãŒã¹ããªã³ã©ã€ã³ã§å€æ°æäŸããŠããŸãã
æ£åŒãªããã¥ã¡ã³ã
Rust ãããžã§ã¯ãã¯å€ãã®ãªãœãŒã¹ããã¹ãããŠãããããã㯠Rust å šè¬ã«å¯Ÿå¿ããŠããŸãã
- The Rust Programming Language: Rust ã®æšæºçãªæžç±ã§ãç¡æã§å©çšã§ããŸããRust ã«ã€ããŠè©³ãã説æãããŠããã»ãããã«ãã§ãããããžã§ã¯ããããã€ãå«ãŸããŠããŸãã
- Rust By Example: ããŸããŸãªæ§é ã瀺ãäžé£ã®ãµã³ãã«ã䜿çšããŠãRust ã®æ§æã解説ããŠããŸããå°èŠæš¡ãªæŒç¿ãããã€ãçšæãããŠãããããã§ãµã³ãã«ã®ã³ãŒããæ¡åŒµããããæ±ããããŸãã
- Rust Standard Library: Rust ã®æšæºã©ã€ãã©ãªã®å®å šãªããã¥ã¡ã³ãã§ãã
- The Rust Reference: Rust ã®ææ³ãšã¡ã¢ãªã¢ãã«ã«ã€ããŠèª¬æããŠããæªå®æã®æžç±ã§ãã
Rust ã®å ¬åŒãµã€ãã§ãã¹ããããŠãããããå°éçãªã¬ã€ã:
- The Rustonomicon: æªå å·¥ã®ãã€ã³ã¿ã®æäœããä»ã®èšèªïŒFFIïŒãšã®ããåããªã©ãå®å šã§ãªã Rust ã«ã€ããŠèª¬æããŠããŸãã
- Asynchronous Programming in Rust: Rust Book ã®å·çåŸã«å°å ¥ãããæ°ããéåæããã°ã©ãã³ã° ã¢ãã«ã«ã€ããŠèª¬æããŠããŸãã
- The Embedded Rust Book: ãªãã¬ãŒãã£ã³ã° ã·ã¹ãã ã®ãªãçµã¿èŸŒã¿ããã€ã¹ã§ Rust ã䜿çšããæ¹æ³ã玹ä»ããŠããŸãã
éå ¬åŒã®åŠç¿ææ
Rust ã«é¢ãããã®ä»ã®ã¬ã€ããšãã¥ãŒããªã¢ã«:
- Learn Rust the Dangerous Way: é«åºŠãªç¥èãæããªã C ããã°ã©ããŒã®èŠç¹ã§ Rust ã解説ããŠããŸãã
- Rust for Embedded C Programmers: covers Rust from the perspective of developers who write firmware in C.
- Rust for professionals: ä»ã®èšèªïŒCãC++ãJavaãJavaScriptãPython ãªã©ïŒãšäžŠã¹ãŠæ¯èŒããªãããRust ã®æ§æã«ã€ããŠèª¬æããŠããŸãã
- Rust on Exercism: Rust ã®åŠç¿ã«åœ¹ç«ã€ 100 以äžã®æŒç¿ãçšæãããŠããŸãã
- Ferrous Teaching Material: Rust èšèªã®åºæ¬çãªéšåãšé«åºŠãªéšåã®äž¡æ¹ãã«ããŒãããäžé£ã®ã³ã³ãã¯ããªãã¬ãŒã³ããŒã·ã§ã³ã§ããWebAssemblyãasync / await ãªã©ã®ä»ã®ãããã¯ãæ±ã£ãŠããŸãã
- Advanced testing for Rust applications: a self-paced workshop that goes beyond Rust's built-in testing framework. It covers
googletest
, snapshot testing, mocking as well as how to write your own custom test harness. - Beginner's Series to Rust ããã³ [Take your first steps with Rust](https://docs.microsoft. com/en-us/learn/paths/rust-first-steps/): åå¿è ã®ããããããŒã察象ãšãã 2 ã€ã® Rust ã¬ã€ãã§ãã1 ã€ç®ã¯ 35 åã®åç»ã§æ§æããã2 ã€ç®ã¯ Rust ã®æ§æãšåºæ¬çãªæ§é ã説æãã 11 ã®ã¢ãžã¥ãŒã«ã§æ§æãããŠããŸãã
- Learn Rust With Entirely Too Many Linked Lists: ããã€ãã®ç°ãªãã¿ã€ãã®ãªã¹ãæ§é ã®å®è£ ãéããŠãRust ã®ã¡ã¢ãªç®¡çã«ãŒã«ãæ·±ãæãäžããŠããŸãã
Rust ã«é¢ãããã®ä»ã®æžç±ã«ã€ããŠã¯ãLittle Book of Rust Books ãã芧ãã ããã
ã¯ã¬ãžãã
ããã§çŽ¹ä»ããææã¯ãå€ãã®åªãã Rust ããã¥ã¡ã³ãã®ãœãŒã¹ã«åºã¥ããŠããŸãã圹ç«ã€ãªãœãŒã¹ã®äžèŠ§ã«ã€ããŠã¯ããã®ä»ã®ãªãœãŒã¹ ã®ããŒãžãã芧ãã ããã
Comprehensive Rust ã®ææã¯ãApache 2.0 ã©ã€ã»ã³ã¹ã®èŠçŽã«ãã䜿çšã蚱諟ãããŠããŸãã詳现ã«ã€ããŠã¯ãLICENSE
ãã芧ãã ããã
Rust by Example
äžéšã®äŸãšæŒç¿ã¯ãRust by Example ããã³ããŒããŠç·šéãããã®ã§ããã©ã€ã»ã³ã¹èŠçŽãªã©ã®è©³çŽ°ã«ã€ããŠã¯ãthird_party/rust-by-example/
ãã£ã¬ã¯ããªãåç
§ããŠãã ããã
Rust on Exercism
äžéšã®æŒç¿ã¯ãRust on Exercism ãã³ããŒããŠç·šéãããã®ã§ããã©ã€ã»ã³ã¹èŠçŽãªã©ã®è©³çŽ°ã«ã€ããŠã¯ãthird_party/rust-on-exercism/
ãã£ã¬ã¯ããªãåç
§ããŠãã ããã
CXX
C++ ãšã®çžäºéçšæ§ ã»ã¯ã·ã§ã³ã§ã¯ãCXX ã®ç»åã䜿çšããŠããŸããã©ã€ã»ã³ã¹èŠçŽãªã©ã®è©³çŽ°ã«ã€ããŠã¯ãthird_party/cxx/
ãã£ã¬ã¯ããªãåç
§ããŠãã ããã