├── .cargo └── config.toml ├── .github ├── CODEOWNERS ├── dependabot.yml ├── labeler.yml ├── pandoc │ ├── README.md │ ├── ja.yaml │ ├── ko.yaml │ ├── zh-CN.yaml │ └── zh-TW.yaml ├── typos.toml └── workflows │ ├── build.sh │ ├── build.yml │ ├── check-msgid-changes.py │ ├── check-msgid-changes.yml │ ├── install-mdbook │ └── action.yml │ ├── labeler.yml │ ├── publish.yml │ └── setup-rust-cache │ └── action.yml ├── .gitignore ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── STYLE.md ├── TRANSLATIONS.md ├── book.toml ├── dprint.json ├── mdbook-course ├── Cargo.toml ├── README.md └── src │ ├── bin │ ├── course-content.rs │ ├── course-schedule.rs │ └── mdbook-course.rs │ ├── course.rs │ ├── frontmatter.rs │ ├── lib.rs │ ├── markdown.rs │ ├── replacements.rs │ └── timing_info.rs ├── mdbook-exerciser ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── main.rs ├── po ├── ar.po ├── bn.po ├── da.po ├── de.po ├── el.po ├── es.po ├── fa.po ├── fr.po ├── id.po ├── it.po ├── ja.po ├── ko.po ├── pl.po ├── pt-BR.po ├── ro.po ├── ru.po ├── tr.po ├── uk.po ├── vi.po ├── zh-CN.po └── zh-TW.po ├── rustfmt.toml ├── src ├── README.md ├── SUMMARY.md ├── android.md ├── android │ ├── aidl.md │ ├── aidl │ │ ├── birthday-service.md │ │ ├── birthday_service │ │ │ ├── Android.bp │ │ │ ├── aidl │ │ │ │ ├── Android.bp │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── birthdayservice │ │ │ │ │ ├── BirthdayInfo.aidl │ │ │ │ │ ├── IBirthdayInfoProvider.aidl │ │ │ │ │ └── IBirthdayService.aidl │ │ │ └── src │ │ │ │ ├── client.rs │ │ │ │ ├── lib.rs │ │ │ │ └── server.rs │ │ ├── example-service │ │ │ ├── changing-definition.md │ │ │ ├── changing-implementation.md │ │ │ ├── client.md │ │ │ ├── deploy.md │ │ │ ├── implementation.md │ │ │ ├── interface.md │ │ │ ├── server.md │ │ │ ├── service-bindings.md │ │ │ └── service.md │ │ ├── types.md │ │ └── types │ │ │ ├── arrays.md │ │ │ ├── file-descriptor.md │ │ │ ├── objects.md │ │ │ ├── parcelables.md │ │ │ └── primitives.md │ ├── bpfmt.sh │ ├── build-rules.md │ ├── build-rules │ │ ├── binary.md │ │ ├── binary │ │ │ ├── Android.bp │ │ │ └── src │ │ │ │ └── main.rs │ │ ├── library.md │ │ └── library │ │ │ ├── Android.bp │ │ │ └── src │ │ │ ├── lib.rs │ │ │ └── main.rs │ ├── build_all.sh │ ├── interoperability.md │ ├── interoperability │ │ ├── cpp.md │ │ ├── cpp │ │ │ ├── android-build-cpp.md │ │ │ ├── android-build-rust.md │ │ │ ├── android-cpp-genrules.md │ │ │ ├── bridge.md │ │ │ ├── cpp-bridge.md │ │ │ ├── cpp-exception.md │ │ │ ├── generated-cpp.md │ │ │ ├── overview.svg │ │ │ ├── rust-bridge.md │ │ │ ├── rust-result.md │ │ │ ├── shared-enums.md │ │ │ ├── shared-types.md │ │ │ └── type-mapping.md │ │ ├── java.md │ │ ├── java │ │ │ ├── Android.bp │ │ │ ├── HelloWorld.java │ │ │ └── src │ │ │ │ └── lib.rs │ │ ├── with-c.md │ │ └── with-c │ │ │ ├── bindgen.md │ │ │ ├── bindgen │ │ │ ├── Android.bp │ │ │ ├── c-library.md │ │ │ ├── libbirthday.c │ │ │ ├── libbirthday.h │ │ │ ├── libbirthday_wrapper.h │ │ │ └── main.rs │ │ │ ├── c-library.md │ │ │ ├── hand-written.md │ │ │ ├── run-our-binary.md │ │ │ ├── rust-library.md │ │ │ ├── rust.md │ │ │ └── rust │ │ │ ├── analyze │ │ │ ├── Android.bp │ │ │ └── main.c │ │ │ └── libanalyze │ │ │ ├── Android.bp │ │ │ ├── analyze.h │ │ │ └── analyze.rs │ ├── logging.md │ ├── logging │ │ ├── Android.bp │ │ └── src │ │ │ └── main.rs │ ├── setup.md │ ├── testing.md │ └── testing │ │ ├── Android.bp │ │ ├── Cargo.toml │ │ ├── googletest.md │ │ ├── googletest.rs │ │ ├── mockall.rs │ │ ├── mocking.md │ │ └── src │ │ └── lib.rs ├── bare-metal.md ├── bare-metal │ ├── alloc-example │ │ ├── .cargo │ │ │ └── config.toml │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ ├── alloc.md │ ├── android.md │ ├── android │ │ └── vmbase.md │ ├── aps.md │ ├── aps │ │ ├── aarch64-rt.md │ │ ├── better-uart.md │ │ ├── better-uart │ │ │ ├── bitflags.md │ │ │ ├── driver.md │ │ │ ├── registers.md │ │ │ └── using.md │ │ ├── entry-point.md │ │ ├── examples │ │ │ ├── .cargo │ │ │ │ └── config.toml │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ ├── Makefile │ │ │ ├── image.ld │ │ │ └── src │ │ │ │ ├── asm.rs │ │ │ │ ├── entry.S │ │ │ │ ├── exceptions.S │ │ │ │ ├── exceptions.rs │ │ │ │ ├── idmap.S │ │ │ │ ├── logger.rs │ │ │ │ ├── main_improved.rs │ │ │ │ ├── main_logger.rs │ │ │ │ ├── main_minimal.rs │ │ │ │ ├── main_psci.rs │ │ │ │ ├── main_rt.rs │ │ │ │ ├── pl011.rs │ │ │ │ └── pl011_minimal.rs │ │ ├── exceptions.md │ │ ├── inline-assembly.md │ │ ├── logging.md │ │ ├── logging │ │ │ └── using.md │ │ ├── mmio.md │ │ ├── other-projects.md │ │ ├── uart.md │ │ └── uart │ │ │ ├── traits.md │ │ │ └── using.md │ ├── microcontrollers.md │ ├── microcontrollers │ │ ├── board-support.md │ │ ├── debugging.md │ │ ├── embedded-hal.md │ │ ├── examples │ │ │ ├── .cargo │ │ │ │ └── config.toml │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ ├── Embed.toml │ │ │ └── src │ │ │ │ └── bin │ │ │ │ ├── board_support.rs │ │ │ │ ├── hal.rs │ │ │ │ ├── interrupts │ │ │ │ └── mod.rs │ │ │ │ ├── minimal.rs │ │ │ │ ├── mmio.rs │ │ │ │ ├── pac.rs │ │ │ │ └── typestate.rs │ │ ├── hals.md │ │ ├── mmio.md │ │ ├── other-projects.md │ │ ├── pacs.md │ │ ├── probe-rs.md │ │ └── type-state.md │ ├── minimal.md │ ├── no_std.md │ ├── useful-crates.md │ └── useful-crates │ │ ├── aarch64-paging.md │ │ ├── allocator-example │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ │ ├── buddy_system_allocator.md │ │ ├── spin.md │ │ ├── tinyvec.md │ │ ├── zerocopy-example │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ │ └── zerocopy.md ├── borrowing.md ├── borrowing │ ├── Cargo.toml │ ├── borrowck.md │ ├── examples.md │ ├── exercise.md │ ├── interior-mutability.md │ ├── interior-mutability │ │ ├── cell.md │ │ └── refcell.md │ ├── shared.md │ └── solution.md ├── cargo.md ├── cargo │ ├── code-samples.md │ ├── running-locally.md │ └── rust-ecosystem.md ├── chromium.md ├── chromium │ ├── adding-third-party-crates.md │ ├── adding-third-party-crates │ │ ├── checking-in.md │ │ ├── configuring-cargo-toml.md │ │ ├── configuring-gnrt-config-toml.md │ │ ├── depending-on-a-crate.md │ │ ├── downloading-crates.md │ │ ├── generating-gn-build-rules.md │ │ ├── keeping-up-to-date.md │ │ ├── resolving-problems.md │ │ ├── resolving-problems │ │ │ ├── build-scripts-which-generate-code.md │ │ │ └── build-scripts-which-take-arbitrary-actions.md │ │ └── reviews-and-audits.md │ ├── build-rules.md │ ├── build-rules │ │ ├── depending.md │ │ ├── unsafe.md │ │ ├── vscode.md │ │ └── vscode.png │ ├── cargo.md │ ├── interoperability-with-cpp.md │ ├── interoperability-with-cpp │ │ ├── error-handling-png.md │ │ ├── error-handling-qr.md │ │ ├── error-handling.md │ │ ├── example-bindings.md │ │ ├── limitations-of-cxx.md │ │ └── using-cxx-in-chromium.md │ ├── policy.md │ ├── setup.md │ ├── testing.md │ └── testing │ │ ├── build-gn.md │ │ ├── chromium-import-macro.md │ │ └── rust-gtest-interop.md ├── closures.md ├── closures │ ├── capturing.md │ ├── exercise.md │ ├── exercise.rs │ ├── solution.md │ ├── syntax.md │ └── traits.md ├── concurrency │ ├── async-control-flow.md │ ├── async-control-flow │ │ ├── channels.md │ │ ├── join.md │ │ └── select.md │ ├── async-exercises.md │ ├── async-exercises │ │ ├── afternoon.md │ │ ├── chat-app.md │ │ ├── chat-async │ │ │ ├── Cargo.toml │ │ │ └── src │ │ │ │ └── bin │ │ │ │ ├── client.rs │ │ │ │ └── server.rs │ │ ├── dining-philosophers.md │ │ ├── dining-philosophers.rs │ │ └── solutions.md │ ├── async-pitfalls.md │ ├── async-pitfalls │ │ ├── async-traits.md │ │ ├── blocking-executor.md │ │ ├── cancellation.md │ │ └── pin.md │ ├── async.md │ ├── async │ │ ├── async-await.md │ │ ├── futures.md │ │ ├── runtimes.md │ │ ├── runtimes │ │ │ └── tokio.md │ │ └── tasks.md │ ├── channels.md │ ├── channels │ │ ├── bounded.md │ │ ├── senders-receivers.md │ │ └── unbounded.md │ ├── send-sync.md │ ├── send-sync │ │ ├── examples.md │ │ ├── marker-traits.md │ │ ├── send.md │ │ └── sync.md │ ├── shared-state.md │ ├── shared-state │ │ ├── arc.md │ │ ├── example.md │ │ └── mutex.md │ ├── sync-exercises.md │ ├── sync-exercises │ │ ├── Cargo.toml │ │ ├── dining-philosophers.md │ │ ├── dining-philosophers.rs │ │ ├── link-checker.md │ │ ├── link-checker.rs │ │ └── solutions.md │ ├── threads.md │ ├── threads │ │ ├── plain.md │ │ └── scoped.md │ ├── welcome-async.md │ └── welcome.md ├── control-flow-basics.md ├── control-flow-basics │ ├── Cargo.toml │ ├── blocks-and-scopes.md │ ├── break-continue.md │ ├── break-continue │ │ └── labels.md │ ├── exercise.md │ ├── exercise.rs │ ├── functions.md │ ├── if.md │ ├── loops.md │ ├── loops │ │ ├── for.md │ │ └── loop.md │ ├── macros.md │ ├── match.md │ └── solution.md ├── credits.md ├── error-handling.md ├── error-handling │ ├── Cargo.toml │ ├── anyhow.md │ ├── error.md │ ├── exercise.md │ ├── exercise.rs │ ├── panics.md │ ├── result.md │ ├── solution.md │ ├── thiserror.md │ ├── try-conversions.md │ └── try.md ├── exercises │ ├── bare-metal │ │ ├── afternoon.md │ │ ├── compass.md │ │ ├── compass │ │ │ ├── .cargo │ │ │ │ └── config.toml │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ ├── Embed.toml │ │ │ └── src │ │ │ │ └── main.rs │ │ ├── morning.md │ │ ├── rtc.md │ │ ├── rtc │ │ │ ├── .cargo │ │ │ │ └── config.toml │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ ├── Makefile │ │ │ ├── image.ld │ │ │ └── src │ │ │ │ ├── exceptions.rs │ │ │ │ ├── logger.rs │ │ │ │ ├── main.rs │ │ │ │ └── pl031.rs │ │ ├── solutions-afternoon.md │ │ └── solutions-morning.md │ └── chromium │ │ ├── bringing-it-together.md │ │ ├── build-rules.md │ │ ├── chwomium.png │ │ ├── interoperability-with-cpp.md │ │ ├── solutions.md │ │ ├── testing.md │ │ └── third-party.md ├── generics.md ├── generics │ ├── Cargo.toml │ ├── dyn-trait.md │ ├── exercise.md │ ├── exercise.rs │ ├── generic-data.md │ ├── generic-functions.md │ ├── generic-traits.md │ ├── impl-trait.md │ ├── solution.md │ └── trait-bounds.md ├── glossary.md ├── hello-world.md ├── hello-world │ ├── benefits.md │ ├── playground.md │ └── what-is-rust.md ├── index.md ├── iterators.md ├── iterators │ ├── Cargo.toml │ ├── collect.md │ ├── exercise.md │ ├── exercise.rs │ ├── helpers.md │ ├── intoiterator.md │ ├── iterator.md │ ├── motivation.md │ └── solution.md ├── lifetimes.md ├── lifetimes │ ├── Cargo.toml │ ├── exercise.md │ ├── exercise.rs │ ├── lifetime-annotations.md │ ├── lifetime-elision.md │ ├── solution.md │ └── struct-lifetimes.md ├── memory-management.md ├── memory-management │ ├── Cargo.toml │ ├── approaches.md │ ├── clone.md │ ├── copy-types.md │ ├── drop.md │ ├── exercise.md │ ├── exercise.rs │ ├── move.md │ ├── ownership.md │ ├── review.md │ └── solution.md ├── methods-and-traits.md ├── methods-and-traits │ ├── Cargo.toml │ ├── deriving.md │ ├── exercise.md │ ├── exercise.rs │ ├── methods.md │ ├── solution.md │ ├── traits.md │ └── traits │ │ ├── associated-types.md │ │ ├── implementing.md │ │ └── supertraits.md ├── modules.md ├── modules │ ├── Cargo.toml │ ├── encapsulation.md │ ├── exercise.md │ ├── exercise.rs │ ├── filesystem.md │ ├── modules.md │ ├── paths.md │ ├── solution.md │ └── visibility.md ├── other-resources.md ├── pattern-matching.md ├── pattern-matching │ ├── Cargo.toml │ ├── destructuring-enums.md │ ├── destructuring-structs.md │ ├── exercise.md │ ├── exercise.rs │ ├── infallible.md │ ├── let-control-flow.md │ ├── let-control-flow │ │ ├── if-let.md │ │ ├── let-else.md │ │ └── while-let.md │ ├── match.md │ └── solution.md ├── references.md ├── references │ ├── Cargo.toml │ ├── dangling.md │ ├── exclusive.md │ ├── exercise.md │ ├── exercise.rs │ ├── shared.md │ ├── slices.md │ ├── solution.md │ └── strings.md ├── running-the-course.md ├── running-the-course │ ├── course-structure.md │ ├── keyboard-shortcuts.md │ └── translations.md ├── smart-pointers.md ├── smart-pointers │ ├── Cargo.toml │ ├── box.md │ ├── exercise.md │ ├── exercise.rs │ ├── rc.md │ ├── solution.md │ └── trait-objects.md ├── std-traits.md ├── std-traits │ ├── Cargo.toml │ ├── casting.md │ ├── comparisons.md │ ├── default.md │ ├── exercise.md │ ├── exercise.rs │ ├── from-and-into.md │ ├── operators.md │ ├── read-and-write.md │ └── solution.md ├── std-types.md ├── std-types │ ├── Cargo.toml │ ├── docs.md │ ├── exercise.md │ ├── exercise.rs │ ├── hashmap.md │ ├── option.md │ ├── result.md │ ├── solution.md │ ├── std.md │ ├── string.md │ └── vec.md ├── testing.md ├── testing │ ├── Cargo.toml │ ├── exercise.md │ ├── exercise.rs │ ├── lints.md │ ├── other.md │ ├── solution.md │ └── unit-tests.md ├── thanks.md ├── tuples-and-arrays.md ├── tuples-and-arrays │ ├── Cargo.toml │ ├── arrays.md │ ├── destructuring.md │ ├── exercise.md │ ├── exercise.rs │ ├── iteration.md │ ├── solution.md │ └── tuples.md ├── types-and-values.md ├── types-and-values │ ├── Cargo.toml │ ├── arithmetic.md │ ├── exercise.md │ ├── exercise.rs │ ├── hello-world.md │ ├── inference.md │ ├── solution.md │ ├── values.md │ └── variables.md ├── unsafe-rust.md ├── unsafe-rust │ ├── Cargo.toml │ ├── dereferencing.md │ ├── exercise.md │ ├── exercise.rs │ ├── mutable-static.md │ ├── solution.md │ ├── unions.md │ ├── unsafe-functions.md │ ├── unsafe-functions │ │ ├── calling.md │ │ ├── extern-c.md │ │ └── rust.md │ ├── unsafe-traits.md │ └── unsafe.md ├── user-defined-types.md ├── user-defined-types │ ├── Cargo.toml │ ├── aliases.md │ ├── const.md │ ├── enums.md │ ├── exercise.md │ ├── exercise.rs │ ├── named-structs.md │ ├── solution.md │ ├── static.md │ └── tuple-structs.md ├── welcome-day-1-afternoon.md ├── welcome-day-1.md ├── welcome-day-2-afternoon.md ├── welcome-day-2.md ├── welcome-day-3-afternoon.md ├── welcome-day-3.md ├── welcome-day-4-afternoon.md └── welcome-day-4.md ├── tests ├── README.md ├── package-lock.json ├── package.json ├── src │ ├── generic-page.test.ts │ ├── objects │ │ ├── playground.ts │ │ └── slide.ts │ ├── playground.test.ts │ ├── slide-style-guide.test.ts │ ├── slides │ │ ├── create-slide.list.sh │ │ ├── slide-exemptions.list.ts │ │ └── slides.list.ts │ └── speaker-notes.test.ts ├── tsconfig.json ├── wdio.conf-mdbook.ts └── wdio.conf.ts ├── theme ├── book.js ├── css │ ├── language-picker.css │ ├── redbox.css │ ├── rtl.css │ ├── speaker-notes.css │ └── svgbob.css ├── head.hbs ├── index.hbs ├── instructor-menu.js ├── redbox.js ├── save-playgrounds.js └── speaker-notes.js ├── third_party ├── README.md ├── cxx │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── blobstore │ │ ├── Android.bp │ │ ├── BUILD │ │ ├── Cargo.toml │ │ ├── build.rs │ │ ├── include │ │ │ └── blobstore.h │ │ └── src │ │ │ ├── blobstore.cc │ │ │ └── main.rs │ ├── book │ │ ├── snippets.cc │ │ └── snippets.rs │ └── overview.svg ├── mdbook │ ├── LICENSE │ ├── README.md │ ├── book.js │ └── index.hbs ├── rust-by-example │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── destructuring-arrays.rs │ ├── destructuring-structs.rs │ ├── match-guards.rs │ └── webevent.rs └── rust-on-exercism │ ├── LICENSE │ ├── README.md │ ├── health-statistics.md │ └── health-statistics.rs └── xtask ├── Cargo.toml ├── README.md └── src └── main.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | # We use this alias for task automation in the project. 3 | # See README in xtask directory. 4 | xtask = "run --package xtask --" 5 | 6 | [env] 7 | # To provide an anchor to the root of the workspace when working with paths. 8 | # See https://github.com/rust-lang/cargo/issues/3946#issuecomment-973132993 9 | CARGO_WORKSPACE_DIR = { value = "", relative = true } 10 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Translations 2 | po/ar.po @younies 3 | po/bn.po @mirwasi @sudiptachatterjee 4 | po/da.po @mgeisler @voss @thedataking 5 | po/de.po @ronaldfw @fechu @kweber 6 | po/el.po @Mandragorian 7 | po/es.po @deavid @vzz1x2 8 | po/fa.po @DannyRavi @hamidrezakp @moaminsharifi @mehrad77 @javad-jafari 9 | po/fr.po @sakex 10 | po/it.po @detro @nicomazz 11 | po/ja.po @keiichiw @chikoski @kantasv @HidenoriKobayashi 12 | po/ko.po @jiyongp @jooyunghan @namhyung @myoun 13 | po/pl.po @jkotur @dyeroshenko 14 | po/pt-BR.po @rastringer @hugojacob @henrif75 @joaovicmendes @jcvicelli @azevedoalice 15 | po/ro.po @AlexandraImbrisca @razvanm 16 | po/ru.po @istolga @baltuky @zvonden @dyeroshenko 17 | po/tr.po @alerque 18 | po/uk.po @dyeroshenko 19 | po/vi.po @daivinhtran @qu-ngx 20 | po/zh-CN.po @wnghl @anlunx @kongy @noahdragon @superwhd @emmali01 @candysonya @AgainstEntropy 21 | po/zh-TW.po @edong @hueich @kuanhungchen @victorhsieh @mingyc @johnathan79717 22 | -------------------------------------------------------------------------------- /.github/pandoc/README.md: -------------------------------------------------------------------------------- 1 | # Config Overrides for `mdbook-pandoc` 2 | 3 | The configuration file fragments here are used to override the configuration in 4 | `book.toml`. See `.github/workflows/build.sh` for details. 5 | -------------------------------------------------------------------------------- /.github/pandoc/ja.yaml: -------------------------------------------------------------------------------- 1 | variables: 2 | CJKmainfont: "Noto Serif CJK JP" 3 | CJKsansfont: "Noto Sans CJK JP" 4 | CJKmonofont: "Noto Sans Mono CJK JP" 5 | -------------------------------------------------------------------------------- /.github/pandoc/ko.yaml: -------------------------------------------------------------------------------- 1 | variables: 2 | CJKmainfont: "Noto Serif CJK KR" 3 | CJKsansfont: "Noto Sans CJK KR" 4 | CJKmonofont: "Noto Sans Mono CJK KR" 5 | -------------------------------------------------------------------------------- /.github/pandoc/zh-CN.yaml: -------------------------------------------------------------------------------- 1 | variables: 2 | CJKmainfont: "Noto Serif CJK SC" 3 | CJKsansfont: "Noto Sans CJK SC" 4 | CJKmonofont: "Noto Sans Mono CJK SC" 5 | -------------------------------------------------------------------------------- /.github/pandoc/zh-TW.yaml: -------------------------------------------------------------------------------- 1 | variables: 2 | CJKmainfont: "Noto Serif CJK TC" 3 | CJKsansfont: "Noto Sans CJK TC" 4 | CJKmonofont: "Noto Sans Mono CJK TC" 5 | -------------------------------------------------------------------------------- /.github/typos.toml: -------------------------------------------------------------------------------- 1 | [default.extend-identifiers] 2 | # False positives. 3 | mis = "mis" 4 | MIS = "MIS" 5 | inout = "inout" 6 | BARs = "BARs" 7 | 8 | [type.po] 9 | # Localized content should not be checked for typos. English 10 | # in these files should be validated manually. 11 | extend-glob = ["*.po"] 12 | check-file = false 13 | 14 | [files] 15 | # Typos in third party packages should be fixed upstream. 16 | extend-exclude = ["third_party/*", "theme/book.js"] 17 | -------------------------------------------------------------------------------- /.github/workflows/check-msgid-changes.yml: -------------------------------------------------------------------------------- 1 | name: Prevent unintended msgid changes 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - "po/*.po" 7 | 8 | jobs: 9 | check-msgid-changes: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Reset git 18 | run: git reset origin/main 19 | 20 | - name: Check po file changes 21 | run: python3 .github/workflows/check-msgid-changes.py 22 | -------------------------------------------------------------------------------- /.github/workflows/install-mdbook/action.yml: -------------------------------------------------------------------------------- 1 | name: Install mdbook and dependencies 2 | 3 | description: Install mdbook with the dependencies we need. 4 | 5 | runs: 6 | using: composite 7 | steps: 8 | - name: Install mdbook 9 | run: cargo xtask install-tools 10 | shell: bash 11 | 12 | - name: Install dependencies for mdbook-pandoc 13 | run: | 14 | sudo apt-get update 15 | sudo apt-get install -y texlive texlive-luatex texlive-lang-cjk texlive-lang-arabic librsvg2-bin fonts-noto 16 | curl -LsSf https://github.com/jgm/pandoc/releases/download/3.7.0.1/pandoc-3.7.0.1-linux-amd64.tar.gz | tar zxf - 17 | echo "$PWD/pandoc-3.7.0.1/bin" >> $GITHUB_PATH 18 | shell: bash 19 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Pull Request Labeler" 2 | on: 3 | - pull_request_target 4 | 5 | jobs: 6 | triage: 7 | permissions: 8 | contents: read 9 | pull-requests: write 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/labeler@v5 13 | with: 14 | sync-labels: true 15 | -------------------------------------------------------------------------------- /.github/workflows/setup-rust-cache/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup Rust cache 2 | 3 | description: Configure the rust-cache workflow. 4 | 5 | inputs: 6 | key: 7 | description: Additional caching key 8 | required: false 9 | default: 10 | 11 | runs: 12 | using: composite 13 | steps: 14 | - name: Setup Rust cache 15 | uses: Swatinem/rust-cache@v2 16 | with: 17 | # Only save the cache on the main branch to avoid PRs filling 18 | # up the cache. 19 | save-if: ${{ github.ref == 'refs/heads/main' }} 20 | # Further, save the cache per key - e.g. language grouping. 21 | key: ${{ inputs.key }} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | /book/ 3 | target/ 4 | *.bin 5 | 6 | # Translation artifacts 7 | po/*.mo 8 | po/*.po~ 9 | 10 | # macOS artifacts 11 | .DS_Store 12 | 13 | # Jetbrains IDEs project files 14 | .idea/ 15 | .iml 16 | .iws 17 | count.dat 18 | 19 | # Nodejs Files (dprint) 20 | node_modules/ 21 | package.json 22 | !tests/package.json 23 | pnpm-lock.yaml 24 | 25 | # Crowdin Config (Contains API Keys) 26 | crowdin.yml 27 | 28 | # Google's Project IDX files and VSCode 29 | .idx/ 30 | .vscode/ 31 | 32 | # Python virtualenv (for mdbook-slide-evaluator local installation) 33 | .venv/ 34 | 35 | # tests/ framework artifacts 36 | tests/src/slide/slides/slides.list.ts 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | # Please keep the workspace members sorted. 3 | members = [ 4 | "mdbook-course", 5 | "mdbook-exerciser", 6 | "src/android/testing", 7 | "src/bare-metal/useful-crates/allocator-example", 8 | "src/bare-metal/useful-crates/zerocopy-example", 9 | "src/borrowing", 10 | "src/concurrency/async-exercises/chat-async", 11 | "src/concurrency/sync-exercises", 12 | "src/control-flow-basics", 13 | "src/error-handling", 14 | "src/generics", 15 | "src/iterators", 16 | "src/lifetimes", 17 | "src/memory-management", 18 | "src/methods-and-traits", 19 | "src/modules", 20 | "src/pattern-matching", 21 | "src/references", 22 | "src/smart-pointers", 23 | "src/std-traits", 24 | "src/std-types", 25 | "src/testing", 26 | "src/tuples-and-arrays", 27 | "src/types-and-values", 28 | "src/unsafe-rust", 29 | "src/user-defined-types", 30 | "third_party/cxx/blobstore", 31 | "xtask", 32 | ] 33 | resolver = "2" 34 | -------------------------------------------------------------------------------- /dprint.json: -------------------------------------------------------------------------------- 1 | { 2 | "lineWidth": 80, 3 | "json": {}, 4 | "markdown": { 5 | "textWrap": "always" 6 | }, 7 | "exec": { 8 | "commands": [{ 9 | "command": "yapf3", 10 | "exts": ["py"] 11 | }, { 12 | "command": "rustup run stable rustfmt --edition 2024", 13 | "exts": ["rs"] 14 | }] 15 | }, 16 | "excludes": [ 17 | "/book/", 18 | "/theme/*.hbs", 19 | "/theme/book.js", 20 | "/third_party/", 21 | "target/" 22 | ], 23 | "plugins": [ 24 | "https://plugins.dprint.dev/exec-0.5.1.json@492414e39dea4dccc07b4af796d2f4efdb89e84bae2bd4e1e924c0cc050855bf", 25 | "https://plugins.dprint.dev/json-0.20.0.wasm", 26 | "https://plugins.dprint.dev/markdown-0.18.0.wasm", 27 | "https://plugins.dprint.dev/toml-0.7.0.wasm", 28 | "https://plugins.dprint.dev/prettier-0.56.0.json@0b67676535141b03f0eb0648172d3a20f13044b3d8df677d645bd79e3ee5147f" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /mdbook-course/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mdbook-course" 3 | version = "0.1.0" 4 | authors = ["Dustin Mitchell "] 5 | edition = "2024" 6 | license = "Apache-2.0" 7 | publish = false 8 | repository = "https://github.com/google/comprehensive-rust" 9 | description = "An mdbook preprocessor for comprehensive-rust." 10 | 11 | [dependencies] 12 | anyhow = "1.0.98" 13 | clap = "4.5.39" 14 | lazy_static = "1.5" 15 | log = "0.4.27" 16 | matter = "0.1.0-alpha4" 17 | mdbook = "0.4.51" 18 | pretty_env_logger = "0.5.0" 19 | regex = "1.11" 20 | serde = "1.0.219" 21 | serde_json = "1.0.140" 22 | serde_yaml = "0.9" 23 | -------------------------------------------------------------------------------- /mdbook-course/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub mod course; 16 | pub mod frontmatter; 17 | pub mod markdown; 18 | pub mod replacements; 19 | pub mod timing_info; 20 | -------------------------------------------------------------------------------- /mdbook-exerciser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mdbook-exerciser" 3 | version = "0.1.0" 4 | authors = ["Andrew Walbran "] 5 | edition = "2024" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/google/comprehensive-rust" 8 | description = "A tool for extracting starter code for exercises from Markdown files." 9 | 10 | [dependencies] 11 | anyhow = "1.0.98" 12 | log = "0.4.27" 13 | mdbook = "0.4.51" 14 | pretty_env_logger = "0.5.0" 15 | pulldown-cmark = { version = "0.13.0", default-features = false } 16 | -------------------------------------------------------------------------------- /mdbook-exerciser/README.md: -------------------------------------------------------------------------------- 1 | # exerciser 2 | 3 | This is an mdBook renderer to generate templates for exercises from the Markdown 4 | source. Given a Markdown file `example.md` with one or more sections like: 5 | 6 | ````markdown 7 | 8 | 9 | ```rust,compile_fail 10 | {{#include example/src/main.rs:main}} 11 | 12 | fn some_more_code() { 13 | // TODO: Write some Rust code here. 14 | } 15 | ``` 16 | ```` 17 | 18 | and mdbook configuration in `book.toml` like: 19 | 20 | ```toml 21 | [output.exerciser] 22 | output-directory = "comprehensive-rust-exercises" 23 | ``` 24 | 25 | It will create a file 26 | `book/exerciser/comprehensive-rust-exercises/example/src/main.rs` with the 27 | appropriate contents. 28 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Please use a nightly rustfmt for these settings. 2 | imports_granularity = "module" 3 | wrap_comments = true 4 | 5 | # The code blocks get a scrollbar if they are wider than this. 6 | max_width = 85 7 | # Allow all constructs to take up max_width columns. 8 | use_small_heuristics = "Max" 9 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Course Content 2 | 3 | The files in this directory make up the content of the course. The files here 4 | can include third-party content from `../third_party/` as well. 5 | 6 | When we publish a translation of the course, we `git restore` the `src/` and 7 | `third_party/` directories at the repository root back to the date listed in the 8 | POT-Creation-Date header of the translation. **It is crucial, that all 9 | translatable content lives in those two directories.** The other files (such as 10 | `book.toml` and `theme/`) are not restored and we always use the latest version 11 | of them. 12 | -------------------------------------------------------------------------------- /src/android.md: -------------------------------------------------------------------------------- 1 | --- 2 | course: Android 3 | session: Android 4 | --- 5 | 6 | # Welcome to Rust in Android 7 | 8 | Rust is supported for system software on Android. This means that you can write 9 | new services, libraries, drivers or even firmware in Rust (or improve existing 10 | code as needed). 11 | 12 |
13 | 14 | The speaker may mention any of the following given the increased use of Rust in 15 | Android: 16 | 17 | - Service example: 18 | [DNS over HTTP](https://security.googleblog.com/2022/07/dns-over-http3-in-android.html). 19 | 20 | - Libraries: 21 | [Rutabaga Virtual Graphics Interface](https://crosvm.dev/book/appendix/rutabaga_gfx.html). 22 | 23 | - Kernel Drivers: 24 | [Binder](https://lore.kernel.org/rust-for-linux/20231101-rust-binder-v1-0-08ba9197f637@google.com/). 25 | 26 | - Firmware: 27 | [pKVM firmware](https://security.googleblog.com/2023/10/bare-metal-rust-in-android.html). 28 | 29 |
30 | -------------------------------------------------------------------------------- /src/android/aidl.md: -------------------------------------------------------------------------------- 1 | # AIDL 2 | 3 | The 4 | [Android Interface Definition Language 5 | (AIDL)](https://developer.android.com/guide/components/aidl) is supported in 6 | Rust: 7 | 8 | - Rust code can call existing AIDL servers, 9 | - You can create new AIDL servers in Rust. 10 | 11 |
12 | 13 | - AIDL is what enables Android apps to interact with each other. 14 | 15 | - Since Rust is supported as a first-class citizen in this ecosystem, Rust 16 | services can be called by any other process on the phone. 17 | 18 |
19 | -------------------------------------------------------------------------------- /src/android/aidl/birthday-service.md: -------------------------------------------------------------------------------- 1 | # Birthday Service Tutorial 2 | 3 | To illustrate how to use Rust with Binder, we're going to walk through the 4 | process of creating a Binder interface. We're then going to both implement the 5 | described service and write client code that talks to that service. 6 | -------------------------------------------------------------------------------- /src/android/aidl/birthday_service/Android.bp: -------------------------------------------------------------------------------- 1 | // ANCHOR: libbirthdayservice 2 | rust_library { 3 | name: "libbirthdayservice", 4 | srcs: ["src/lib.rs"], 5 | crate_name: "birthdayservice", 6 | rustlibs: [ 7 | "com.example.birthdayservice-rust", 8 | "libbinder_rs", 9 | ], 10 | } 11 | // ANCHOR_END: libbirthdayservice 12 | 13 | // ANCHOR: birthday_server 14 | rust_binary { 15 | name: "birthday_server", 16 | crate_name: "birthday_server", 17 | srcs: ["src/server.rs"], 18 | rustlibs: [ 19 | "com.example.birthdayservice-rust", 20 | "libbinder_rs", 21 | "libbirthdayservice", 22 | ], 23 | prefer_rlib: true, // To avoid dynamic link error. 24 | } 25 | // ANCHOR_END: birthday_server 26 | 27 | // ANCHOR: birthday_client 28 | rust_binary { 29 | name: "birthday_client", 30 | crate_name: "birthday_client", 31 | srcs: ["src/client.rs"], 32 | rustlibs: [ 33 | "com.example.birthdayservice-rust", 34 | "libbinder_rs", 35 | ], 36 | prefer_rlib: true, // To avoid dynamic link error. 37 | } 38 | // ANCHOR_END: birthday_client 39 | -------------------------------------------------------------------------------- /src/android/aidl/birthday_service/aidl/Android.bp: -------------------------------------------------------------------------------- 1 | aidl_interface { 2 | name: "com.example.birthdayservice", 3 | srcs: ["com/example/birthdayservice/*.aidl"], 4 | unstable: true, 5 | backend: { 6 | rust: { // Rust is not enabled by default 7 | enabled: true, 8 | }, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /src/android/aidl/birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl: -------------------------------------------------------------------------------- 1 | package com.example.birthdayservice; 2 | 3 | parcelable BirthdayInfo { 4 | String name; 5 | int years; 6 | } 7 | -------------------------------------------------------------------------------- /src/android/aidl/birthday_service/aidl/com/example/birthdayservice/IBirthdayInfoProvider.aidl: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: IBirthdayInfoProvider 16 | package com.example.birthdayservice; 17 | 18 | interface IBirthdayInfoProvider { 19 | String name(); 20 | int years(); 21 | } 22 | -------------------------------------------------------------------------------- /src/android/aidl/example-service/changing-definition.md: -------------------------------------------------------------------------------- 1 | # Changing API 2 | 3 | Let us extend the API with more functionality: we want to let clients specify a 4 | list of lines for the birthday card: 5 | 6 | ```java 7 | package com.example.birthdayservice; 8 | 9 | /** Birthday service interface. */ 10 | interface IBirthdayService { 11 | /** Generate a Happy Birthday message. */ 12 | String wishHappyBirthday(String name, int years, in String[] text); 13 | } 14 | ``` 15 | 16 | This results in an updated trait definition for `IBirthdayService`: 17 | 18 | ```rust,ignore 19 | trait IBirthdayService { 20 | fn wishHappyBirthday( 21 | &self, 22 | name: &str, 23 | years: i32, 24 | text: &[String], 25 | ) -> binder::Result; 26 | } 27 | ``` 28 | 29 |
30 | 31 | - Note how the `String[]` in the AIDL definition is translated as a `&[String]` 32 | in Rust, i.e. that idiomatic Rust types are used in the generated bindings 33 | wherever possible: 34 | - `in` array arguments are translated to slices. 35 | - `out` and `inout` args are translated to `&mut Vec`. 36 | - Return values are translated to returning a `Vec`. 37 | 38 |
39 | -------------------------------------------------------------------------------- /src/android/aidl/example-service/changing-implementation.md: -------------------------------------------------------------------------------- 1 | # Updating Client and Service 2 | 3 | Update the client and server code to account for the new API. 4 | 5 | _birthday_service/src/lib.rs_: 6 | 7 | ```rust,ignore 8 | impl IBirthdayService for BirthdayService { 9 | fn wishHappyBirthday( 10 | &self, 11 | name: &str, 12 | years: i32, 13 | text: &[String], 14 | ) -> binder::Result { 15 | let mut msg = format!( 16 | "Happy Birthday {name}, congratulations with the {years} years!", 17 | ); 18 | 19 | for line in text { 20 | msg.push('\n'); 21 | msg.push_str(line); 22 | } 23 | 24 | Ok(msg) 25 | } 26 | } 27 | ``` 28 | 29 | _birthday_service/src/client.rs_: 30 | 31 | ```rust,ignore 32 | let msg = service.wishHappyBirthday( 33 | &name, 34 | years, 35 | &[ 36 | String::from("Habby birfday to yuuuuu"), 37 | String::from("And also: many more"), 38 | ], 39 | )?; 40 | ``` 41 | 42 |
43 | 44 | - TODO: Move code snippets into project files where they'll actually be built? 45 | 46 |
47 | -------------------------------------------------------------------------------- /src/android/aidl/example-service/deploy.md: -------------------------------------------------------------------------------- 1 | # Deploy 2 | 3 | We can now build, push, and start the service: 4 | 5 | ```shell 6 | {{#include ../../build_all.sh:birthday_server}} 7 | ``` 8 | 9 | In another terminal, check that the service runs: 10 | 11 | ```shell 12 | {{#include ../../build_all.sh:service_check_birthday_server}} 13 | ``` 14 | 15 | ```text 16 | Service birthdayservice: found 17 | ``` 18 | 19 | You can also call the service with `service call`: 20 | 21 | ```shell 22 | {{#include ../../build_all.sh:service_call_birthday_server}} 23 | ``` 24 | 25 | ```text 26 | Result: Parcel( 27 | 0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.' 28 | 0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.' 29 | 0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .' 30 | 0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.' 31 | 0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .' 32 | 0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.' 33 | 0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.' 34 | 0x00000070: 00210073 00000000 's.!..... ') 35 | ``` 36 | -------------------------------------------------------------------------------- /src/android/aidl/example-service/implementation.md: -------------------------------------------------------------------------------- 1 | # Implementation 2 | -------------------------------------------------------------------------------- /src/android/aidl/example-service/interface.md: -------------------------------------------------------------------------------- 1 | # AIDL Interfaces 2 | 3 | You declare the API of your service using an AIDL interface: 4 | 5 | _birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_: 6 | 7 | ```java 8 | {{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:package}} 9 | 10 | {{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:IBirthdayService}} 11 | } 12 | ``` 13 | 14 | _birthday_service/aidl/Android.bp_: 15 | 16 | ```javascript 17 | {{#include ../birthday_service/aidl/Android.bp}} 18 | ``` 19 | 20 |
21 | 22 | - Note that the directory structure under the `aidl/` directory needs to match 23 | the package name used in the AIDL file, i.e. the package is 24 | `com.example.birthdayservice` and the file is at 25 | `aidl/com/example/IBirthdayService.aidl`. 26 | 27 |
28 | -------------------------------------------------------------------------------- /src/android/aidl/example-service/service-bindings.md: -------------------------------------------------------------------------------- 1 | # Generated Service API 2 | 3 | Binder generates a trait for each interface definition. 4 | 5 | _birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_: 6 | 7 | ```java 8 | {{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:IBirthdayService}} 9 | } 10 | ``` 11 | 12 | _out/soong/.intermediates/.../com_example_birthdayservice.rs_: 13 | 14 | 15 | 16 | ```rust,ignore 17 | trait IBirthdayService { 18 | fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::Result; 19 | } 20 | ``` 21 | 22 | Your service will need to implement this trait, and your client will use this 23 | trait to talk to the service. 24 | 25 |
26 | 27 | - Point out how the generated function signature, specifically the argument and 28 | return types, correspond the interface definition. 29 | - `String` for an argument results in a different Rust type than `String` as a 30 | return type. 31 | 32 |
33 | -------------------------------------------------------------------------------- /src/android/aidl/types.md: -------------------------------------------------------------------------------- 1 | # Working With AIDL Types 2 | 3 | AIDL types translate into the appropriate idiomatic Rust type: 4 | 5 | - Primitive types map (mostly) to idiomatic Rust types. 6 | - Collection types like slices, `Vec`s and string types are supported. 7 | - References to AIDL objects and file handles can be sent between clients and 8 | services. 9 | - File handles and parcelables are fully supported. 10 | -------------------------------------------------------------------------------- /src/android/aidl/types/arrays.md: -------------------------------------------------------------------------------- 1 | # Array Types 2 | 3 | The array types (`T[]`, `byte[]`, and `List`) get translated to the 4 | appropriate Rust array type depending on how they are used in the function 5 | signature: 6 | 7 | | Position | Rust Type | 8 | | ---------------------- | ------------- | 9 | | `in` argument | `&[T]` | 10 | | `out`/`inout` argument | `&mut Vec` | 11 | | Return | `Vec` | 12 | 13 |
14 | 15 | - In Android 13 or higher, fixed-size arrays are supported, i.e. `T[N]` becomes 16 | `[T; N]`. Fixed-size arrays can have multiple dimensions (e.g. `int[3][4]`). 17 | In the Java backend, fixed-size arrays are represented as array types. 18 | - Arrays in parcelable fields always get translated to `Vec`. 19 | 20 |
21 | -------------------------------------------------------------------------------- /src/android/aidl/types/parcelables.md: -------------------------------------------------------------------------------- 1 | # Parcelables 2 | 3 | Binder for Rust supports sending parcelables directly: 4 | 5 | _birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl_: 6 | 7 | ```java 8 | {{#include ../birthday_service/aidl/com/example/birthdayservice/BirthdayInfo.aidl}} 9 | ``` 10 | 11 | _birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl_: 12 | 13 | ```java 14 | import com.example.birthdayservice.BirthdayInfo; 15 | 16 | interface IBirthdayService { 17 | {{#include ../birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:with_info}} 18 | } 19 | ``` 20 | 21 | _birthday_service/src/client.rs_: 22 | 23 | ```rust,ignore 24 | fn main() { 25 | binder::ProcessState::start_thread_pool(); 26 | let service = connect().expect("Failed to connect to BirthdayService"); 27 | 28 | let info = BirthdayInfo { name: "Alice".into(), years: 123 }; 29 | service.wishWithInfo(&info)?; 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /src/android/aidl/types/primitives.md: -------------------------------------------------------------------------------- 1 | # Primitive Types 2 | 3 | Primitive types map (mostly) idiomatically: 4 | 5 | | AIDL Type | Rust Type | Note | 6 | | --------- | --------- | ----------------------------------- | 7 | | `boolean` | `bool` | | 8 | | `byte` | `i8` | Note that bytes are signed. | 9 | | `char` | `u16` | Note the usage of `u16`, NOT `u32`. | 10 | | `int` | `i32` | | 11 | | `long` | `i64` | | 12 | | `float` | `f32` | | 13 | | `double` | `f64` | | 14 | | `String` | `String` | | 15 | -------------------------------------------------------------------------------- /src/android/bpfmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | # Copyright 2022 Google LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Simple wrapper for bpfmt which will remove unnecessary newlines before the 17 | # mdbook anchors. 18 | 19 | if ! type bpfmt > /dev/null; then 20 | echo 'Can not find bpfmt, do you need to run "m bpfmt"?' 21 | exit 1 22 | fi 23 | 24 | for f in comprehensive_rust/**/Android.bp; do 25 | bpfmt -s -w $f 26 | sed -zi 's|\n// ANCHOR_END|// ANCHOR_END|g' $f 27 | done 28 | -------------------------------------------------------------------------------- /src/android/build-rules/binary.md: -------------------------------------------------------------------------------- 1 | # Rust Binaries 2 | 3 | Let us start with a simple application. At the root of an AOSP checkout, create 4 | the following files: 5 | 6 | _hello_rust/Android.bp_: 7 | 8 | ```javascript 9 | {{#include binary/Android.bp}} 10 | ``` 11 | 12 | _hello_rust/src/main.rs_: 13 | 14 | ```rust 15 | {{#include binary/src/main.rs:main}} 16 | ``` 17 | 18 | You can now build, push, and run the binary: 19 | 20 | ```shell 21 | {{#include ../build_all.sh:hello_rust}} 22 | ``` 23 | 24 | ```text 25 | Hello from Rust! 26 | ``` 27 | 28 |
29 | 30 | - Go through the build steps and demonstrate them running in your emulator. 31 | 32 | - Notice the extensive documentation comments? The Android build rules enforce 33 | that all modules have documentation. Try removing it and see what error you 34 | get. 35 | 36 | - Stress that the Rust build rules look like the other Soong rules. This is on 37 | purpose to make it as easy to use Rust as C++ or Java. 38 | 39 |
40 | -------------------------------------------------------------------------------- /src/android/build-rules/binary/Android.bp: -------------------------------------------------------------------------------- 1 | rust_binary { 2 | name: "hello_rust", 3 | crate_name: "hello_rust", 4 | srcs: ["src/main.rs"], 5 | } 6 | -------------------------------------------------------------------------------- /src/android/build-rules/binary/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: main 16 | //! Rust demo. 17 | 18 | /// Prints a greeting to standard output. 19 | fn main() { 20 | println!("Hello from Rust!"); 21 | } 22 | -------------------------------------------------------------------------------- /src/android/build-rules/library/Android.bp: -------------------------------------------------------------------------------- 1 | rust_binary { 2 | name: "hello_rust_with_dep", 3 | crate_name: "hello_rust_with_dep", 4 | srcs: ["src/main.rs"], 5 | rustlibs: [ 6 | "libgreetings", 7 | "libtextwrap", 8 | ], 9 | prefer_rlib: true, // Need this to avoid dynamic link error. 10 | } 11 | 12 | rust_library { 13 | name: "libgreetings", 14 | crate_name: "greetings", 15 | srcs: ["src/lib.rs"], 16 | } 17 | -------------------------------------------------------------------------------- /src/android/build-rules/library/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: greeting 16 | //! Greeting library. 17 | 18 | /// Greet `name`. 19 | pub fn greeting(name: &str) -> String { 20 | format!("Hello {name}, it is very nice to meet you!") 21 | } 22 | -------------------------------------------------------------------------------- /src/android/build-rules/library/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: main 16 | //! Rust demo. 17 | 18 | use greetings::greeting; 19 | use textwrap::fill; 20 | 21 | /// Prints a greeting to standard output. 22 | fn main() { 23 | println!("{}", fill(&greeting("Bob"), 24)); 24 | } 25 | -------------------------------------------------------------------------------- /src/android/interoperability.md: -------------------------------------------------------------------------------- 1 | # Interoperability 2 | 3 | Rust has excellent support for interoperability with other languages. This means 4 | that you can: 5 | 6 | - Call Rust functions from other languages. 7 | - Call functions written in other languages from Rust. 8 | 9 | When you call functions in a foreign language we say that you're using a 10 | _foreign function interface_, also known as FFI. 11 | 12 |
13 | 14 | - This is a key ability of Rust: compiled code becomes indistinguishable from 15 | compiled C or C++ code. 16 | 17 | - Technically, we say that Rust can be compiled to the same [ABI] (application 18 | binary interface) as C code. 19 | 20 |
21 | 22 | [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface 23 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp.md: -------------------------------------------------------------------------------- 1 | # With C++ 2 | 3 | The [CXX crate][1] makes it possible to do safe interoperability between Rust 4 | and C++. 5 | 6 | The overall approach looks like this: 7 | 8 | 9 | 10 | [1]: https://cxx.rs/ 11 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/android-build-cpp.md: -------------------------------------------------------------------------------- 1 | # Building in Android 2 | 3 | Create a `cc_library_static` to build the C++ library, including the CXX 4 | generated header and source file. 5 | 6 | ```javascript 7 | cc_library_static { 8 | name: "libcxx_test_cpp", 9 | srcs: ["cxx_test.cpp"], 10 | generated_headers: [ 11 | "cxx-bridge-header", 12 | "libcxx_test_bridge_header" 13 | ], 14 | generated_sources: ["libcxx_test_bridge_code"], 15 | } 16 | ``` 17 | 18 |
19 | 20 | - Point out that `libcxx_test_bridge_header` and `libcxx_test_bridge_code` are 21 | the dependencies for the CXX-generated C++ bindings. We'll show how these are 22 | setup on the next slide. 23 | - Note that you also need to depend on the `cxx-bridge-header` library in order 24 | to pull in common CXX definitions. 25 | - Full docs for using CXX in Android can be found in [the Android docs]. You may 26 | want to share that link with the class so that students know where they can 27 | find these instructions again in the future. 28 | 29 | [the Android docs]: https://source.android.com/docs/setup/build/rust/building-rust-modules/android-rust-patterns#rust-cpp-interop-using-cxx 30 | 31 |
32 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/android-build-rust.md: -------------------------------------------------------------------------------- 1 | # Building in Android 2 | 3 | Create a `rust_binary` that depends on `libcxx` and your `cc_library_static`. 4 | 5 | ```javascript 6 | rust_binary { 7 | name: "cxx_test", 8 | srcs: ["lib.rs"], 9 | rustlibs: ["libcxx"], 10 | static_libs: ["libcxx_test_cpp"], 11 | } 12 | ``` 13 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/bridge.md: -------------------------------------------------------------------------------- 1 | # The Bridge Module 2 | 3 | CXX relies on a description of the function signatures that will be exposed from 4 | each language to the other. You provide this description using extern blocks in 5 | a Rust module annotated with the `#[cxx::bridge]` attribute macro. 6 | 7 | ```rust,ignore 8 | {{#include ../../../../third_party/cxx/blobstore/src/main.rs:bridge}} 9 | ``` 10 | 11 |
12 | 13 | - The bridge is generally declared in an `ffi` module within your crate. 14 | - From the declarations made in the bridge module, CXX will generate matching 15 | Rust and C++ type/function definitions in order to expose those items to both 16 | languages. 17 | - To view the generated Rust code, use [cargo-expand] to view the expanded proc 18 | macro. For most of the examples you would use `cargo expand ::ffi` to expand 19 | just the `ffi` module (though this doesn't apply for Android projects). 20 | - To view the generated C++ code, look in `target/cxxbridge`. 21 | 22 | [cargo-expand]: https://github.com/dtolnay/cargo-expand 23 | 24 |
25 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/cpp-exception.md: -------------------------------------------------------------------------------- 1 | # C++ Error Handling 2 | 3 | ```rust,ignore 4 | {{#include ../../../../third_party/cxx/book/snippets.rs:cpp_exception}} 5 | ``` 6 | 7 |
8 | 9 | - C++ functions declared to return a `Result` will catch any thrown exception on 10 | the C++ side and return it as an `Err` value to the calling Rust function. 11 | - If an exception is thrown from an extern "C++" function that is not declared 12 | by the CXX bridge to return `Result`, the program calls C++'s 13 | `std::terminate`. The behavior is equivalent to the same exception being 14 | thrown through a `noexcept` C++ function. 15 | 16 |
17 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/generated-cpp.md: -------------------------------------------------------------------------------- 1 | # Generated C++ 2 | 3 | ```rust,ignore 4 | #[cxx::bridge] 5 | mod ffi { 6 | {{#include ../../../../third_party/cxx/blobstore/src/main.rs:rust_bridge}} 7 | } 8 | ``` 9 | 10 | Results in (roughly) the following C++: 11 | 12 | ```cpp 13 | struct MultiBuf final : public ::rust::Opaque { 14 | ~MultiBuf() = delete; 15 | 16 | private: 17 | friend ::rust::layout; 18 | struct layout { 19 | static ::std::size_t size() noexcept; 20 | static ::std::size_t align() noexcept; 21 | }; 22 | }; 23 | 24 | ::rust::Slice<::std::uint8_t const> next_chunk(::org::blobstore::MultiBuf &buf) noexcept; 25 | ``` 26 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/overview.svg: -------------------------------------------------------------------------------- 1 | ../../../../third_party/cxx/overview.svg -------------------------------------------------------------------------------- /src/android/interoperability/cpp/rust-bridge.md: -------------------------------------------------------------------------------- 1 | # Rust Bridge Declarations 2 | 3 | ```rust,ignore 4 | {{#include ../../../../third_party/cxx/book/snippets.rs:rust_bridge}} 5 | ``` 6 | 7 |
8 | 9 | - Items declared in the `extern "Rust"` reference items that are in scope in the 10 | parent module. 11 | - The CXX code generator uses your `extern "Rust"` section(s) to produce a C++ 12 | header file containing the corresponding C++ declarations. The generated 13 | header has the same path as the Rust source file containing the bridge, except 14 | with a .rs.h file extension. 15 | 16 |
17 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/rust-result.md: -------------------------------------------------------------------------------- 1 | # Rust Error Handling 2 | 3 | ```rust,ignore 4 | {{#include ../../../../third_party/cxx/book/snippets.rs:rust_result}} 5 | ``` 6 | 7 |
8 | 9 | - Rust functions that return `Result` are translated to exceptions on the C++ 10 | side. 11 | - The exception thrown will always be of type `rust::Error`, which primarily 12 | exposes a way to get the error message string. The error message will come 13 | from the error type's `Display` impl. 14 | - A panic unwinding from Rust to C++ will always cause the process to 15 | immediately terminate. 16 | 17 |
18 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/shared-enums.md: -------------------------------------------------------------------------------- 1 | # Shared Enums 2 | 3 | ```rust,ignore 4 | {{#include ../../../../third_party/cxx/book/snippets.rs:shared_enums_bridge}} 5 | ``` 6 | 7 | Generated Rust: 8 | 9 | ```rust 10 | {{#include ../../../../third_party/cxx/book/snippets.rs:shared_enums_rust}} 11 | ``` 12 | 13 | Generated C++: 14 | 15 | ```c++ 16 | {{#include ../../../../third_party/cxx/book/snippets.cc:shared_enums_cpp}} 17 | ``` 18 | 19 |
20 | 21 | - On the Rust side, the code generated for shared enums is actually a struct 22 | wrapping a numeric value. This is because it is not UB in C++ for an enum 23 | class to hold a value different from all of the listed variants, and our Rust 24 | representation needs to have the same behavior. 25 | 26 |
27 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/shared-types.md: -------------------------------------------------------------------------------- 1 | # Shared Types 2 | 3 | ```rust,ignore 4 | {{#include ../../../../third_party/cxx/book/snippets.rs:shared_types}} 5 | ``` 6 | 7 |
8 | 9 | - Only C-like (unit) enums are supported. 10 | - A limited number of traits are supported for `#[derive()]` on shared types. 11 | Corresponding functionality is also generated for the C++ code, e.g. if you 12 | derive `Hash` also generates an implementation of `std::hash` for the 13 | corresponding C++ type. 14 | 15 |
16 | -------------------------------------------------------------------------------- /src/android/interoperability/cpp/type-mapping.md: -------------------------------------------------------------------------------- 1 | # Additional Types 2 | 3 | | Rust Type | C++ Type | 4 | | ----------------- | -------------------- | 5 | | `String` | `rust::String` | 6 | | `&str` | `rust::Str` | 7 | | `CxxString` | `std::string` | 8 | | `&[T]`/`&mut [T]` | `rust::Slice` | 9 | | `Box` | `rust::Box` | 10 | | `UniquePtr` | `std::unique_ptr` | 11 | | `Vec` | `rust::Vec` | 12 | | `CxxVector` | `std::vector` | 13 | 14 |
15 | 16 | - These types can be used in the fields of shared structs and the arguments and 17 | returns of extern functions. 18 | - Note that Rust's `String` does not map directly to `std::string`. There are a 19 | few reasons for this: 20 | - `std::string` does not uphold the UTF-8 invariant that `String` requires. 21 | - The two types have different layouts in memory and so can't be passed 22 | directly between languages. 23 | - `std::string` requires move constructors that don't match Rust's move 24 | semantics, so a `std::string` can't be passed by value to Rust. 25 | 26 |
27 | -------------------------------------------------------------------------------- /src/android/interoperability/java/Android.bp: -------------------------------------------------------------------------------- 1 | // ANCHOR: libhello_jni 2 | rust_ffi_shared { 3 | name: "libhello_jni", 4 | crate_name: "hello_jni", 5 | srcs: ["src/lib.rs"], 6 | rustlibs: ["libjni"], 7 | } 8 | // ANCHOR_END: libhello_jni 9 | 10 | // ANCHOR: helloworld_jni 11 | java_binary { 12 | name: "helloworld_jni", 13 | srcs: ["HelloWorld.java"], 14 | main_class: "HelloWorld", 15 | jni_libs: ["libhello_jni"], 16 | } 17 | // ANCHOR_END: helloworld_jni 18 | -------------------------------------------------------------------------------- /src/android/interoperability/java/HelloWorld.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // ANCHOR: HelloWorld 18 | class HelloWorld { 19 | private static native String hello(String name); 20 | 21 | static { 22 | System.loadLibrary("hello_jni"); 23 | } 24 | 25 | public static void main(String[] args) { 26 | String output = HelloWorld.hello("Alice"); 27 | System.out.println(output); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/bindgen/Android.bp: -------------------------------------------------------------------------------- 1 | // ANCHOR: libbirthday 2 | cc_library { 3 | name: "libbirthday", 4 | srcs: ["libbirthday.c"], 5 | } 6 | // ANCHOR_END: libbirthday 7 | 8 | // ANCHOR: libbirthday_bindgen 9 | rust_bindgen { 10 | name: "libbirthday_bindgen", 11 | crate_name: "birthday_bindgen", 12 | wrapper_src: "libbirthday_wrapper.h", 13 | source_stem: "bindings", 14 | static_libs: ["libbirthday"], 15 | } 16 | // ANCHOR_END: libbirthday_bindgen 17 | 18 | // ANCHOR: libbirthday_bindgen_test 19 | rust_test { 20 | name: "libbirthday_bindgen_test", 21 | srcs: [":libbirthday_bindgen"], 22 | crate_name: "libbirthday_bindgen_test", 23 | test_suites: ["general-tests"], 24 | auto_gen_config: true, 25 | clippy_lints: "none", // Generated file, skip linting 26 | lints: "none", 27 | } 28 | // ANCHOR_END: libbirthday_bindgen_test 29 | 30 | // ANCHOR: print_birthday_card 31 | rust_binary { 32 | name: "print_birthday_card", 33 | srcs: ["main.rs"], 34 | rustlibs: ["libbirthday_bindgen"], 35 | static_libs: ["libbirthday"], 36 | } 37 | // ANCHOR_END: print_birthday_card 38 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/bindgen/c-library.md: -------------------------------------------------------------------------------- 1 | # Create a C library 2 | 3 | _interoperability/c/libbirthday/Android.bp_: 4 | 5 | ```javascript 6 | {{#include c/libbirthday/Android.bp:libbirthday}} 7 | ``` 8 | 9 | _interoperability/c/libbirthday/libbirthday.h_: 10 | 11 | ```c 12 | {{#include c/libbirthday/libbirthday.h}} 13 | ``` 14 | 15 | _interoperability/c/libbirthday/libbirthday.c_: 16 | 17 | ```c 18 | {{#include c/libbirthday/libbirthday.c}} 19 | ``` 20 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/bindgen/libbirthday.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // ANCHOR: print_card 18 | #include 19 | #include "libbirthday.h" 20 | 21 | void print_card(const card* card) { 22 | printf("+--------------\n"); 23 | printf("| Happy Birthday %s!\n", card->name); 24 | printf("| Congratulations with the %i years!\n", card->years); 25 | printf("+--------------\n"); 26 | } 27 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/bindgen/libbirthday.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // ANCHOR: card 18 | typedef struct card { 19 | const char* name; 20 | int years; 21 | } card; 22 | 23 | void print_card(const card* card); 24 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/bindgen/libbirthday_wrapper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // ANCHOR: include 18 | #include "libbirthday.h" 19 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/c-library.md: -------------------------------------------------------------------------------- 1 | # A Simple C Library 2 | 3 | Let's first create a small C library: 4 | 5 | _interoperability/bindgen/libbirthday.h_: 6 | 7 | ```c 8 | {{#include bindgen/libbirthday.h:card}} 9 | ``` 10 | 11 | _interoperability/bindgen/libbirthday.c_: 12 | 13 | ```c 14 | {{#include bindgen/libbirthday.c:print_card}} 15 | ``` 16 | 17 | Add this to your `Android.bp` file: 18 | 19 | _interoperability/bindgen/Android.bp_: 20 | 21 | ```javascript 22 | {{#include bindgen/Android.bp:libbirthday}} 23 | ``` 24 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/hand-written.md: -------------------------------------------------------------------------------- 1 | # Handwritten FFI 2 | 3 | We can declare external functions by hand: 4 | 5 | ```rust 6 | unsafe extern "C" { 7 | safe fn abs(x: i32) -> i32; 8 | } 9 | 10 | fn main() { 11 | let x = -42; 12 | let abs_x = abs(x); 13 | println!("{x}, {abs_x}"); 14 | } 15 | ``` 16 | 17 | We already saw this in the 18 | [Safe FFI Wrapper exercise](../../exercises/day-3/safe-ffi-wrapper.md). 19 | 20 | > This assumes full knowledge of the target platform. Not recommended for 21 | > production. 22 | 23 |
24 | 25 | - This is just a motivating example. For a real library, you want to use 26 | `bindgen` as shown on the next slide. 27 | 28 |
29 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/run-our-binary.md: -------------------------------------------------------------------------------- 1 | # Running Our Binary 2 | 3 | Build, push, and run the binary on your device: 4 | 5 | ```shell 6 | {{#include ../../build_all.sh:print_birthday_card}} 7 | ``` 8 | 9 | Finally, we can run auto-generated tests to ensure the bindings work: 10 | 11 | _interoperability/bindgen/Android.bp_: 12 | 13 | ```javascript 14 | {{#include bindgen/Android.bp:libbirthday_bindgen_test}} 15 | ``` 16 | 17 | ```shell 18 | {{#include ../../build_all.sh:libbirthday_bindgen_test}} 19 | ``` 20 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/rust-library.md: -------------------------------------------------------------------------------- 1 | # A Simple Rust Library 2 | 3 | Exporting Rust functions and types to C is easy. Here's a simple Rust library: 4 | 5 | _interoperability/rust/libanalyze/analyze.rs_ 6 | 7 | ```rust,editable 8 | {{#include rust/libanalyze/analyze.rs:analyze_numbers}} 9 | ``` 10 | 11 | _interoperability/rust/libanalyze/Android.bp_ 12 | 13 | ```javascript 14 | {{#include rust/libanalyze/Android.bp}} 15 | ``` 16 | 17 |
18 | 19 | `#[unsafe(no_mangle)]` disables Rust's usual name mangling, so the exported 20 | symbol will just be the name of the function. You can also use 21 | `#[unsafe(export_name = "some_name")]` to specify whatever name you want. 22 | 23 |
24 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/rust.md: -------------------------------------------------------------------------------- 1 | # Calling Rust 2 | 3 | We can now call this from a C binary: 4 | 5 | _interoperability/rust/libanalyze/analyze.h_ 6 | 7 | ```c 8 | {{#include rust/libanalyze/analyze.h:analyze_numbers}} 9 | ``` 10 | 11 | _interoperability/rust/analyze/main.c_ 12 | 13 | ```c 14 | {{#include rust/analyze/main.c:main}} 15 | ``` 16 | 17 | _interoperability/rust/analyze/Android.bp_ 18 | 19 | ```javascript 20 | {{#include rust/analyze/Android.bp}} 21 | ``` 22 | 23 | Build, push, and run the binary on your device: 24 | 25 | ```shell 26 | {{#include ../../build_all.sh:analyze_numbers}} 27 | ``` 28 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/rust/analyze/Android.bp: -------------------------------------------------------------------------------- 1 | cc_binary { 2 | name: "analyze_numbers", 3 | srcs: ["main.c"], 4 | static_libs: ["libanalyze_ffi"], 5 | } 6 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/rust/analyze/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // ANCHOR: main 18 | #include "analyze.h" 19 | 20 | int main() { 21 | analyze_numbers(10, 20); 22 | analyze_numbers(123, 123); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/rust/libanalyze/Android.bp: -------------------------------------------------------------------------------- 1 | rust_ffi { 2 | name: "libanalyze_ffi", 3 | crate_name: "analyze_ffi", 4 | srcs: ["analyze.rs"], 5 | include_dirs: ["."], 6 | } 7 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/rust/libanalyze/analyze.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // ANCHOR: analyze_numbers 18 | #ifndef ANALYZE_H 19 | #define ANALYZE_H 20 | 21 | void analyze_numbers(int x, int y); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/android/interoperability/with-c/rust/libanalyze/analyze.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: analyze_numbers 16 | //! Rust FFI demo. 17 | #![deny(improper_ctypes_definitions)] 18 | 19 | use std::os::raw::c_int; 20 | 21 | /// Analyze the numbers. 22 | // SAFETY: There is no other global function of this name. 23 | #[unsafe(no_mangle)] 24 | pub extern "C" fn analyze_numbers(x: c_int, y: c_int) { 25 | if x < y { 26 | println!("x ({x}) is smallest!"); 27 | } else { 28 | println!("y ({y}) is probably larger than x ({x})"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/android/logging.md: -------------------------------------------------------------------------------- 1 | # Logging 2 | 3 | You should use the `log` crate to automatically log to `logcat` (on-device) or 4 | `stdout` (on-host): 5 | 6 | _hello_rust_logs/Android.bp_: 7 | 8 | ```javascript 9 | {{#include logging/Android.bp}} 10 | ``` 11 | 12 | _hello_rust_logs/src/main.rs_: 13 | 14 | ```rust,ignore 15 | {{#include logging/src/main.rs:main}} 16 | ``` 17 | 18 | Build, push, and run the binary on your device: 19 | 20 | ```shell 21 | {{#include build_all.sh:hello_rust_logs}} 22 | ``` 23 | 24 | The logs show up in `adb logcat`: 25 | 26 | ```shell 27 | adb logcat -s rust 28 | ``` 29 | 30 | ```text 31 | 09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting program. 32 | 09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going fine. 33 | 09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went wrong! 34 | ``` 35 | 36 |
37 | 38 | - The logger implementation in `liblogger` is only needed in the final binary, 39 | if you're logging from a library you only need the `log` facade crate. 40 | 41 |
42 | -------------------------------------------------------------------------------- /src/android/logging/Android.bp: -------------------------------------------------------------------------------- 1 | rust_binary { 2 | name: "hello_rust_logs", 3 | crate_name: "hello_rust_logs", 4 | srcs: ["src/main.rs"], 5 | rustlibs: [ 6 | "liblog_rust", 7 | "liblogger", 8 | ], 9 | host_supported: true, 10 | } 11 | -------------------------------------------------------------------------------- /src/android/logging/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: main 16 | //! Rust logging demo. 17 | 18 | use log::{debug, error, info}; 19 | 20 | /// Logs a greeting. 21 | fn main() { 22 | logger::init( 23 | logger::Config::default() 24 | .with_tag_on_device("rust") 25 | .with_max_level(log::LevelFilter::Trace), 26 | ); 27 | debug!("Starting program."); 28 | info!("Things are going fine."); 29 | error!("Something went wrong!"); 30 | } 31 | -------------------------------------------------------------------------------- /src/android/setup.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | We will be using a Cuttlefish Android Virtual Device to test our code. Make sure 4 | you have access to one or create a new one with: 5 | 6 | ```shell 7 | source build/envsetup.sh 8 | lunch aosp_cf_x86_64_phone-trunk_staging-userdebug 9 | acloud create 10 | ``` 11 | 12 | Please see the 13 | [Android Developer Codelab](https://source.android.com/docs/setup/start) for 14 | details. 15 | 16 | The code on the following pages can be found in the 17 | [`src/android/` directory](https://github.com/google/comprehensive-rust/tree/main/src/android) 18 | of the course material. Please `git clone` the repository to follow along. 19 | 20 |
21 | 22 | Key points: 23 | 24 | - Cuttlefish is a reference Android device designed to work on generic Linux 25 | desktops. MacOS support is also planned. 26 | 27 | - The Cuttlefish system image maintains high fidelity to real devices, and is 28 | the ideal emulator to run many Rust use cases. 29 | 30 |
31 | -------------------------------------------------------------------------------- /src/android/testing.md: -------------------------------------------------------------------------------- 1 | # Testing in Android 2 | 3 | Building on [Testing](../testing.md), we will now look at how unit tests work in 4 | AOSP. Use the `rust_test` module for your unit tests: 5 | 6 | _testing/Android.bp_: 7 | 8 | ```javascript 9 | {{#include testing/Android.bp}} 10 | ``` 11 | 12 | _testing/src/lib.rs_: 13 | 14 | ```rust 15 | {{#include testing/src/lib.rs:leftpad}} 16 | ``` 17 | 18 | You can now run the test with 19 | 20 | ```shell 21 | {{#include build_all.sh:libleftpad_test}} 22 | ``` 23 | 24 | The output looks like this: 25 | 26 | ```text 27 | INFO: Elapsed time: 2.666s, Critical Path: 2.40s 28 | INFO: 3 processes: 2 internal, 1 linux-sandbox. 29 | INFO: Build completed successfully, 3 total actions 30 | //comprehensive-rust-android/testing:libleftpad_test_host PASSED in 2.3s 31 | PASSED libleftpad_test.tests::long_string (0.0s) 32 | PASSED libleftpad_test.tests::short_string (0.0s) 33 | Test cases: finished with 2 passing and 0 failing out of 2 test cases 34 | ``` 35 | 36 | Notice how you only mention the root of the library crate. Tests are found 37 | recursively in nested modules. 38 | -------------------------------------------------------------------------------- /src/android/testing/Android.bp: -------------------------------------------------------------------------------- 1 | rust_library { 2 | name: "libleftpad", 3 | crate_name: "leftpad", 4 | srcs: ["src/lib.rs"], 5 | } 6 | 7 | rust_test { 8 | name: "libleftpad_test", 9 | crate_name: "leftpad_test", 10 | srcs: ["src/lib.rs"], 11 | host_supported: true, 12 | test_suites: ["general-tests"], 13 | } 14 | 15 | rust_test { 16 | name: "libgoogletest_example", 17 | crate_name: "googletest_example", 18 | srcs: ["googletest.rs"], 19 | rustlibs: ["libgoogletest_rust"], 20 | host_supported: true, 21 | } 22 | 23 | rust_test { 24 | name: "libmockall_example", 25 | crate_name: "mockall_example", 26 | srcs: ["mockall.rs"], 27 | rustlibs: ["libmockall"], 28 | host_supported: true, 29 | } 30 | -------------------------------------------------------------------------------- /src/android/testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "android-testing" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[example]] 8 | name = "googletest-example" 9 | crate-type = ["staticlib"] 10 | path = "googletest.rs" 11 | test = true 12 | 13 | [[example]] 14 | name = "mockall-example" 15 | crate-type = ["staticlib"] 16 | path = "mockall.rs" 17 | test = true 18 | 19 | [dependencies] 20 | googletest = "0.14.1" 21 | mockall = "0.13.1" 22 | -------------------------------------------------------------------------------- /src/android/testing/googletest.rs: -------------------------------------------------------------------------------- 1 | // ANCHOR: test_elements_are 2 | use googletest::prelude::*; 3 | 4 | #[googletest::test] 5 | fn test_elements_are() { 6 | let value = vec!["foo", "bar", "baz"]; 7 | expect_that!(value, elements_are!(eq(&"foo"), lt(&"xyz"), starts_with("b"))); 8 | } 9 | // ANCHOR_END: test_elements_are 10 | 11 | #[should_panic] 12 | // ANCHOR: test_multiline_string_diff 13 | #[test] 14 | fn test_multiline_string_diff() { 15 | let haiku = "Memory safety found,\n\ 16 | Rust's strong typing guides the way,\n\ 17 | Secure code you'll write."; 18 | assert_that!( 19 | haiku, 20 | eq("Memory safety found,\n\ 21 | Rust's silly humor guides the way,\n\ 22 | Secure code you'll write.") 23 | ); 24 | } 25 | // ANCHOR_END: test_multiline_string_diff 26 | -------------------------------------------------------------------------------- /src/android/testing/mockall.rs: -------------------------------------------------------------------------------- 1 | // ANCHOR: simple_example 2 | use std::time::Duration; 3 | 4 | #[mockall::automock] 5 | pub trait Pet { 6 | fn is_hungry(&self, since_last_meal: Duration) -> bool; 7 | } 8 | 9 | #[test] 10 | fn test_robot_dog() { 11 | let mut mock_dog = MockPet::new(); 12 | mock_dog.expect_is_hungry().return_const(true); 13 | assert!(mock_dog.is_hungry(Duration::from_secs(10))); 14 | } 15 | // ANCHOR_END: simple_example 16 | 17 | // ANCHOR: extended_example 18 | #[test] 19 | fn test_robot_cat() { 20 | let mut mock_cat = MockPet::new(); 21 | mock_cat 22 | .expect_is_hungry() 23 | .with(mockall::predicate::gt(Duration::from_secs(3 * 3600))) 24 | .return_const(true); 25 | mock_cat.expect_is_hungry().return_const(false); 26 | assert!(mock_cat.is_hungry(Duration::from_secs(5 * 3600))); 27 | assert!(!mock_cat.is_hungry(Duration::from_secs(5))); 28 | } 29 | // ANCHOR_END: extended_example 30 | -------------------------------------------------------------------------------- /src/android/testing/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: leftpad 16 | //! Left-padding library. 17 | 18 | /// Left-pad `s` to `width`. 19 | pub fn leftpad(s: &str, width: usize) -> String { 20 | format!("{s:>width$}") 21 | } 22 | 23 | #[cfg(test)] 24 | mod tests { 25 | use super::*; 26 | 27 | #[test] 28 | fn short_string() { 29 | assert_eq!(leftpad("foo", 5), " foo"); 30 | } 31 | 32 | #[test] 33 | fn long_string() { 34 | assert_eq!(leftpad("foobar", 6), "foobar"); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/bare-metal/alloc-example/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "aarch64-unknown-none" 3 | -------------------------------------------------------------------------------- /src/bare-metal/alloc-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "alloc-example" 5 | version = "0.1.0" 6 | edition = "2024" 7 | publish = false 8 | 9 | [dependencies] 10 | buddy_system_allocator = "0.11.0" 11 | panic-halt = "1.0.0" 12 | 13 | [profile.dev] 14 | panic = "abort" 15 | 16 | [profile.release] 17 | panic = "abort" 18 | -------------------------------------------------------------------------------- /src/bare-metal/alloc.md: -------------------------------------------------------------------------------- 1 | # `alloc` 2 | 3 | To use `alloc` you must implement a 4 | [global (heap) allocator](https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html). 5 | 6 | ```rust,editable,compile_fail 7 | {{#include alloc-example/src/main.rs:Alloc}} 8 | ``` 9 | 10 |
11 | 12 | - `buddy_system_allocator` is a crate implementing a basic buddy system 13 | allocator. Other crates are available, or you can write your own or hook into 14 | your existing allocator. 15 | - The const parameter of `LockedHeap` is the max order of the allocator; i.e. in 16 | this case it can allocate regions of up to 2**32 bytes. 17 | - If any crate in your dependency tree depends on `alloc` then you must have 18 | exactly one global allocator defined in your binary. Usually this is done in 19 | the top-level binary crate. 20 | - `extern crate panic_halt as _` is necessary to ensure that the `panic_halt` 21 | crate is linked in so we get its panic handler. 22 | - This example will build but not run, as it doesn't have an entry point. 23 | 24 |
25 | -------------------------------------------------------------------------------- /src/bare-metal/android.md: -------------------------------------------------------------------------------- 1 | # Bare-Metal on Android 2 | 3 | To build a bare-metal Rust binary in AOSP, you need to use a `rust_ffi_static` 4 | Soong rule to build your Rust code, then a `cc_binary` with a linker script to 5 | produce the binary itself, and then a `raw_binary` to convert the ELF to a raw 6 | binary ready to be run. 7 | 8 | 9 | 10 | ```soong 11 | rust_ffi_static { 12 | name: "libvmbase_example", 13 | defaults: ["vmbase_ffi_defaults"], 14 | crate_name: "vmbase_example", 15 | srcs: ["src/main.rs"], 16 | rustlibs: [ 17 | "libvmbase", 18 | ], 19 | } 20 | 21 | cc_binary { 22 | name: "vmbase_example", 23 | defaults: ["vmbase_elf_defaults"], 24 | srcs: [ 25 | "idmap.S", 26 | ], 27 | static_libs: [ 28 | "libvmbase_example", 29 | ], 30 | linker_scripts: [ 31 | "image.ld", 32 | ":vmbase_sections", 33 | ], 34 | } 35 | 36 | raw_binary { 37 | name: "vmbase_example_bin", 38 | stem: "vmbase_example.bin", 39 | src: ":vmbase_example", 40 | enabled: false, 41 | target: { 42 | android_arm64: { 43 | enabled: true, 44 | }, 45 | }, 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /src/bare-metal/android/vmbase.md: -------------------------------------------------------------------------------- 1 | # vmbase 2 | 3 | For VMs running under crosvm on aarch64, the [vmbase][1] library provides a 4 | linker script and useful defaults for the build rules, along with an entry 5 | point, UART console logging and more. 6 | 7 | 8 | 9 | ```rust,compile_fail 10 | #![no_main] 11 | #![no_std] 12 | 13 | use vmbase::{main, println}; 14 | 15 | main!(main); 16 | 17 | pub fn main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) { 18 | println!("Hello world"); 19 | } 20 | ``` 21 | 22 |
23 | 24 | - The `main!` macro marks your main function, to be called from the `vmbase` 25 | entry point. 26 | - The `vmbase` entry point handles console initialisation, and issues a 27 | PSCI_SYSTEM_OFF to shutdown the VM if your main function returns. 28 | 29 |
30 | 31 | [1]: https://android.googlesource.com/platform/packages/modules/Virtualization/+/refs/heads/main/libs/libvmbase/ 32 | -------------------------------------------------------------------------------- /src/bare-metal/aps.md: -------------------------------------------------------------------------------- 1 | --- 2 | session: Afternoon 3 | --- 4 | 5 | # Application processors 6 | 7 | So far we've talked about microcontrollers, such as the Arm Cortex-M series. 8 | These are typically small systems with very limited resources. 9 | 10 | Larger systems with more resources are typically called application processors, 11 | built around processors such as the ARM Cortex-A or Intel Atom. 12 | 13 | For simplicity we'll just work with QEMU's aarch64 14 | ['virt'](https://qemu-project.gitlab.io/qemu/system/arm/virt.html) board. 15 | 16 |
17 | 18 | - Broadly speaking, microcontrollers don't have an MMU or multiple levels of 19 | privilege (exception levels on Arm CPUs, rings on x86). 20 | - Application processors have more resources, and often run an operating system, 21 | instead of directly executing the target application on startup. 22 | - QEMU supports emulating various different machines or board models for each 23 | architecture. The 'virt' board doesn't correspond to any particular real 24 | hardware, but is designed purely for virtual machines. 25 | - We will still address this board as bare-metal, as if we were writing an 26 | operating system. 27 | 28 |
29 | -------------------------------------------------------------------------------- /src/bare-metal/aps/aarch64-rt.md: -------------------------------------------------------------------------------- 1 | # aarch64-rt 2 | 3 | The `aarch64-rt` crate provides the assembly entry point and exception vector 4 | that we implemented before. We just need to mark our main function with the 5 | `entry!` macro. 6 | 7 | It also provides the `initial_pagetable!` macro to let us define an initial 8 | static pagetable in Rust, rather than in assembly code like we did before. 9 | 10 | We can also use the UART driver from the `arm-pl011-uart` crate rather than 11 | writing our own. 12 | 13 | ```rust,editable,compile_fail 14 | {{#include examples/src/main_rt.rs:main}} 15 | ``` 16 | -------------------------------------------------------------------------------- /src/bare-metal/aps/better-uart.md: -------------------------------------------------------------------------------- 1 | # A better UART driver 2 | 3 | The PL011 actually has [a bunch more registers][1], and adding offsets to 4 | construct pointers to access them is error-prone and hard to read. Plus, some of 5 | them are bit fields which would be nice to access in a structured way. 6 | 7 | | Offset | Register name | Width | 8 | | ------ | ------------- | ----- | 9 | | 0x00 | DR | 12 | 10 | | 0x04 | RSR | 4 | 11 | | 0x18 | FR | 9 | 12 | | 0x20 | ILPR | 8 | 13 | | 0x24 | IBRD | 16 | 14 | | 0x28 | FBRD | 6 | 15 | | 0x2c | LCR_H | 8 | 16 | | 0x30 | CR | 16 | 17 | | 0x34 | IFLS | 6 | 18 | | 0x38 | IMSC | 11 | 19 | | 0x3c | RIS | 11 | 20 | | 0x40 | MIS | 11 | 21 | | 0x44 | ICR | 11 | 22 | | 0x48 | DMACR | 3 | 23 | 24 |
25 | 26 | - There are also some ID registers which have been omitted for brevity. 27 | 28 |
29 | 30 | [1]: https://developer.arm.com/documentation/ddi0183/g/programmers-model/summary-of-registers 31 | -------------------------------------------------------------------------------- /src/bare-metal/aps/better-uart/bitflags.md: -------------------------------------------------------------------------------- 1 | # Bitflags 2 | 3 | The [`bitflags`](https://crates.io/crates/bitflags) crate is useful for working 4 | with bitflags. 5 | 6 | ```rust,editable,compile_fail 7 | {{#include ../examples/src/pl011.rs:Flags}} 8 | ``` 9 | 10 |
11 | 12 | - The `bitflags!` macro creates a newtype something like `Flags(u16)`, along 13 | with a bunch of method implementations to get and set flags. 14 | 15 |
16 | -------------------------------------------------------------------------------- /src/bare-metal/aps/better-uart/driver.md: -------------------------------------------------------------------------------- 1 | # Driver 2 | 3 | Now let's use the new `Registers` struct in our driver. 4 | 5 | ```rust,editable,compile_fail 6 | {{#include ../examples/src/pl011.rs:Uart}} 7 | ``` 8 | 9 |
10 | 11 | - Note the use of `&raw const` / `&raw mut` to get pointers to individual fields 12 | without creating an intermediate reference, which would be unsound. 13 | 14 |
15 | -------------------------------------------------------------------------------- /src/bare-metal/aps/better-uart/registers.md: -------------------------------------------------------------------------------- 1 | # Multiple registers 2 | 3 | We can use a struct to represent the memory layout of the UART's registers. 4 | 5 | 6 | 7 | ```rust,editable,compile_fail 8 | {{#include ../examples/src/pl011.rs:Registers}} 9 | ``` 10 | 11 |
12 | 13 | - [`#[repr(C)]`](https://doc.rust-lang.org/reference/type-layout.html#the-c-representation) 14 | tells the compiler to lay the struct fields out in order, following the same 15 | rules as C. This is necessary for our struct to have a predictable layout, as 16 | default Rust representation allows the compiler to (among other things) 17 | reorder fields however it sees fit. 18 | 19 |
20 | -------------------------------------------------------------------------------- /src/bare-metal/aps/better-uart/using.md: -------------------------------------------------------------------------------- 1 | # Using it 2 | 3 | Let's write a small program using our driver to write to the serial console, and 4 | echo incoming bytes. 5 | 6 | ```rust,editable,compile_fail 7 | {{#include ../examples/src/main_improved.rs:main}} 8 | ``` 9 | 10 |
11 | 12 | - Run the example in QEMU with `make qemu` under `src/bare-metal/aps/examples`. 13 | 14 |
15 | -------------------------------------------------------------------------------- /src/bare-metal/aps/examples/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "aarch64-unknown-none" 3 | rustflags = ["-C", "link-arg=-Timage.ld"] 4 | -------------------------------------------------------------------------------- /src/bare-metal/aps/examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "ap-examples" 5 | version = "0.1.0" 6 | edition = "2024" 7 | publish = false 8 | 9 | [dependencies] 10 | aarch64-paging = { version = "0.9.1", default-features = false } 11 | aarch64-rt = "0.1.3" 12 | arm-pl011-uart = "0.3.1" 13 | bitflags = "2.9.1" 14 | log = "0.4.27" 15 | smccc = "0.2.0" 16 | spin = "0.10.0" 17 | 18 | [[bin]] 19 | name = "improved" 20 | path = "src/main_improved.rs" 21 | 22 | [[bin]] 23 | name = "logger" 24 | path = "src/main_logger.rs" 25 | 26 | [[bin]] 27 | name = "minimal" 28 | path = "src/main_minimal.rs" 29 | 30 | [[bin]] 31 | name = "rt" 32 | path = "src/main_rt.rs" 33 | 34 | [[bin]] 35 | name = "psci" 36 | path = "src/main_psci.rs" 37 | -------------------------------------------------------------------------------- /src/bare-metal/aps/examples/src/asm.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use core::arch::global_asm; 16 | 17 | global_asm!(include_str!("entry.S")); 18 | global_asm!(include_str!("exceptions.S")); 19 | global_asm!(include_str!("idmap.S")); 20 | -------------------------------------------------------------------------------- /src/bare-metal/aps/logging.md: -------------------------------------------------------------------------------- 1 | # Logging 2 | 3 | It would be nice to be able to use the logging macros from the [`log`][1] crate. 4 | We can do this by implementing the `Log` trait. 5 | 6 | ```rust,editable,compile_fail 7 | {{#include examples/src/logger.rs:main}} 8 | ``` 9 | 10 |
11 | 12 | - The first unwrap in `log` will succeed because we initialise `LOGGER` before 13 | calling `set_logger`. The second will succeed because `Uart::write_str` always 14 | returns `Ok`. 15 | 16 |
17 | 18 | [1]: https://crates.io/crates/log 19 | -------------------------------------------------------------------------------- /src/bare-metal/aps/logging/using.md: -------------------------------------------------------------------------------- 1 | # Using it 2 | 3 | We need to initialise the logger before we use it. 4 | 5 | ```rust,editable,compile_fail 6 | {{#include ../examples/src/main_logger.rs:main}} 7 | ``` 8 | 9 |
10 | 11 | - Note that our panic handler can now log details of panics. 12 | - Run the example in QEMU with `make qemu_logger` under 13 | `src/bare-metal/aps/examples`. 14 | 15 |
16 | -------------------------------------------------------------------------------- /src/bare-metal/aps/other-projects.md: -------------------------------------------------------------------------------- 1 | # Other projects 2 | 3 | - [oreboot](https://github.com/oreboot/oreboot) 4 | - "coreboot without the C". 5 | - Supports x86, aarch64 and RISC-V. 6 | - Relies on LinuxBoot rather than having many drivers itself. 7 | - [Rust RaspberryPi OS tutorial](https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials) 8 | - Initialisation, UART driver, simple bootloader, JTAG, exception levels, 9 | exception handling, page tables. 10 | - Some dodginess around cache maintenance and initialisation in Rust, not 11 | necessarily a good example to copy for production code. 12 | - [`cargo-call-stack`](https://crates.io/crates/cargo-call-stack) 13 | - Static analysis to determine maximum stack usage. 14 | 15 |
16 | 17 | - The RaspberryPi OS tutorial runs Rust code before the MMU and caches are 18 | enabled. This will read and write memory (e.g. the stack). However, this has 19 | the problems mentioned at the beginning of this session regarding unaligned 20 | access and cache coherency. 21 | 22 |
23 | -------------------------------------------------------------------------------- /src/bare-metal/aps/uart.md: -------------------------------------------------------------------------------- 1 | # Let's write a UART driver 2 | 3 | The QEMU 'virt' machine has a [PL011][1] UART, so let's write a driver for that. 4 | 5 | ```rust,editable 6 | {{#include examples/src/pl011_minimal.rs:Example}} 7 | ``` 8 | 9 |
10 | 11 | - Note that `Uart::new` is unsafe while the other methods are safe. This is 12 | because as long as the caller of `Uart::new` guarantees that its safety 13 | requirements are met (i.e. that there is only ever one instance of the driver 14 | for a given UART, and nothing else aliasing its address space), then it is 15 | always safe to call `write_byte` later because we can assume the necessary 16 | preconditions. 17 | - We could have done it the other way around (making `new` safe but `write_byte` 18 | unsafe), but that would be much less convenient to use as every place that 19 | calls `write_byte` would need to reason about the safety 20 | - This is a common pattern for writing safe wrappers of unsafe code: moving the 21 | burden of proof for soundness from a large number of places to a smaller 22 | number of places. 23 | 24 |
25 | 26 | [1]: https://developer.arm.com/documentation/ddi0183/g 27 | -------------------------------------------------------------------------------- /src/bare-metal/aps/uart/traits.md: -------------------------------------------------------------------------------- 1 | # More traits 2 | 3 | We derived the `Debug` trait. It would be useful to implement a few more traits 4 | too. 5 | 6 | ```rust,editable,compile_fail 7 | {{#include ../examples/src/pl011_minimal.rs:Traits}} 8 | ``` 9 | 10 |
11 | 12 | - Implementing `Write` lets us use the `write!` and `writeln!` macros with our 13 | `Uart` type. 14 | 15 | - `Send` is an auto-trait, but not implemented automatically because it is not 16 | implemented for pointers. 17 | 18 |
19 | -------------------------------------------------------------------------------- /src/bare-metal/aps/uart/using.md: -------------------------------------------------------------------------------- 1 | # Using it 2 | 3 | Let's write a small program using our driver to write to the serial console. 4 | 5 | ```rust,editable,compile_fail 6 | {{#include ../examples/src/main_minimal.rs:main}} 7 | ``` 8 | 9 |
10 | 11 | - As in the [inline assembly](../inline-assembly.md) example, this `main` 12 | function is called from our entry point code in `entry.S`. See the speaker 13 | notes there for details. 14 | - Run the example in QEMU with `make qemu_minimal` under 15 | `src/bare-metal/aps/examples`. 16 | 17 |
18 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers.md: -------------------------------------------------------------------------------- 1 | # Microcontrollers 2 | 3 | The `cortex_m_rt` crate provides (among other things) a reset handler for Cortex 4 | M microcontrollers. 5 | 6 | 7 | 8 | ```rust,editable,compile_fail 9 | {{#include microcontrollers/examples/src/bin/minimal.rs:Example}} 10 | ``` 11 | 12 | Next we'll look at how to access peripherals, with increasing levels of 13 | abstraction. 14 | 15 |
16 | 17 | - The `cortex_m_rt::entry` macro requires that the function have type 18 | `fn() -> !`, because returning to the reset handler doesn't make sense. 19 | - Run the example with `cargo embed --bin minimal` 20 | 21 |
22 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/board-support.md: -------------------------------------------------------------------------------- 1 | # Board support crates 2 | 3 | Board support crates provide a further level of wrapping for a specific board 4 | for convenience. 5 | 6 | 7 | 8 | ```rust,editable,compile_fail 9 | {{#include examples/src/bin/board_support.rs:Example}} 10 | ``` 11 | 12 |
13 | 14 | - In this case the board support crate is just providing more useful names, and 15 | a bit of initialisation. 16 | - The crate may also include drivers for some on-board devices outside of the 17 | microcontroller itself. 18 | - `microbit-v2` includes a simple driver for the LED matrix. 19 | 20 | Run the example with: 21 | 22 | ```sh 23 | cargo embed --bin board_support 24 | ``` 25 | 26 |
27 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | 3 | _Embed.toml_: 4 | 5 | 6 | 7 | ```toml 8 | [default.general] 9 | chip = "nrf52833_xxAA" 10 | 11 | [debug.gdb] 12 | enabled = true 13 | ``` 14 | 15 | In one terminal under `src/bare-metal/microcontrollers/examples/`: 16 | 17 | 18 | 19 | ```sh 20 | cargo embed --bin board_support debug 21 | ``` 22 | 23 | In another terminal in the same directory: 24 | 25 | On gLinux or Debian: 26 | 27 | 28 | 29 | ```sh 30 | gdb-multiarch target/thumbv7em-none-eabihf/debug/board_support --eval-command="target remote :1337" 31 | ``` 32 | 33 | On MacOS: 34 | 35 | 36 | 37 | ```sh 38 | arm-none-eabi-gdb target/thumbv7em-none-eabihf/debug/board_support --eval-command="target remote :1337" 39 | ``` 40 | 41 |
42 | 43 | In GDB, try running: 44 | 45 | 46 | 47 | ```gdb 48 | b src/bin/board_support.rs:29 49 | b src/bin/board_support.rs:30 50 | b src/bin/board_support.rs:32 51 | c 52 | c 53 | c 54 | ``` 55 | 56 |
57 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/examples/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" # Cortex-M4F 3 | 4 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "microcontroller-examples" 5 | version = "0.1.0" 6 | edition = "2024" 7 | publish = false 8 | 9 | [dependencies] 10 | cortex-m-rt = "0.7.5" 11 | embedded-hal = "1.0.0" 12 | microbit-v2 = "0.15.1" 13 | nrf52833-hal = "0.18.0" 14 | nrf52833-pac = { version = "0.12.2", features = ["rt"] } 15 | panic-halt = "1.0.0" 16 | 17 | [[bin]] 18 | name = "board_support" 19 | 20 | [[bin]] 21 | name = "hal" 22 | 23 | [[bin]] 24 | name = "minimal" 25 | 26 | [[bin]] 27 | name = "mmio" 28 | 29 | [[bin]] 30 | name = "pac" 31 | 32 | [[bin]] 33 | name = "typestate" 34 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/examples/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nrf52833_xxAA" 3 | 4 | [debug.gdb] 5 | enabled = true 6 | 7 | [debug.reset] 8 | halt_afterwards = true 9 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/examples/src/bin/board_support.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: Example 16 | #![no_main] 17 | #![no_std] 18 | 19 | extern crate panic_halt as _; 20 | 21 | use cortex_m_rt::entry; 22 | use embedded_hal::digital::OutputPin; 23 | use microbit::Board; 24 | 25 | #[entry] 26 | fn main() -> ! { 27 | let mut board = Board::take().unwrap(); 28 | 29 | board.display_pins.col1.set_low().unwrap(); 30 | board.display_pins.row1.set_high().unwrap(); 31 | 32 | loop {} 33 | } 34 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/examples/src/bin/interrupts/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #[unsafe(link_section = ".vector_table.interrupts")] 16 | // SAFETY: There is no other global variable of this name. 17 | #[unsafe(no_mangle)] 18 | pub static __INTERRUPTS: [usize; 1] = [0]; 19 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/examples/src/bin/minimal.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: Example 16 | #![no_main] 17 | #![no_std] 18 | 19 | extern crate panic_halt as _; 20 | 21 | mod interrupts; 22 | 23 | use cortex_m_rt::entry; 24 | 25 | #[entry] 26 | fn main() -> ! { 27 | loop {} 28 | } 29 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/hals.md: -------------------------------------------------------------------------------- 1 | # HAL crates 2 | 3 | [HAL crates](https://github.com/rust-embedded/awesome-embedded-rust#hal-implementation-crates) 4 | for many microcontrollers provide wrappers around various peripherals. These 5 | generally implement traits from 6 | [`embedded-hal`](https://crates.io/crates/embedded-hal). 7 | 8 | ```rust,editable,compile_fail 9 | {{#include examples/src/bin/hal.rs:Example}} 10 | ``` 11 | 12 |
13 | 14 | - `set_low` and `set_high` are methods on the `embedded_hal` `OutputPin` trait. 15 | - HAL crates exist for many Cortex-M and RISC-V devices, including various 16 | STM32, GD32, nRF, NXP, MSP430, AVR and PIC microcontrollers. 17 | 18 | Run the example with: 19 | 20 | ```sh 21 | cargo embed --bin hal 22 | ``` 23 | 24 |
25 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/mmio.md: -------------------------------------------------------------------------------- 1 | # Raw MMIO 2 | 3 | Most microcontrollers access peripherals via memory-mapped IO. Let's try turning 4 | on an LED on our micro:bit: 5 | 6 | ```rust,editable,compile_fail 7 | {{#include examples/src/bin/mmio.rs:Example}} 8 | ``` 9 | 10 |
11 | 12 | - GPIO 0 pin 21 is connected to the first column of the LED matrix, and pin 28 13 | to the first row. 14 | 15 | Run the example with: 16 | 17 | ```sh 18 | cargo embed --bin mmio 19 | ``` 20 | 21 |
22 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/pacs.md: -------------------------------------------------------------------------------- 1 | # Peripheral Access Crates 2 | 3 | [`svd2rust`](https://crates.io/crates/svd2rust) generates mostly-safe Rust 4 | wrappers for memory-mapped peripherals from 5 | [CMSIS-SVD](https://www.keil.com/pack/doc/CMSIS/SVD/html/index.html) files. 6 | 7 | ```rust,editable,compile_fail 8 | {{#include examples/src/bin/pac.rs:Example}} 9 | ``` 10 | 11 |
12 | 13 | - SVD (System View Description) files are XML files typically provided by 14 | silicon vendors which describe the memory map of the device. 15 | - They are organised by peripheral, register, field and value, with names, 16 | descriptions, addresses and so on. 17 | - SVD files are often buggy and incomplete, so there are various projects 18 | which patch the mistakes, add missing details, and publish the generated 19 | crates. 20 | - `cortex-m-rt` provides the vector table, among other things. 21 | - If you `cargo install cargo-binutils` then you can run 22 | `cargo objdump --bin pac -- -d --no-show-raw-insn` to see the resulting 23 | binary. 24 | 25 | Run the example with: 26 | 27 | ```sh 28 | cargo embed --bin pac 29 | ``` 30 | 31 |
32 | -------------------------------------------------------------------------------- /src/bare-metal/microcontrollers/type-state.md: -------------------------------------------------------------------------------- 1 | # The type state pattern 2 | 3 | ```rust,editable,compile_fail 4 | {{#include examples/src/bin/typestate.rs:Example}} 5 | ``` 6 | 7 |
8 | 9 | - Pins don't implement `Copy` or `Clone`, so only one instance of each can 10 | exist. Once a pin is moved out of the port struct nobody else can take it. 11 | - Changing the configuration of a pin consumes the old pin instance, so you 12 | can’t keep use the old instance afterwards. 13 | - The type of a value indicates the state that it is in: e.g. in this case, the 14 | configuration state of a GPIO pin. This encodes the state machine into the 15 | type system, and ensures that you don't try to use a pin in a certain way 16 | without properly configuring it first. Illegal state transitions are caught at 17 | compile time. 18 | - You can call `is_high` on an input pin and `set_high` on an output pin, but 19 | not vice-versa. 20 | - Many HAL crates follow this pattern. 21 | 22 |
23 | -------------------------------------------------------------------------------- /src/bare-metal/minimal.md: -------------------------------------------------------------------------------- 1 | # A minimal `no_std` program 2 | 3 | 4 | 5 | ```rust,editable,compile_fail 6 | #![no_main] 7 | #![no_std] 8 | 9 | use core::panic::PanicInfo; 10 | 11 | #[panic_handler] 12 | fn panic(_panic: &PanicInfo) -> ! { 13 | loop {} 14 | } 15 | ``` 16 | 17 |
18 | 19 | - This will compile to an empty binary. 20 | - `std` provides a panic handler; without it we must provide our own. 21 | - It can also be provided by another crate, such as `panic-halt`. 22 | - Depending on the target, you may need to compile with `panic = "abort"` to 23 | avoid an error about `eh_personality`. 24 | - Note that there is no `main` or any other entry point; it's up to you to 25 | define your own entry point. This will typically involve a linker script and 26 | some assembly code to set things up ready for Rust code to run. 27 | 28 |
29 | -------------------------------------------------------------------------------- /src/bare-metal/useful-crates.md: -------------------------------------------------------------------------------- 1 | # Useful crates 2 | 3 | We'll look at a few crates which solve some common problems in bare-metal 4 | programming. 5 | -------------------------------------------------------------------------------- /src/bare-metal/useful-crates/allocator-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "allocator-example" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | buddy_system_allocator = "0.11.0" 9 | -------------------------------------------------------------------------------- /src/bare-metal/useful-crates/allocator-example/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: main 16 | use buddy_system_allocator::FrameAllocator; 17 | use core::alloc::Layout; 18 | 19 | fn main() { 20 | let mut allocator = FrameAllocator::<32>::new(); 21 | allocator.add_frame(0x200_0000, 0x400_0000); 22 | 23 | let layout = Layout::from_size_align(0x100, 0x100).unwrap(); 24 | let bar = allocator 25 | .alloc_aligned(layout) 26 | .expect("Failed to allocate 0x100 byte MMIO region"); 27 | println!("Allocated 0x100 byte MMIO region at {:#x}", bar); 28 | } 29 | -------------------------------------------------------------------------------- /src/bare-metal/useful-crates/tinyvec.md: -------------------------------------------------------------------------------- 1 | # `tinyvec` 2 | 3 | Sometimes you want something which can be resized like a `Vec`, but without heap 4 | allocation. [`tinyvec`][1] provides this: a vector backed by an array or slice, 5 | which could be statically allocated or on the stack, which keeps track of how 6 | many elements are used and panics if you try to use more than are allocated. 7 | 8 | 9 | 10 | ```rust,editable,compile_fail 11 | use tinyvec::{ArrayVec, array_vec}; 12 | 13 | fn main() { 14 | let mut numbers: ArrayVec<[u32; 5]> = array_vec!(42, 66); 15 | println!("{numbers:?}"); 16 | numbers.push(7); 17 | println!("{numbers:?}"); 18 | numbers.remove(1); 19 | println!("{numbers:?}"); 20 | } 21 | ``` 22 | 23 |
24 | 25 | - `tinyvec` requires that the element type implement `Default` for 26 | initialisation. 27 | - The Rust Playground includes `tinyvec`, so this example will run fine inline. 28 | 29 |
30 | 31 | [1]: https://crates.io/crates/tinyvec 32 | -------------------------------------------------------------------------------- /src/bare-metal/useful-crates/zerocopy-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zerocopy-example" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | zerocopy = { version = "0.8.25", features = ["derive"] } 9 | -------------------------------------------------------------------------------- /src/borrowing.md: -------------------------------------------------------------------------------- 1 | # Borrowing 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/borrowing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "borrowing" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [lib] 8 | name = "borrowing" 9 | path = "../../third_party/rust-on-exercism/health-statistics.rs" 10 | -------------------------------------------------------------------------------- /src/borrowing/examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 3 3 | --- 4 | 5 | # Borrow Errors 6 | 7 | As a concrete example of how these borrowing rules prevent memory errors, 8 | consider the case of modifying a collection while there are references to its 9 | elements: 10 | 11 | ```rust,editable,compile_fail 12 | fn main() { 13 | let mut vec = vec![1, 2, 3, 4, 5]; 14 | let elem = &vec[2]; 15 | vec.push(6); 16 | dbg!(elem); 17 | } 18 | ``` 19 | 20 | Similarly, consider the case of iterator invalidation: 21 | 22 | ```rust,editable,compile_fail 23 | fn main() { 24 | let mut vec = vec![1, 2, 3, 4, 5]; 25 | for elem in &vec { 26 | vec.push(elem * 2); 27 | } 28 | } 29 | ``` 30 | 31 |
32 | 33 | - In both of these cases, modifying the collection by pushing new elements into 34 | it can potentially invalidate existing references to the collection's elements 35 | if the collection has to reallocate. 36 | 37 |
38 | -------------------------------------------------------------------------------- /src/borrowing/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 20 3 | --- 4 | 5 | # Exercise: Health Statistics 6 | 7 | {{#include ../../third_party/rust-on-exercism/health-statistics.md}} 8 | 9 | Copy the code below to and fill in the missing 10 | method: 11 | 12 | ```rust,editable 13 | {{#include ../../third_party/rust-on-exercism/health-statistics.rs:setup}} 14 | 15 | {{#include ../../third_party/rust-on-exercism/health-statistics.rs:User_visit_doctor}} 16 | todo!("Update a user's statistics based on measurements from a visit to the doctor") 17 | } 18 | } 19 | 20 | {{#include ../../third_party/rust-on-exercism/health-statistics.rs:tests}} 21 | ``` 22 | -------------------------------------------------------------------------------- /src/borrowing/interior-mutability.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 10 3 | --- 4 | 5 | # Interior Mutability 6 | 7 | In some situations, it's necessary to modify data behind a shared (read-only) 8 | reference. For example, a shared data structure might have an internal cache, 9 | and wish to update that cache from read-only methods. 10 | 11 | The "interior mutability" pattern allows exclusive (mutable) access behind a 12 | shared reference. The standard library provides several ways to do this, all 13 | while still ensuring safety, typically by performing a runtime check. 14 | 15 |
16 | 17 | The main thing to take away from this slide is that Rust provides _safe_ ways to 18 | modify data behind a shared reference. There are a variety of ways to ensure 19 | that safety, and the next sub-slides present a few of them. 20 | 21 |
22 | -------------------------------------------------------------------------------- /src/borrowing/interior-mutability/cell.md: -------------------------------------------------------------------------------- 1 | # `Cell` 2 | 3 | `Cell` wraps a value and allows getting or setting the value using only a shared 4 | reference to the `Cell`. However, it does not allow any references to the inner 5 | value. Since there are no references, borrowing rules cannot be broken. 6 | 7 | ```rust,editable 8 | use std::cell::Cell; 9 | 10 | fn main() { 11 | // Note that `cell` is NOT declared as mutable. 12 | let cell = Cell::new(5); 13 | 14 | cell.set(123); 15 | dbg!(cell.get()); 16 | } 17 | ``` 18 | 19 |
20 | 21 | - `Cell` is a simple means to ensure safety: it has a `set` method that takes 22 | `&self`. This needs no runtime check, but requires moving values, which can 23 | have its own cost. 24 | 25 |
26 | -------------------------------------------------------------------------------- /src/borrowing/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include ../../third_party/rust-on-exercism/health-statistics.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/chromium.md: -------------------------------------------------------------------------------- 1 | --- 2 | course: Chromium 3 | session: Chromium 4 | --- 5 | 6 | # Welcome to Rust in Chromium 7 | 8 | Rust is supported for third-party libraries in Chromium, with first-party glue 9 | code to connect between Rust and existing Chromium C++ code. 10 | 11 | > Today, we'll call into Rust to do something silly with strings. If you've got 12 | > a corner of the code where you're displaying a UTF8 string to the user, feel 13 | > free to follow this recipe in your part of the codebase instead of the exact 14 | > part we talk about. 15 | -------------------------------------------------------------------------------- /src/chromium/adding-third-party-crates/checking-in.md: -------------------------------------------------------------------------------- 1 | # Checking Crates into Chromium Source Code 2 | 3 | `git status` should reveal: 4 | 5 | - Crate code in `//third_party/rust/chromium_crates_io` 6 | - Metadata (`BUILD.gn` and `README.chromium`) in 7 | `//third_party/rust//` 8 | 9 | Please also add an `OWNERS` file in the latter location. 10 | 11 | You should land all this, along with your `Cargo.toml` and `gnrt_config.toml` 12 | changes, into the Chromium repo. 13 | 14 | **Important**: you need to use `git add -f` because otherwise `.gitignore` files 15 | may result in some files being skipped. 16 | 17 | As you do so, you might find presubmit checks fail because of non-inclusive 18 | language. This is because Rust crate data tends to include names of git 19 | branches, and many projects still use non-inclusive terminology there. So you 20 | may need to run: 21 | 22 | ```shell 23 | infra/update_inclusive_language_presubmit_exempt_dirs.sh > infra/inclusive_language_presubmit_exempt_dirs.txt 24 | git add -p infra/inclusive_language_presubmit_exempt_dirs.txt # add whatever changes are yours 25 | ``` 26 | -------------------------------------------------------------------------------- /src/chromium/adding-third-party-crates/configuring-cargo-toml.md: -------------------------------------------------------------------------------- 1 | # Configuring the `Cargo.toml` file to add crates 2 | 3 | Chromium has a single set of centrally-managed direct crate dependencies. These 4 | are managed through a single [`Cargo.toml`][0]: 5 | 6 | ```toml 7 | [dependencies] 8 | bitflags = "1" 9 | cfg-if = "1" 10 | cxx = "1" 11 | # lots more... 12 | ``` 13 | 14 | As with any other `Cargo.toml`, you can specify 15 | [more details about the dependencies][1] --- most commonly, you'll want to 16 | specify the `features` that you wish to enable in the crate. 17 | 18 | When adding a crate to Chromium, you'll often need to provide some extra 19 | information in an additional file, `gnrt_config.toml`, which we'll meet next. 20 | 21 | [0]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/rust/chromium_crates_io/Cargo.toml 22 | [1]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html 23 | -------------------------------------------------------------------------------- /src/chromium/adding-third-party-crates/configuring-gnrt-config-toml.md: -------------------------------------------------------------------------------- 1 | # Configuring `gnrt_config.toml` 2 | 3 | Alongside `Cargo.toml` is [`gnrt_config.toml`][0]. This contains 4 | Chromium-specific extensions to crate handling. 5 | 6 | If you add a new crate, you should specify at least the `group`. This is one of: 7 | 8 | ```toml 9 | # 'safe': The library satisfies the rule-of-2 and can be used in any process. 10 | # 'sandbox': The library does not satisfy the rule-of-2 and must be used in 11 | # a sandboxed process such as the renderer or a utility process. 12 | # 'test': The library is only used in tests. 13 | ``` 14 | 15 | For instance, 16 | 17 | ```toml 18 | [crate.my-new-crate] 19 | group = 'test' # only used in test code 20 | ``` 21 | 22 | Depending on the crate source code layout, you may also need to use this file to 23 | specify where its `LICENSE` file(s) can be found. 24 | 25 | Later, we'll see some other things you will need to configure in this file to 26 | resolve problems. 27 | 28 | [0]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/rust/chromium_crates_io/gnrt_config.toml 29 | -------------------------------------------------------------------------------- /src/chromium/adding-third-party-crates/depending-on-a-crate.md: -------------------------------------------------------------------------------- 1 | # Depending on a Crate 2 | 3 | Once you've added a third-party crate and generated build rules, depending on a 4 | crate is simple. Find your `rust_static_library` target, and add a `dep` on the 5 | `:lib` target within your crate. 6 | 7 | Specifically, 8 | 9 | ```bob 10 | +------------+ +----------------------+ 11 | "//third_party/rust" | crate name | "/v" | major semver version | ":lib" 12 | +------------+ +----------------------+ 13 | ``` 14 | 15 | For instance, 16 | 17 | ```gn 18 | rust_static_library("my_rust_lib") { 19 | crate_root = "lib.rs" 20 | sources = [ "lib.rs" ] 21 | deps = [ "//third_party/rust/example_rust_crate/v1:lib" ] 22 | } 23 | ``` 24 | -------------------------------------------------------------------------------- /src/chromium/adding-third-party-crates/downloading-crates.md: -------------------------------------------------------------------------------- 1 | # Downloading Crates 2 | 3 | A tool called `gnrt` knows how to download crates and how to generate `BUILD.gn` 4 | rules. 5 | 6 | To start, download the crate you want like this: 7 | 8 | ```shell 9 | cd chromium/src 10 | vpython3 tools/crates/run_gnrt.py -- vendor 11 | ``` 12 | 13 | > Although the `gnrt` tool is part of the Chromium source code, by running this 14 | > command you will be downloading and running its dependencies from `crates.io`. 15 | > See [the earlier section][0] discussing this security decision. 16 | 17 | This `vendor` command may download: 18 | 19 | - Your crate 20 | - Direct and transitive dependencies 21 | - New versions of other crates, as required by `cargo` to resolve the complete 22 | set of crates required by Chromium. 23 | 24 | Chromium maintains patches for some crates, kept in 25 | `//third_party/rust/chromium_crates_io/patches`. These will be reapplied 26 | automatically, but if patching fails you may need to take manual action. 27 | 28 | [0]: ../cargo.md 29 | -------------------------------------------------------------------------------- /src/chromium/adding-third-party-crates/generating-gn-build-rules.md: -------------------------------------------------------------------------------- 1 | # Generating `gn` Build Rules 2 | 3 | Once you've downloaded the crate, generate the `BUILD.gn` files like this: 4 | 5 | ```shell 6 | vpython3 tools/crates/run_gnrt.py -- gen 7 | ``` 8 | 9 | Now run `git status`. You should find: 10 | 11 | - At least one new crate source code in 12 | `third_party/rust/chromium_crates_io/vendor` 13 | - At least one new `BUILD.gn` in 14 | `third_party/rust//v` 15 | - An appropriate `README.chromium` 16 | 17 | The "major semver version" is a [Rust "semver" version number][0]. 18 | 19 | Take a close look, especially at the things generated in `third_party/rust`. 20 | 21 |
22 | 23 | Talk a little about semver --- and specifically the way that in Chromium it's to 24 | allow multiple incompatible versions of a crate, which is discouraged but 25 | sometimes necessary in the Cargo ecosystem. 26 | 27 | 28 | 29 | [0]: https://doc.rust-lang.org/cargo/reference/semver.html 30 | -------------------------------------------------------------------------------- /src/chromium/adding-third-party-crates/keeping-up-to-date.md: -------------------------------------------------------------------------------- 1 | # Keeping Crates Up to Date 2 | 3 | As the OWNER of any third party Chromium dependency, you are 4 | [expected to keep it up to date with any security fixes][0]. It is hoped that we 5 | will soon automate this for Rust crates, but for now, it's still your 6 | responsibility just as it is for any other third party dependency. 7 | 8 | [0]: https://chromium.googlesource.com/chromium/src/+/main/docs/adding_to_third_party.md#add-owners 9 | -------------------------------------------------------------------------------- /src/chromium/adding-third-party-crates/resolving-problems/build-scripts-which-generate-code.md: -------------------------------------------------------------------------------- 1 | # Build Scripts Which Generate Code 2 | 3 | If `ninja` complains about missing files, check the `build.rs` to see if it 4 | writes source code files. 5 | 6 | If so, modify [`gnrt_config.toml`][1] to add `build-script-outputs` to the 7 | crate. If this is a transitive dependency, that is, one on which Chromium code 8 | should not directly depend, also add `allow-first-party-usage=false`. There are 9 | several examples already in that file: 10 | 11 | ```toml 12 | [crate.unicode-linebreak] 13 | allow-first-party-usage = false 14 | build-script-outputs = ["tables.rs"] 15 | ``` 16 | 17 | Now rerun [`gnrt.py -- gen`][2] to regenerate `BUILD.gn` files to inform ninja 18 | that this particular output file is input to subsequent build steps. 19 | 20 | [1]: ../configuring-gnrt-config-toml.md 21 | [2]: ../generating-gn-build-rules.md 22 | -------------------------------------------------------------------------------- /src/chromium/adding-third-party-crates/resolving-problems/build-scripts-which-take-arbitrary-actions.md: -------------------------------------------------------------------------------- 1 | # Build Scripts Which Build C++ or Take Arbitrary Actions 2 | 3 | Some crates use the [`cc`][1] crate to build and link C/C++ libraries. Other 4 | crates parse C/C++ using [`bindgen`][2] within their build scripts. These 5 | actions can't be supported in a Chromium context --- our gn, ninja and LLVM 6 | build system is very specific in expressing relationships between build actions. 7 | 8 | So, your options are: 9 | 10 | - Avoid these crates 11 | - Apply a patch to the crate. 12 | 13 | Patches should be kept in 14 | `third_party/rust/chromium_crates_io/patches/` - see for example the 15 | [patches against the `cxx` crate][3] - and will be applied automatically by 16 | `gnrt` each time it upgrades the crate. 17 | 18 | [1]: https://crates.io/crates/cc 19 | [2]: https://crates.io/crates/bindgen 20 | [3]: https://source.chromium.org/chromium/chromium/src/+/main:third_party/rust/chromium_crates_io/patches/cxx/ 21 | -------------------------------------------------------------------------------- /src/chromium/build-rules/depending.md: -------------------------------------------------------------------------------- 1 | # Depending on Rust Code from Chromium C++ 2 | 3 | Simply add the above target to the `deps` of some Chromium C++ target. 4 | 5 | ```gn 6 | import("//build/rust/rust_static_library.gni") 7 | 8 | rust_static_library("my_rust_lib") { 9 | crate_root = "lib.rs" 10 | sources = [ "lib.rs" ] 11 | } 12 | 13 | # or source_set, static_library etc. 14 | component("preexisting_cpp") { 15 | deps = [ ":my_rust_lib" ] 16 | } 17 | ``` 18 | 19 |
20 | We'll see that this relationship only works if the Rust code exposes plain C APIs 21 | which can be called from C++, or if we use a C++/Rust interop tool. 22 |
23 | -------------------------------------------------------------------------------- /src/chromium/build-rules/unsafe.md: -------------------------------------------------------------------------------- 1 | # Including `unsafe` Rust Code 2 | 3 | Unsafe Rust code is forbidden in `rust_static_library` by default --- it won't 4 | compile. If you need unsafe Rust code, add `allow_unsafe = true` to the gn 5 | target. (Later in the course we'll see circumstances where this is necessary.) 6 | 7 | ```gn 8 | import("//build/rust/rust_static_library.gni") 9 | 10 | rust_static_library("my_rust_lib") { 11 | crate_root = "lib.rs" 12 | sources = [ 13 | "lib.rs", 14 | "hippopotamus.rs" 15 | ] 16 | allow_unsafe = true 17 | } 18 | ``` 19 | -------------------------------------------------------------------------------- /src/chromium/build-rules/vscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/comprehensive-rust/f3e369274a405262396a054220d74732be3a033b/src/chromium/build-rules/vscode.png -------------------------------------------------------------------------------- /src/chromium/interoperability-with-cpp/error-handling.md: -------------------------------------------------------------------------------- 1 | # CXX Error Handling 2 | 3 | CXX's [support for `Result`][0] relies on C++ exceptions, so we can't use 4 | that in Chromium. Alternatives: 5 | 6 | - The `T` part of `Result` can be: 7 | - Returned via out parameters (e.g. via `&mut T`). This requires that `T` can 8 | be passed across the FFI boundary - for example `T` has to be: 9 | - A primitive type (like `u32` or `usize`) 10 | - A type natively supported by `cxx` (like `UniquePtr`) that has a 11 | suitable default value to use in a failure case (_unlike_ `Box`). 12 | - Retained on the Rust side, and exposed via reference. This may be needed 13 | when `T` is a Rust type, which cannot be passed across the FFI boundary, and 14 | cannot be stored in `UniquePtr`. 15 | 16 | - The `E` part of `Result` can be: 17 | - Returned as a boolean (e.g. `true` representing success, and `false` 18 | representing failure) 19 | - Preserving error details is in theory possible, but so far hasn't been 20 | needed in practice. 21 | 22 | [0]: https://cxx.rs/binding/result.html 23 | -------------------------------------------------------------------------------- /src/chromium/interoperability-with-cpp/example-bindings.md: -------------------------------------------------------------------------------- 1 | # Example Bindings 2 | 3 | CXX requires that the whole C++/Rust boundary is declared in `cxx::bridge` 4 | modules inside `.rs` source code. 5 | 6 | ```rust,ignore 7 | {{#include ../../../third_party/cxx/book/snippets.rs:cxx_overview}} 8 | ``` 9 | 10 |
11 | 12 | Point out: 13 | 14 | - Although this looks like a regular Rust `mod`, the `#[cxx::bridge]` procedural 15 | macro does complex things to it. The generated code is quite a bit more 16 | sophisticated - though this does still result in a `mod` called `ffi` in your 17 | code. 18 | - Native support for C++'s `std::unique_ptr` in Rust 19 | - Native support for Rust slices in C++ 20 | - Calls from C++ to Rust, and Rust types (in the top part) 21 | - Calls from Rust to C++, and C++ types (in the bottom part) 22 | 23 | **Common misconception**: It _looks_ like a C++ header is being parsed by Rust, 24 | but this is misleading. This header is never interpreted by Rust, but simply 25 | `#include`d in the generated C++ code for the benefit of C++ compilers. 26 | 27 |
28 | -------------------------------------------------------------------------------- /src/chromium/setup.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | Make sure you can build and run Chromium. Any platform and set of build flags is 4 | OK, so long as your code is relatively recent (commit position 1223636 onwards, 5 | corresponding to November 2023): 6 | 7 | ```shell 8 | gn gen out/Debug 9 | autoninja -C out/Debug chrome 10 | out/Debug/chrome # or on Mac, out/Debug/Chromium.app/Contents/MacOS/Chromium 11 | ``` 12 | 13 | (A component, debug build is recommended for quickest iteration time. This is 14 | the default!) 15 | 16 | See 17 | [How to build Chromium](https://www.chromium.org/developers/how-tos/get-the-code/) 18 | if you aren't already at that point. Be warned: setting up to build Chromium 19 | takes time. 20 | 21 | It's also recommended that you have Visual Studio code installed. 22 | 23 | # About the exercises 24 | 25 | This part of the course has a series of exercises which build on each other. 26 | We'll be doing them spread throughout the course instead of just at the end. If 27 | you don't have time to complete a certain part, don't worry: you can catch up in 28 | the next slot. 29 | -------------------------------------------------------------------------------- /src/chromium/testing/build-gn.md: -------------------------------------------------------------------------------- 1 | # GN Rules for Rust Tests 2 | 3 | The simplest way to build Rust `gtest` tests is to add them to an existing test 4 | binary that already contains tests authored in C++. For example: 5 | 6 | ```gn 7 | test("ui_base_unittests") { 8 | ... 9 | sources += [ "my_rust_lib_unittest.rs" ] 10 | deps += [ ":my_rust_lib" ] 11 | } 12 | ``` 13 | 14 | Authoring Rust tests in a separate `static_library` also works, but requires 15 | manually declaring the dependency on the support libraries: 16 | 17 | ```gn 18 | rust_static_library("my_rust_lib_unittests") { 19 | testonly = true 20 | is_gtest_unittests = true 21 | crate_root = "my_rust_lib_unittest.rs" 22 | sources = [ "my_rust_lib_unittest.rs" ] 23 | deps = [ 24 | ":my_rust_lib", 25 | "//testing/rust_gtest_interop", 26 | ] 27 | } 28 | 29 | test("ui_base_unittests") { 30 | ... 31 | deps += [ ":my_rust_lib_unittests" ] 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /src/chromium/testing/rust-gtest-interop.md: -------------------------------------------------------------------------------- 1 | # `rust_gtest_interop` Library 2 | 3 | The [`rust_gtest_interop`][0] library provides a way to: 4 | 5 | - Use a Rust function as a `gtest` testcase (using the `#[gtest(...)]` 6 | attribute) 7 | - Use `expect_eq!` and similar macros (similar to `assert_eq!` but not panicking 8 | and not terminating the test when the assertion fails). 9 | 10 | Example: 11 | 12 | ```rust,ignore 13 | use rust_gtest_interop::prelude::*; 14 | 15 | #[gtest(MyRustTestSuite, MyAdditionTest)] 16 | fn test_addition() { 17 | expect_eq!(2 + 2, 4); 18 | } 19 | ``` 20 | 21 | [0]: https://chromium.googlesource.com/chromium/src/+/main/testing/rust_gtest_interop/README.md 22 | -------------------------------------------------------------------------------- /src/closures.md: -------------------------------------------------------------------------------- 1 | # Closures 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/closures/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 10 3 | --- 4 | 5 | # Exercise: Log Filter 6 | 7 | Building on the generic logger from this morning, implement a `Filter` which 8 | uses a closure to filter log messages, sending those which pass the filtering 9 | predicate to an inner logger. 10 | 11 | ```rust,compile_fail,editable 12 | {{#include exercise.rs:setup}} 13 | 14 | // TODO: Define and implement `Filter`. 15 | 16 | {{#include exercise.rs:main}} 17 | ``` 18 | -------------------------------------------------------------------------------- /src/closures/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/concurrency/async-control-flow.md: -------------------------------------------------------------------------------- 1 | # Channels and Control Flow 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/concurrency/async-exercises.md: -------------------------------------------------------------------------------- 1 | # Exercises 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/concurrency/async-exercises/afternoon.md: -------------------------------------------------------------------------------- 1 | # Exercises 2 | -------------------------------------------------------------------------------- /src/concurrency/async-exercises/chat-async/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chat-async" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | futures-util = { version = "0.3.31", features = ["sink"] } 8 | http = "1.3.1" 9 | tokio = { version = "1.45.1", features = ["full"] } 10 | tokio-websockets = { version = "0.11.4", features = ["client", "fastrand", "server", "sha1_smol"] } 11 | -------------------------------------------------------------------------------- /src/concurrency/async-exercises/solutions.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 20 3 | --- 4 | 5 | # Solutions 6 | 7 | ## Dining Philosophers --- Async 8 | 9 | ```rust,compile_fail 10 | {{#include dining-philosophers.rs:solution}} 11 | ``` 12 | 13 | ## Broadcast Chat Application 14 | 15 | _src/bin/server.rs_: 16 | 17 | ```rust,compile_fail 18 | {{#include chat-async/src/bin/server.rs:solution}} 19 | ``` 20 | 21 | _src/bin/client.rs_: 22 | 23 | ```rust,compile_fail 24 | {{#include chat-async/src/bin/client.rs:solution}} 25 | ``` 26 | -------------------------------------------------------------------------------- /src/concurrency/async-pitfalls.md: -------------------------------------------------------------------------------- 1 | # Pitfalls 2 | 3 | Async / await provides convenient and efficient abstraction for concurrent 4 | asynchronous programming. However, the async/await model in Rust also comes with 5 | its share of pitfalls and footguns. We illustrate some of them in this chapter. 6 | 7 | {{%segment outline}} 8 | -------------------------------------------------------------------------------- /src/concurrency/async.md: -------------------------------------------------------------------------------- 1 | # Async Basics 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/concurrency/channels.md: -------------------------------------------------------------------------------- 1 | # Channels 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/concurrency/send-sync.md: -------------------------------------------------------------------------------- 1 | # `Send` and `Sync` 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/concurrency/send-sync/marker-traits.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 2 3 | --- 4 | 5 | # Marker Traits 6 | 7 | How does Rust know to forbid shared access across threads? The answer is in two 8 | traits: 9 | 10 | - [`Send`][1]: a type `T` is `Send` if it is safe to move a `T` across a thread 11 | boundary. 12 | - [`Sync`][2]: a type `T` is `Sync` if it is safe to move a `&T` across a thread 13 | boundary. 14 | 15 | `Send` and `Sync` are [unsafe traits][3]. The compiler will automatically derive 16 | them for your types as long as they only contain `Send` and `Sync` types. You 17 | can also implement them manually when you know it is valid. 18 | 19 | [1]: https://doc.rust-lang.org/std/marker/trait.Send.html 20 | [2]: https://doc.rust-lang.org/std/marker/trait.Sync.html 21 | [3]: ../../unsafe-rust/unsafe-traits.md 22 | 23 |
24 | 25 | - One can think of these traits as markers that the type has certain 26 | thread-safety properties. 27 | - They can be used in the generic constraints as normal traits. 28 | 29 |
30 | -------------------------------------------------------------------------------- /src/concurrency/send-sync/send.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 2 3 | --- 4 | 5 | # `Send` 6 | 7 | > A type `T` is [`Send`][1] if it is safe to move a `T` value to another thread. 8 | 9 | The effect of moving ownership to another thread is that _destructors_ will run 10 | in that thread. So the question is when you can allocate a value in one thread 11 | and deallocate it in another. 12 | 13 | [1]: https://doc.rust-lang.org/std/marker/trait.Send.html 14 | 15 |
16 | 17 | As an example, a connection to the SQLite library must only be accessed from a 18 | single thread. 19 | 20 |
21 | -------------------------------------------------------------------------------- /src/concurrency/send-sync/sync.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 2 3 | --- 4 | 5 | # `Sync` 6 | 7 | > A type `T` is [`Sync`][1] if it is safe to access a `T` value from multiple 8 | > threads at the same time. 9 | 10 | More precisely, the definition is: 11 | 12 | > `T` is `Sync` if and only if `&T` is `Send` 13 | 14 | [1]: https://doc.rust-lang.org/std/marker/trait.Sync.html 15 | 16 |
17 | 18 | This statement is essentially a shorthand way of saying that if a type is 19 | thread-safe for shared use, it is also thread-safe to pass references of it 20 | across threads. 21 | 22 | This is because if a type is Sync it means that it can be shared across multiple 23 | threads without the risk of data races or other synchronization issues, so it is 24 | safe to move it to another thread. A reference to the type is also safe to move 25 | to another thread, because the data it references can be accessed from any 26 | thread safely. 27 | 28 |
29 | -------------------------------------------------------------------------------- /src/concurrency/shared-state.md: -------------------------------------------------------------------------------- 1 | # Shared State 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/concurrency/sync-exercises.md: -------------------------------------------------------------------------------- 1 | # Exercises 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/concurrency/sync-exercises/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sync-exercises" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "dining-philosophers" 9 | path = "dining-philosophers.rs" 10 | 11 | [[bin]] 12 | name = "link-checker" 13 | path = "link-checker.rs" 14 | 15 | [dependencies] 16 | reqwest = { version = "0.12.18", features = ["blocking"] } 17 | scraper = "0.23.1" 18 | thiserror = "2.0.12" 19 | 20 | [dev-dependencies] 21 | tempfile = "3.20.0" 22 | -------------------------------------------------------------------------------- /src/concurrency/sync-exercises/solutions.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 30 3 | --- 4 | 5 | # Solutions 6 | 7 | ## Dining Philosophers 8 | 9 | ```rust 10 | {{#include dining-philosophers.rs:solution}} 11 | ``` 12 | 13 | ## Link Checker 14 | 15 | ```rust,compile_fail 16 | {{#include link-checker.rs:solution}} 17 | ``` 18 | -------------------------------------------------------------------------------- /src/concurrency/threads.md: -------------------------------------------------------------------------------- 1 | # Threads 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/concurrency/threads/scoped.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 13 3 | --- 4 | 5 | # Scoped Threads 6 | 7 | Normal threads cannot borrow from their environment: 8 | 9 | ```rust,editable,compile_fail 10 | use std::thread; 11 | 12 | fn foo() { 13 | let s = String::from("Hello"); 14 | thread::spawn(|| { 15 | dbg!(s.len()); 16 | }); 17 | } 18 | 19 | fn main() { 20 | foo(); 21 | } 22 | ``` 23 | 24 | However, you can use a [scoped thread][1] for this: 25 | 26 | ```rust,editable 27 | use std::thread; 28 | 29 | fn foo() { 30 | let s = String::from("Hello"); 31 | thread::scope(|scope| { 32 | scope.spawn(|| { 33 | dbg!(s.len()); 34 | }); 35 | }); 36 | } 37 | 38 | fn main() { 39 | foo(); 40 | } 41 | ``` 42 | 43 | [1]: https://doc.rust-lang.org/std/thread/fn.scope.html 44 | 45 |
46 | 47 | - The reason for that is that when the `thread::scope` function completes, all 48 | the threads are guaranteed to be joined, so they can return borrowed data. 49 | - Normal Rust borrowing rules apply: you can either borrow mutably by one 50 | thread, or immutably by any number of threads. 51 | 52 |
53 | -------------------------------------------------------------------------------- /src/concurrency/welcome.md: -------------------------------------------------------------------------------- 1 | --- 2 | course: Concurrency 3 | session: Morning 4 | target_minutes: 180 5 | --- 6 | 7 | # Welcome to Concurrency in Rust 8 | 9 | Rust has full support for concurrency using OS threads with mutexes and 10 | channels. 11 | 12 | The Rust type system plays an important role in making many concurrency bugs 13 | compile time bugs. This is often referred to as _fearless concurrency_ since you 14 | can rely on the compiler to ensure correctness at runtime. 15 | 16 | ## Schedule 17 | 18 | {{%session outline}} 19 | 20 |
21 | 22 | - Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc. 23 | - The type system gives us safety for concurrency without any special features. 24 | - The same tools that help with "concurrent" access in a single thread (e.g., a 25 | called function that might mutate an argument or save references to it to read 26 | later) save us from multi-threading issues. 27 | 28 |
29 | -------------------------------------------------------------------------------- /src/control-flow-basics.md: -------------------------------------------------------------------------------- 1 | # Control Flow Basics 2 | 3 | {{%segment outline}} 4 | 5 |
6 | 7 | - We will now cover the many kinds of flow control found in Rust. 8 | 9 | - Most of this will be very familiar to what you have seen in other programming 10 | languages. 11 | 12 |
13 | -------------------------------------------------------------------------------- /src/control-flow-basics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "control-flow-basics" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "collatz" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/control-flow-basics/blocks-and-scopes.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 5 3 | --- 4 | 5 | # Blocks and Scopes 6 | 7 | A block in Rust contains a sequence of expressions, enclosed by braces `{}`. 8 | Each block has a value and a type, which are those of the last expression of the 9 | block: 10 | 11 | ```rust,editable 12 | fn main() { 13 | let z = 13; 14 | let x = { 15 | let y = 10; 16 | dbg!(y); 17 | z - y 18 | }; 19 | dbg!(x); 20 | // dbg!(y); 21 | } 22 | ``` 23 | 24 | If the last expression ends with `;`, then the resulting value and type is `()`. 25 | 26 | A variable's scope is limited to the enclosing block. 27 | 28 |
29 | 30 | - You can explain that dbg! is a Rust macro that prints and returns the value of 31 | a given expression for quick and dirty debugging. 32 | 33 | - You can show how the value of the block changes by changing the last line in 34 | the block. For instance, adding/removing a semicolon or using a `return`. 35 | 36 | - Demonstrate that attempting to access `y` outside of its scope won't compile. 37 | 38 | - Values are effectively "deallocated" when they go out of their scope, even if 39 | their data on the stack is still there. 40 | 41 |
42 | -------------------------------------------------------------------------------- /src/control-flow-basics/break-continue.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 4 3 | --- 4 | 5 | # `break` and `continue` 6 | 7 | If you want to immediately start the next iteration use 8 | [`continue`](https://doc.rust-lang.org/reference/expressions/loop-expr.html#continue-expressions). 9 | 10 | If you want to exit any kind of loop early, use 11 | [`break`](https://doc.rust-lang.org/reference/expressions/loop-expr.html#break-expressions). 12 | With `loop`, this can take an optional expression that becomes the value of the 13 | `loop` expression. 14 | 15 | ```rust,editable 16 | fn main() { 17 | let mut i = 0; 18 | loop { 19 | i += 1; 20 | if i > 5 { 21 | break; 22 | } 23 | if i % 2 == 0 { 24 | continue; 25 | } 26 | dbg!(i); 27 | } 28 | } 29 | ``` 30 | 31 |
32 | 33 | Note that `loop` is the only looping construct which can return a non-trivial 34 | value. This is because it's guaranteed to only return at a `break` statement 35 | (unlike `while` and `for` loops, which can also return when the condition 36 | fails). 37 | 38 |
39 | -------------------------------------------------------------------------------- /src/control-flow-basics/break-continue/labels.md: -------------------------------------------------------------------------------- 1 | # Labels 2 | 3 | Both `continue` and `break` can optionally take a label argument which is used 4 | to break out of nested loops: 5 | 6 | ```rust,editable 7 | fn main() { 8 | let s = [[5, 6, 7], [8, 9, 10], [21, 15, 32]]; 9 | let mut elements_searched = 0; 10 | let target_value = 10; 11 | 'outer: for i in 0..=2 { 12 | for j in 0..=2 { 13 | elements_searched += 1; 14 | if s[i][j] == target_value { 15 | break 'outer; 16 | } 17 | } 18 | } 19 | dbg!(elements_searched); 20 | } 21 | ``` 22 | 23 |
24 | 25 | - Labeled break also works on arbitrary blocks, e.g. 26 | ```rust 27 | 'label: { 28 | break 'label; 29 | println!("This line gets skipped"); 30 | } 31 | ``` 32 | 33 |
34 | -------------------------------------------------------------------------------- /src/control-flow-basics/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 15 3 | --- 4 | 5 | # Exercise: Collatz Sequence 6 | 7 | The [Collatz Sequence](https://en.wikipedia.org/wiki/Collatz_conjecture) is 8 | defined as follows, for an arbitrary n1 greater than zero: 9 | 10 | - If _ni_ is 1, then the sequence terminates at _ni_. 11 | - If _ni_ is even, then _ni+1 = ni / 2_. 12 | - If _ni_ is odd, then _ni+1 = 3 * ni + 1_. 13 | 14 | For example, beginning with _n1_ = 3: 15 | 16 | - 3 is odd, so _n2_ = 3 * 3 + 1 = 10; 17 | - 10 is even, so _n3_ = 10 / 2 = 5; 18 | - 5 is odd, so _n4_ = 3 * 5 + 1 = 16; 19 | - 16 is even, so _n5_ = 16 / 2 = 8; 20 | - 8 is even, so _n6_ = 8 / 2 = 4; 21 | - 4 is even, so _n7_ = 4 / 2 = 2; 22 | - 2 is even, so _n8_ = 1; and 23 | - the sequence terminates. 24 | 25 | Write a function to calculate the length of the collatz sequence for a given 26 | initial `n`. 27 | 28 | ```rust,editable,should_panic 29 | {{#include exercise.rs:collatz_length}} 30 | todo!("Implement this") 31 | } 32 | 33 | {{#include exercise.rs:main}} 34 | ``` 35 | -------------------------------------------------------------------------------- /src/control-flow-basics/loops.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 5 3 | --- 4 | 5 | # Loops 6 | 7 | There are three looping keywords in Rust: `while`, `loop`, and `for`: 8 | 9 | ## `while` 10 | 11 | The 12 | [`while` keyword](https://doc.rust-lang.org/reference/expressions/loop-expr.html#predicate-loops) 13 | works much like in other languages, executing the loop body as long as the 14 | condition is true. 15 | 16 | ```rust,editable 17 | fn main() { 18 | let mut x = 200; 19 | while x >= 10 { 20 | x = x / 2; 21 | } 22 | dbg!(x); 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /src/control-flow-basics/loops/for.md: -------------------------------------------------------------------------------- 1 | # `for` 2 | 3 | The [`for` loop](https://doc.rust-lang.org/std/keyword.for.html) iterates over 4 | ranges of values or the items in a collection: 5 | 6 | ```rust,editable 7 | fn main() { 8 | for x in 1..5 { 9 | dbg!(x); 10 | } 11 | 12 | for elem in [2, 4, 8, 16, 32] { 13 | dbg!(elem); 14 | } 15 | } 16 | ``` 17 | 18 |
19 | 20 | - Under the hood `for` loops use a concept called "iterators" to handle 21 | iterating over different kinds of ranges/collections. Iterators will be 22 | discussed in more detail later. 23 | - Note that the first `for` loop only iterates to `4`. Show the `1..=5` syntax 24 | for an inclusive range. 25 | 26 |
27 | -------------------------------------------------------------------------------- /src/control-flow-basics/loops/loop.md: -------------------------------------------------------------------------------- 1 | # `loop` 2 | 3 | The [`loop` statement](https://doc.rust-lang.org/std/keyword.loop.html) just 4 | loops forever, until a `break`. 5 | 6 | ```rust,editable 7 | fn main() { 8 | let mut i = 0; 9 | loop { 10 | i += 1; 11 | dbg!(i); 12 | if i > 100 { 13 | break; 14 | } 15 | } 16 | } 17 | ``` 18 | 19 |
20 | 21 | - The `loop` statement works like a `while true` loop. Use it for things like 22 | servers which will serve connections forever. 23 | 24 |
25 | -------------------------------------------------------------------------------- /src/control-flow-basics/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/credits.md: -------------------------------------------------------------------------------- 1 | # Credits 2 | 3 | The material here builds on top of the many great sources of Rust documentation. 4 | See the page on [other resources](other-resources.md) for a full list of useful 5 | resources. 6 | 7 | The material of Comprehensive Rust is licensed under the terms of the Apache 2.0 8 | license, please see 9 | [`LICENSE`](https://github.com/google/comprehensive-rust/blob/main/LICENSE) for 10 | details. 11 | 12 | ## Rust by Example 13 | 14 | Some examples and exercises have been copied and adapted from 15 | [Rust by Example](https://doc.rust-lang.org/rust-by-example/). Please see the 16 | `third_party/rust-by-example/` directory for details, including the license 17 | terms. 18 | 19 | ## Rust on Exercism 20 | 21 | Some exercises have been copied and adapted from 22 | [Rust on Exercism](https://exercism.org/tracks/rust). Please see the 23 | `third_party/rust-on-exercism/` directory for details, including the license 24 | terms. 25 | 26 | ## CXX 27 | 28 | The [Interoperability with C++](android/interoperability/cpp.md) section uses an 29 | image from [CXX](https://cxx.rs/). Please see the `third_party/cxx/` directory 30 | for details, including the license terms. 31 | -------------------------------------------------------------------------------- /src/error-handling.md: -------------------------------------------------------------------------------- 1 | # Error Handling 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/error-handling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "error-handling" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = "*" 9 | thiserror = "*" 10 | 11 | [lib] 12 | name = "parser" 13 | path = "exercise.rs" 14 | -------------------------------------------------------------------------------- /src/error-handling/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 20 3 | --- 4 | 5 | # Exercise: Rewriting with Result 6 | 7 | In this exercise we're revisiting the expression evaluator exercise that we did 8 | in day 2. Our initial solution ignores a possible error case: Dividing by zero! 9 | Rewrite `eval` to instead use idiomatic error handling to handle this error case 10 | and return an error when it occurs. We provide a simple `DivideByZeroError` type 11 | to use as the error type for `eval`. 12 | 13 | ```rust,editable 14 | {{#include exercise.rs:types}} 15 | 16 | {{#include exercise.rs:eval}} 17 | 18 | {{#include exercise.rs:tests}} 19 | ``` 20 | 21 |
22 | 23 | - The starting code here isn't exactly the same as the previous exercise's 24 | solution: We've added in an explicit panic to show students where the error 25 | case is. Point this out if students get confused. 26 | 27 |
28 | -------------------------------------------------------------------------------- /src/error-handling/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:types}} 5 | 6 | {{#include exercise.rs:solution}} 7 | 8 | {{#include exercise.rs:tests}} 9 | ``` 10 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/afternoon.md: -------------------------------------------------------------------------------- 1 | # Exercises 2 | 3 | We will write a driver for the PL031 real-time clock device. 4 | 5 |
6 | 7 | After looking at the exercises, you can look at the [solutions] provided. 8 | 9 | [solutions]: solutions-afternoon.md 10 | 11 |
12 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/compass/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "thumbv7em-none-eabihf" # Cortex-M4F 3 | 4 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] 5 | rustflags = ["-C", "link-arg=-Tlink.x"] 6 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/compass/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "compass" 5 | version = "0.1.0" 6 | edition = "2024" 7 | publish = false 8 | 9 | [dependencies] 10 | cortex-m-rt = "0.7.5" 11 | embedded-hal = "1.0.0" 12 | lsm303agr = "1.1.0" 13 | microbit-v2 = "0.15.1" 14 | panic-halt = "1.0.0" 15 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/compass/Embed.toml: -------------------------------------------------------------------------------- 1 | [default.general] 2 | chip = "nrf52833_xxAA" 3 | 4 | [debug.gdb] 5 | enabled = true 6 | 7 | [debug.reset] 8 | halt_afterwards = true 9 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/morning.md: -------------------------------------------------------------------------------- 1 | # Exercises 2 | 3 | We will read the direction from an I2C compass, and log the readings to a serial 4 | port. 5 | 6 |
7 | 8 | After looking at the exercises, you can look at the [solutions] provided. 9 | 10 | [solutions]: solutions-morning.md 11 | 12 |
13 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/rtc/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "aarch64-unknown-none" 3 | rustflags = ["-C", "link-arg=-Timage.ld"] 4 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/rtc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | [package] 4 | name = "rtc" 5 | version = "0.1.0" 6 | edition = "2024" 7 | publish = false 8 | 9 | [dependencies] 10 | aarch64-paging = { version = "0.9.1", default-features = false } 11 | aarch64-rt = "0.1.3" 12 | arm-gic = "0.4.0" 13 | arm-pl011-uart = "0.3.1" 14 | bitflags = "2.9.1" 15 | chrono = { version = "0.4.41", default-features = false } 16 | log = "0.4.27" 17 | smccc = "0.2.1" 18 | spin = "0.10.0" 19 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/rtc/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | .PHONY: build qemu_minimal qemu qemu_logger 16 | 17 | all: rtc.bin 18 | 19 | build: 20 | cargo build 21 | 22 | rtc.bin: build 23 | cargo objcopy -- -O binary $@ 24 | 25 | qemu: rtc.bin 26 | qemu-system-aarch64 -machine virt,gic-version=3 -cpu max -serial mon:stdio -display none -kernel $< -s 27 | 28 | clean: 29 | cargo clean 30 | rm -f *.bin 31 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/solutions-afternoon.md: -------------------------------------------------------------------------------- 1 | # Bare Metal Rust Afternoon 2 | 3 | ## RTC driver 4 | 5 | ([back to exercise](rtc.md)) 6 | 7 | _main.rs_: 8 | 9 | ```rust,compile_fail 10 | {{#include rtc/src/main.rs:solution}} 11 | ``` 12 | 13 | _pl031.rs_: 14 | 15 | ```rust 16 | {{#include rtc/src/pl031.rs:solution}} 17 | ``` 18 | -------------------------------------------------------------------------------- /src/exercises/bare-metal/solutions-morning.md: -------------------------------------------------------------------------------- 1 | # Bare Metal Rust Morning Exercise 2 | 3 | ## Compass 4 | 5 | ([back to exercise](compass.md)) 6 | 7 | ```rust,compile_fail 8 | {{#include compass/src/main.rs:solution}} 9 | ``` 10 | -------------------------------------------------------------------------------- /src/exercises/chromium/chwomium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/comprehensive-rust/f3e369274a405262396a054220d74732be3a033b/src/exercises/chromium/chwomium.png -------------------------------------------------------------------------------- /src/exercises/chromium/solutions.md: -------------------------------------------------------------------------------- 1 | # Exercise Solutions 2 | 3 | Solutions to the Chromium exercises can be found in [this series of CLs][0]. 4 | 5 | [0]: https://chromium-review.googlesource.com/c/chromium/src/+/5096560 6 | -------------------------------------------------------------------------------- /src/exercises/chromium/testing.md: -------------------------------------------------------------------------------- 1 | # Testing exercise 2 | 3 | Time for another exercise! 4 | 5 | In your Chromium build: 6 | 7 | - Add a testable function next to `hello_from_rust`. Some suggestions: adding 8 | two integers received as arguments, computing the nth Fibonacci number, 9 | summing integers in a slice, etc. 10 | - Add a separate `..._unittest.rs` file with a test for the new function. 11 | - Add the new tests to `BUILD.gn`. 12 | - Build the tests, run them, and verify that the new test works. 13 | -------------------------------------------------------------------------------- /src/exercises/chromium/third-party.md: -------------------------------------------------------------------------------- 1 | # Exercise 2 | 3 | Add [uwuify][0] to Chromium, turning off the crate's [default features][1]. 4 | Assume that the crate will be used in shipping Chromium, but won't be used to 5 | handle untrustworthy input. 6 | 7 | (In the next exercise we'll use uwuify from Chromium, but feel free to skip 8 | ahead and do that now if you like. Or, you could create a new 9 | [`rust_executable` target][2] which uses `uwuify`). 10 | 11 |
12 | 13 | Students will need to download lots of transitive dependencies. 14 | 15 | The total crates needed are: 16 | 17 | - `instant`, 18 | - `lock_api`, 19 | - `parking_lot`, 20 | - `parking_lot_core`, 21 | - `redox_syscall`, 22 | - `scopeguard`, 23 | - `smallvec`, and 24 | - `uwuify`. 25 | 26 | If students are downloading even more than that, they probably forgot to turn 27 | off the default features. 28 | 29 | Thanks to [Daniel Liu][3] for this crate! 30 | 31 |
32 | 33 | [0]: https://crates.io/crates/uwuify 34 | [1]: https://doc.rust-lang.org/cargo/reference/features.html#the-default-feature 35 | [2]: https://source.chromium.org/chromium/chromium/src/+/main:build/rust/rust_executable.gni 36 | [3]: https://github.com/Daniel-Liu-c0deb0t 37 | -------------------------------------------------------------------------------- /src/generics.md: -------------------------------------------------------------------------------- 1 | # Generics 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/generics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "generics" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [lib] 8 | name = "generics" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/generics/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 10 3 | --- 4 | 5 | # Exercise: Generic `min` 6 | 7 | In this short exercise, you will implement a generic `min` function that 8 | determines the minimum of two values, using the [`Ord`] trait. 9 | 10 | ```rust,editable 11 | use std::cmp::Ordering; 12 | 13 | // TODO: implement the `min` function used in the tests. 14 | 15 | {{#include exercise.rs:tests}} 16 | ``` 17 | 18 |
19 | 20 | - Show students the [`Ord`] trait and [`Ordering`] enum. 21 | 22 |
23 | 24 | [`Ord`]: https://doc.rust-lang.org/stable/std/cmp/trait.Ord.html 25 | [`Ordering`]: https://doc.rust-lang.org/stable/std/cmp/enum.Ordering.html 26 | -------------------------------------------------------------------------------- /src/generics/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/hello-world.md: -------------------------------------------------------------------------------- 1 | # Hello, World 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/hello-world/playground.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 2 3 | --- 4 | 5 | # Playground 6 | 7 | The [Rust Playground](https://play.rust-lang.org/) provides an easy way to run 8 | short Rust programs, and is the basis for the examples and exercises in this 9 | course. Try running the "hello-world" program it starts with. It comes with a 10 | few handy features: 11 | 12 | - Under "Tools", use the `rustfmt` option to format your code in the "standard" 13 | way. 14 | 15 | - Rust has two main "profiles" for generating code: Debug (extra runtime checks, 16 | less optimization) and Release (fewer runtime checks, lots of optimization). 17 | These are accessible under "Debug" at the top. 18 | 19 | - If you're interested, use "ASM" under "..." to see the generated assembly 20 | code. 21 | 22 |
23 | 24 | As students head into the break, encourage them to open up the playground and 25 | experiment a little. Encourage them to keep the tab open and try things out 26 | during the rest of the course. This is particularly helpful for advanced 27 | students who want to know more about Rust's optimizations or generated assembly. 28 | 29 |
30 | -------------------------------------------------------------------------------- /src/hello-world/what-is-rust.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 10 3 | --- 4 | 5 | # What is Rust? 6 | 7 | Rust is a new programming language which had its [1.0 release in 2015][1]: 8 | 9 | - Rust is a statically compiled language in a similar role as C++ 10 | - `rustc` uses LLVM as its backend. 11 | - Rust supports many 12 | [platforms and architectures](https://doc.rust-lang.org/nightly/rustc/platform-support.html): 13 | - x86, ARM, WebAssembly, ... 14 | - Linux, Mac, Windows, ... 15 | - Rust is used for a wide range of devices: 16 | - firmware and boot loaders, 17 | - smart displays, 18 | - mobile phones, 19 | - desktops, 20 | - servers. 21 | 22 |
23 | 24 | Rust fits in the same area as C++: 25 | 26 | - High flexibility. 27 | - High level of control. 28 | - Can be scaled down to very constrained devices such as microcontrollers. 29 | - Has no runtime or garbage collection. 30 | - Focuses on reliability and safety without sacrificing performance. 31 | 32 |
33 | 34 | [1]: https://blog.rust-lang.org/2015/05/15/Rust-1.0.html 35 | -------------------------------------------------------------------------------- /src/iterators.md: -------------------------------------------------------------------------------- 1 | # Iterators 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/iterators/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iterators" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [lib] 8 | name = "offset_differences" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/iterators/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 30 3 | --- 4 | 5 | # Exercise: Iterator Method Chaining 6 | 7 | In this exercise, you will need to find and use some of the provided methods in 8 | the [`Iterator`][1] trait to implement a complex calculation. 9 | 10 | Copy the following code to and make the tests 11 | pass. Use an iterator expression and `collect` the result to construct the 12 | return value. 13 | 14 | ```rust,editable 15 | {{#include exercise.rs:offset_differences}} 16 | todo!() 17 | } 18 | 19 | {{#include exercise.rs:unit-tests}} 20 | ``` 21 | 22 | [1]: https://doc.rust-lang.org/std/iter/trait.Iterator.html 23 | -------------------------------------------------------------------------------- /src/iterators/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/lifetimes.md: -------------------------------------------------------------------------------- 1 | # Lifetimes 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/lifetimes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lifetimes" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | thiserror = "2.0.11" 9 | 10 | [lib] 11 | name = "protobuf" 12 | path = "exercise.rs" 13 | -------------------------------------------------------------------------------- /src/lifetimes/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/memory-management.md: -------------------------------------------------------------------------------- 1 | # Memory Management 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/memory-management/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "memory-management" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "package-builder" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/memory-management/clone.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 2 3 | --- 4 | 5 | # Clone 6 | 7 | Sometimes you _want_ to make a copy of a value. The `Clone` trait accomplishes 8 | this. 9 | 10 | ```rust,editable 11 | fn say_hello(name: String) { 12 | println!("Hello {name}") 13 | } 14 | 15 | fn main() { 16 | let name = String::from("Alice"); 17 | say_hello(name.clone()); 18 | say_hello(name); 19 | } 20 | ``` 21 | 22 |
23 | 24 | - The idea of `Clone` is to make it easy to spot where heap allocations are 25 | occurring. Look for `.clone()` and a few others like `vec!` or `Box::new`. 26 | 27 | - It's common to "clone your way out" of problems with the borrow checker, and 28 | return later to try to optimize those clones away. 29 | 30 | - `clone` generally performs a deep copy of the value, meaning that if you e.g. 31 | clone an array, all of the elements of the array are cloned as well. 32 | 33 | - The behavior for `clone` is user-defined, so it can perform custom cloning 34 | logic if needed. 35 | 36 |
37 | -------------------------------------------------------------------------------- /src/memory-management/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 20 3 | --- 4 | 5 | # Exercise: Builder Type 6 | 7 | In this example, we will implement a complex data type that owns all of its 8 | data. We will use the "builder pattern" to support building a new value 9 | piece-by-piece, using convenience functions. 10 | 11 | Fill in the missing pieces. 12 | 13 | ```rust,should_panic,editable 14 | {{#include exercise.rs:Package}} 15 | {{#include exercise.rs:as_dependency}} 16 | todo!("1") 17 | } 18 | } 19 | 20 | {{#include exercise.rs:PackageBuilder}} 21 | {{#include exercise.rs:new}} 22 | todo!("2") 23 | } 24 | 25 | {{#include exercise.rs:version}} 26 | 27 | {{#include exercise.rs:authors}} 28 | todo!("3") 29 | } 30 | 31 | {{#include exercise.rs:dependency}} 32 | todo!("4") 33 | } 34 | 35 | {{#include exercise.rs:language}} 36 | todo!("5") 37 | } 38 | 39 | {{#include exercise.rs:build}} 40 | 41 | {{#include exercise.rs:main}} 42 | ``` 43 | -------------------------------------------------------------------------------- /src/memory-management/ownership.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 5 3 | --- 4 | 5 | # Ownership 6 | 7 | All variable bindings have a _scope_ where they are valid and it is an error to 8 | use a variable outside its scope: 9 | 10 | 11 | 12 | ```rust,editable,compile_fail 13 | struct Point(i32, i32); 14 | 15 | fn main() { 16 | { 17 | let p = Point(3, 4); 18 | dbg!(p.0); 19 | } 20 | dbg!(p.1); 21 | } 22 | ``` 23 | 24 | We say that the variable _owns_ the value. Every Rust value has precisely one 25 | owner at all times. 26 | 27 | At the end of the scope, the variable is _dropped_ and the data is freed. A 28 | destructor can run here to free up resources. 29 | 30 |
31 | 32 | Students familiar with garbage-collection implementations will know that a 33 | garbage collector starts with a set of "roots" to find all reachable memory. 34 | Rust's "single owner" principle is a similar idea. 35 | 36 |
37 | -------------------------------------------------------------------------------- /src/memory-management/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/methods-and-traits.md: -------------------------------------------------------------------------------- 1 | # Methods and Traits 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/methods-and-traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "methods-and-traits" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "methods-and-traits" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/methods-and-traits/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 15 3 | --- 4 | 5 | # Exercise: Logger Trait 6 | 7 | Let's design a simple logging utility, using a trait `Logger` with a `log` 8 | method. Code which might log its progress can then take an `&impl Logger`. In 9 | testing, this might put messages in the test logfile, while in a production 10 | build it would send messages to a log server. 11 | 12 | However, the `StderrLogger` given below logs all messages, regardless of 13 | verbosity. Your task is to write a `VerbosityFilter` type that will ignore 14 | messages above a maximum verbosity. 15 | 16 | This is a common pattern: a struct wrapping a trait implementation and 17 | implementing that same trait, adding behavior in the process. In the "Generics" 18 | segment this afternoon, we will see how to make the wrapper generic over the 19 | wrapped type. 20 | 21 | ```rust,compile_fail,editable 22 | {{#include exercise.rs:setup}} 23 | 24 | // TODO: Implement the `Logger` trait for `VerbosityFilter`. 25 | 26 | {{#include exercise.rs:main}} 27 | ``` 28 | -------------------------------------------------------------------------------- /src/methods-and-traits/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/methods-and-traits/traits.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 15 3 | --- 4 | 5 | # Traits 6 | 7 | Rust lets you abstract over types with traits. They're similar to interfaces: 8 | 9 | ```rust,editable 10 | trait Pet { 11 | /// Return a sentence from this pet. 12 | fn talk(&self) -> String; 13 | 14 | /// Print a string to the terminal greeting this pet. 15 | fn greet(&self); 16 | } 17 | ``` 18 | 19 |
20 | 21 | - A trait defines a number of methods that types must have in order to implement 22 | the trait. 23 | 24 | - In the "Generics" segment, next, we will see how to build functionality that 25 | is generic over all types implementing a trait. 26 | 27 |
28 | -------------------------------------------------------------------------------- /src/methods-and-traits/traits/associated-types.md: -------------------------------------------------------------------------------- 1 | # Associated Types 2 | 3 | Associated types are placeholder types which are supplied by the trait 4 | implementation. 5 | 6 | ```rust,editable 7 | #[derive(Debug)] 8 | struct Meters(i32); 9 | #[derive(Debug)] 10 | struct MetersSquared(i32); 11 | 12 | trait Multiply { 13 | type Output; 14 | fn multiply(&self, other: &Self) -> Self::Output; 15 | } 16 | 17 | impl Multiply for Meters { 18 | type Output = MetersSquared; 19 | fn multiply(&self, other: &Self) -> Self::Output { 20 | MetersSquared(self.0 * other.0) 21 | } 22 | } 23 | 24 | fn main() { 25 | println!("{:?}", Meters(10).multiply(&Meters(20))); 26 | } 27 | ``` 28 | 29 |
30 | 31 | - Associated types are sometimes also called "output types". The key observation 32 | is that the implementer, not the caller, chooses this type. 33 | 34 | - Many standard library traits have associated types, including arithmetic 35 | operators and `Iterator`. 36 | 37 |
38 | -------------------------------------------------------------------------------- /src/methods-and-traits/traits/supertraits.md: -------------------------------------------------------------------------------- 1 | # Supertraits 2 | 3 | A trait can require that types implementing it also implement other traits, 4 | called _supertraits_. Here, any type implementing `Pet` must implement `Animal`. 5 | 6 | ```rust,editable 7 | trait Animal { 8 | fn leg_count(&self) -> u32; 9 | } 10 | 11 | trait Pet: Animal { 12 | fn name(&self) -> String; 13 | } 14 | 15 | struct Dog(String); 16 | 17 | impl Animal for Dog { 18 | fn leg_count(&self) -> u32 { 19 | 4 20 | } 21 | } 22 | 23 | impl Pet for Dog { 24 | fn name(&self) -> String { 25 | self.0.clone() 26 | } 27 | } 28 | 29 | fn main() { 30 | let puppy = Dog(String::from("Rex")); 31 | println!("{} has {} legs", puppy.name(), puppy.leg_count()); 32 | } 33 | ``` 34 | 35 |
36 | 37 | This is sometimes called "trait inheritance" but students should not expect this 38 | to behave like OO inheritance. It just specifies an additional requirement on 39 | implementations of a trait. 40 | 41 |
42 | -------------------------------------------------------------------------------- /src/modules.md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/modules/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "modules" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "modules" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/modules/modules.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 3 3 | --- 4 | 5 | # Modules 6 | 7 | We have seen how `impl` blocks let us namespace functions to a type. 8 | 9 | Similarly, `mod` lets us namespace types and functions: 10 | 11 | ```rust,editable 12 | mod foo { 13 | pub fn do_something() { 14 | println!("In the foo module"); 15 | } 16 | } 17 | 18 | mod bar { 19 | pub fn do_something() { 20 | println!("In the bar module"); 21 | } 22 | } 23 | 24 | fn main() { 25 | foo::do_something(); 26 | bar::do_something(); 27 | } 28 | ``` 29 | 30 |
31 | 32 | - Packages provide functionality and include a `Cargo.toml` file that describes 33 | how to build a bundle of 1+ crates. 34 | - Crates are a tree of modules, where a binary crate creates an executable and a 35 | library crate compiles to a library. 36 | - Modules define organization, scope, and are the focus of this section. 37 | 38 |
39 | -------------------------------------------------------------------------------- /src/pattern-matching.md: -------------------------------------------------------------------------------- 1 | # Pattern Matching 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/pattern-matching/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pattern-matching" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [lib] 8 | name = "eval" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/pattern-matching/destructuring-structs.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 4 3 | --- 4 | 5 | # Structs 6 | 7 | Like tuples, Struct can also be destructured by matching: 8 | 9 | ```rust,editable 10 | {{#include ../../third_party/rust-by-example/destructuring-structs.rs}} 11 | ``` 12 | 13 |
14 | 15 | - Change the literal values in `foo` to match with the other patterns. 16 | - Add a new field to `Foo` and make changes to the pattern as needed. 17 | 18 | ## More to Explore 19 | 20 | - Try `match &foo` and check the type of captures. The pattern syntax remains 21 | the same, but the captures become shared references. This is 22 | [match ergonomics](https://rust-lang.github.io/rfcs/2005-match-ergonomics.html) 23 | and is often useful with `match self` when implementing methods on an enum. 24 | - The same effect occurs with `match &mut foo`: the captures become exclusive 25 | references. 26 | - The distinction between a capture and a constant expression can be hard to 27 | spot. Try changing the `2` in the second arm to a variable, and see that it 28 | subtly doesn't work. Change it to a `const` and see it working again. 29 | 30 |
31 | -------------------------------------------------------------------------------- /src/pattern-matching/let-control-flow.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 10 3 | --- 4 | 5 | # Let Control Flow 6 | 7 | Rust has a few control flow constructs which differ from other languages. They 8 | are used for pattern matching: 9 | 10 | - `if let` expressions 11 | - `while let` expressions 12 | - `let else` expressions 13 | -------------------------------------------------------------------------------- /src/pattern-matching/let-control-flow/if-let.md: -------------------------------------------------------------------------------- 1 | # `if let` Expressions 2 | 3 | The 4 | [`if let` expression](https://doc.rust-lang.org/reference/expressions/if-expr.html#if-let-expressions) 5 | lets you execute different code depending on whether a value matches a pattern: 6 | 7 | ```rust,editable 8 | use std::time::Duration; 9 | 10 | fn sleep_for(secs: f32) { 11 | if let Ok(duration) = Duration::try_from_secs_f32(secs) { 12 | std::thread::sleep(duration); 13 | println!("slept for {duration:?}"); 14 | } 15 | } 16 | 17 | fn main() { 18 | sleep_for(-10.0); 19 | sleep_for(0.8); 20 | } 21 | ``` 22 | 23 |
24 | 25 | - Unlike `match`, `if let` does not have to cover all branches. This can make it 26 | more concise than `match`. 27 | - A common usage is handling `Some` values when working with `Option`. 28 | - Unlike `match`, `if let` does not support guard clauses for pattern matching. 29 | - With an `else` clause, this can be used as an expression. 30 | 31 |
32 | -------------------------------------------------------------------------------- /src/pattern-matching/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/references.md: -------------------------------------------------------------------------------- 1 | # References 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/references/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "references" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "references" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/references/dangling.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 3 3 | --- 4 | 5 | # Reference Validity 6 | 7 | Rust enforces a number of rules for references that make them always safe to 8 | use. One rule is that references can never be `null`, making them safe to use 9 | without `null` checks. The other rule we'll look at for now is that references 10 | can't _outlive_ the data they point to. 11 | 12 | ```rust,editable,compile_fail 13 | fn main() { 14 | let x_ref = { 15 | let x = 10; 16 | &x 17 | }; 18 | dbg!(x_ref); 19 | } 20 | ``` 21 | 22 |
23 | 24 | - This slide gets students thinking about references as not simply being 25 | pointers, since Rust has different rules for references than other languages. 26 | 27 | - We'll look at the rest of Rust's borrowing rules on day 3 when we talk about 28 | Rust's ownership system. 29 | 30 | ## More to Explore 31 | 32 | - Rust's equivalent of nullability is the `Option` type, which can be used to 33 | make any type "nullable" (not just references/pointers). We haven't yet 34 | introduced enums or pattern matching, though, so try not to go into too much 35 | detail about this here. 36 | 37 |
38 | -------------------------------------------------------------------------------- /src/references/exclusive.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 5 3 | --- 4 | 5 | # Exclusive References 6 | 7 | Exclusive references, also known as mutable references, allow changing the value 8 | they refer to. They have type `&mut T`. 9 | 10 | 11 | 12 | ```rust,editable 13 | fn main() { 14 | let mut point = (1, 2); 15 | let x_coord = &mut point.0; 16 | *x_coord = 20; 17 | println!("point: {point:?}"); 18 | } 19 | ``` 20 | 21 |
22 | 23 | Key points: 24 | 25 | - "Exclusive" means that only this reference can be used to access the value. No 26 | other references (shared or exclusive) can exist at the same time, and the 27 | referenced value cannot be accessed while the exclusive reference exists. Try 28 | making an `&point.0` or changing `point.0` while `x_coord` is alive. 29 | 30 | - Be sure to note the difference between `let mut x_coord: &i32` and 31 | `let x_coord: &mut i32`. The first one represents a shared reference which can 32 | be bound to different values, while the second represents an exclusive 33 | reference to a mutable value. 34 | 35 |
36 | -------------------------------------------------------------------------------- /src/references/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 20 3 | --- 4 | 5 | # Exercise: Geometry 6 | 7 | We will create a few utility functions for 3-dimensional geometry, representing 8 | a point as `[f64;3]`. It is up to you to determine the function signatures. 9 | 10 | ```rust,compile_fail,editable 11 | // Calculate the magnitude of a vector by summing the squares of its coordinates 12 | // and taking the square root. Use the `sqrt()` method to calculate the square 13 | // root, like `v.sqrt()`. 14 | 15 | {{#include exercise.rs:magnitude}} 16 | fn magnitude(...) -> f64 { 17 | todo!() 18 | } 19 | 20 | // Normalize a vector by calculating its magnitude and dividing all of its 21 | // coordinates by that magnitude. 22 | 23 | {{#include exercise.rs:normalize}} 24 | fn normalize(...) { 25 | todo!() 26 | } 27 | 28 | // Use the following `main` to test your work. 29 | 30 | {{#include exercise.rs:main}} 31 | ``` 32 | -------------------------------------------------------------------------------- /src/references/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | 7 |
8 | 9 | - Note that in `normalize` we were able to do `*item /= mag` to modify each 10 | element. This is because we're iterating using a mutable reference to an 11 | array, which causes the `for` loop to give mutable references to each element. 12 | 13 |
14 | -------------------------------------------------------------------------------- /src/running-the-course/keyboard-shortcuts.md: -------------------------------------------------------------------------------- 1 | # Keyboard Shortcuts 2 | 3 | There are several useful keyboard shortcuts in mdBook: 4 | 5 | - Arrow-Left: Navigate to the previous page. 6 | - Arrow-Right: Navigate to the next page. 7 | - Ctrl + Enter: Execute the code sample that has focus. 8 | - s: Activate the search bar. 9 | -------------------------------------------------------------------------------- /src/smart-pointers.md: -------------------------------------------------------------------------------- 1 | # Smart Pointers 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/smart-pointers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smart-pointers" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [lib] 8 | name = "binary_tree" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/smart-pointers/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 30 3 | --- 4 | 5 | # Exercise: Binary Tree 6 | 7 | A binary tree is a tree-type data structure where every node has two children 8 | (left and right). We will create a tree where each node stores a value. For a 9 | given node N, all nodes in a N's left subtree contain smaller values, and all 10 | nodes in N's right subtree will contain larger values. 11 | 12 | Implement the following types, so that the given tests pass. 13 | 14 | ```rust,compile_fail,editable 15 | {{#include exercise.rs:types}} 16 | 17 | // Implement `new`, `insert`, `len`, and `has` for `Subtree`. 18 | 19 | {{#include exercise.rs:tests}} 20 | ``` 21 | -------------------------------------------------------------------------------- /src/smart-pointers/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/std-traits.md: -------------------------------------------------------------------------------- 1 | # Standard Library Traits 2 | 3 | {{%segment outline}} 4 | 5 |
6 | 7 | As with the standard-library types, spend time reviewing the documentation for 8 | each trait. 9 | 10 | This section is long. Take a break midway through. 11 | 12 |
13 | -------------------------------------------------------------------------------- /src/std-traits/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "std-traits" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [lib] 8 | name = "std_traits" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/std-traits/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 30 3 | --- 4 | 5 | # Exercise: ROT13 6 | 7 | In this example, you will implement the classic 8 | ["ROT13" cipher](https://en.wikipedia.org/wiki/ROT13). Copy this code to the 9 | playground, and implement the missing bits. Only rotate ASCII alphabetic 10 | characters, to ensure the result is still valid UTF-8. 11 | 12 | ```rust,editable 13 | {{#include exercise.rs:head }} 14 | 15 | // Implement the `Read` trait for `RotDecoder`. 16 | 17 | {{#include exercise.rs:tests }} 18 | ``` 19 | 20 | What happens if you chain two `RotDecoder` instances together, each rotating by 21 | 13 characters? 22 | -------------------------------------------------------------------------------- /src/std-traits/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/std-types.md: -------------------------------------------------------------------------------- 1 | # Standard Library Types 2 | 3 | {{%segment outline}} 4 | 5 |
6 | 7 | For each of the slides in this section, spend some time reviewing the 8 | documentation pages, highlighting some of the more common methods. 9 | 10 |
11 | -------------------------------------------------------------------------------- /src/std-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "std-types" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "hashset" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/std-types/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/std-types/std.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 3 3 | --- 4 | 5 | # Standard Library 6 | 7 | Rust comes with a standard library which helps establish a set of common types 8 | used by Rust libraries and programs. This way, two libraries can work together 9 | smoothly because they both use the same `String` type. 10 | 11 | In fact, Rust contains several layers of the Standard Library: `core`, `alloc` 12 | and `std`. 13 | 14 | - `core` includes the most basic types and functions that don't depend on 15 | `libc`, allocator or even the presence of an operating system. 16 | - `alloc` includes types which require a global heap allocator, such as `Vec`, 17 | `Box` and `Arc`. 18 | - Embedded Rust applications often only use `core`, and sometimes `alloc`. 19 | -------------------------------------------------------------------------------- /src/testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "testing" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [lints.rust] 8 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(never)'] } 9 | 10 | [lib] 11 | name = "luhn" 12 | path = "exercise.rs" 13 | -------------------------------------------------------------------------------- /src/testing/lints.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 3 3 | --- 4 | 5 | # Compiler Lints and Clippy 6 | 7 | The Rust compiler produces fantastic error messages, as well as helpful built-in 8 | lints. [Clippy](https://doc.rust-lang.org/clippy/) provides even more lints, 9 | organized into groups that can be enabled per-project. 10 | 11 | ```rust,editable,should_panic,warnunused 12 | #[deny(clippy::cast_possible_truncation)] 13 | fn main() { 14 | let mut x = 3; 15 | while (x < 70000) { 16 | x *= 2; 17 | } 18 | println!("X probably fits in a u16, right? {}", x as u16); 19 | } 20 | ``` 21 | 22 |
23 | 24 | There are compiler lints visible here, but not clippy lints. Run `clippy` on the 25 | playground site to show clippy warnings. Clippy has extensive documentation of 26 | its lints, and adds new lints (including default-deny lints) all the time. 27 | 28 | Note that errors or warnings with `help: ...` can be fixed with `cargo fix` or 29 | via your editor. 30 | 31 |
32 | -------------------------------------------------------------------------------- /src/testing/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/testing/unit-tests.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 5 3 | --- 4 | 5 | # Unit Tests 6 | 7 | Rust and Cargo come with a simple unit test framework. Tests are marked with 8 | `#[test]`. Unit tests are often put in a nested `tests` module, using 9 | `#[cfg(test)]` to conditionally compile them only when building tests. 10 | 11 | ```rust,editable 12 | fn first_word(text: &str) -> &str { 13 | match text.find(' ') { 14 | Some(idx) => &text[..idx], 15 | None => &text, 16 | } 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | 23 | #[test] 24 | fn test_empty() { 25 | assert_eq!(first_word(""), ""); 26 | } 27 | 28 | #[test] 29 | fn test_single_word() { 30 | assert_eq!(first_word("Hello"), "Hello"); 31 | } 32 | 33 | #[test] 34 | fn test_multiple_words() { 35 | assert_eq!(first_word("Hello World"), "Hello"); 36 | } 37 | } 38 | ``` 39 | 40 | - This lets you unit test private helpers. 41 | - The `#[cfg(test)]` attribute is only active when you run `cargo test`. 42 | -------------------------------------------------------------------------------- /src/thanks.md: -------------------------------------------------------------------------------- 1 | --- 2 | course: none 3 | --- 4 | 5 | # Thanks! 6 | 7 | _Thank you for taking Comprehensive Rust 🦀!_ We hope you enjoyed it and that it 8 | was useful. 9 | 10 | We've had a lot of fun putting the course together. The course is not perfect, 11 | so if you spotted any mistakes or have ideas for improvements, please get in 12 | [contact with us on GitHub](https://github.com/google/comprehensive-rust/discussions). 13 | We would love to hear from you. 14 | 15 |
16 | 17 | - Thank you for reading the speaker notes! We hope they have been useful. If you 18 | find pages without notes, please send us a PR and link it to [issue #1083]. We 19 | are also very grateful for fixes and improvements to the existing notes. 20 | 21 |
22 | 23 | [issue #1083]: https://github.com/google/comprehensive-rust/issues/1083 24 | -------------------------------------------------------------------------------- /src/tuples-and-arrays.md: -------------------------------------------------------------------------------- 1 | # Tuples and Arrays 2 | 3 | {{%segment outline}} 4 | 5 |
6 | 7 | - We have seen how primitive types work in Rust. Now it's time for you to start 8 | building new composite types. 9 | 10 |
11 | -------------------------------------------------------------------------------- /src/tuples-and-arrays/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tuples-and-arrays" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "transpose" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/tuples-and-arrays/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 15 3 | --- 4 | 5 | # Exercise: Nested Arrays 6 | 7 | Arrays can contain other arrays: 8 | 9 | ```rust,editable 10 | let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; 11 | ``` 12 | 13 | What is the type of this variable? 14 | 15 | Use an array such as the above to write a function `transpose` which will 16 | transpose a matrix (turn rows into columns): 17 | 18 | 19 | 20 | ```bob 21 | ⎛⎡1 2 3⎤⎞ ⎡1 4 7⎤ 22 | "transpose"⎜⎢4 5 6⎥⎟ "=="⎢2 5 8⎥ 23 | ⎝⎣7 8 9⎦⎠ ⎣3 6 9⎦ 24 | ``` 25 | 26 | Copy the code below to and implement the function. 27 | This function only operates on 3x3 matrices. 28 | 29 | ```rust,should_panic,editable 30 | {{#include exercise.rs:transpose}} 31 | todo!() 32 | } 33 | 34 | {{#include exercise.rs:main}} 35 | ``` 36 | -------------------------------------------------------------------------------- /src/tuples-and-arrays/iteration.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 3 3 | --- 4 | 5 | # Array Iteration 6 | 7 | The `for` statement supports iterating over arrays (but not tuples). 8 | 9 | ```rust,editable 10 | fn main() { 11 | let primes = [2, 3, 5, 7, 11, 13, 17, 19]; 12 | for prime in primes { 13 | for i in 2..prime { 14 | assert_ne!(prime % i, 0); 15 | } 16 | } 17 | } 18 | ``` 19 | 20 |
21 | 22 | This functionality uses the `IntoIterator` trait, but we haven't covered that 23 | yet. 24 | 25 | The `assert_ne!` macro is new here. There are also `assert_eq!` and `assert!` 26 | macros. These are always checked, while debug-only variants like `debug_assert!` 27 | compile to nothing in release builds. 28 | 29 |
30 | -------------------------------------------------------------------------------- /src/tuples-and-arrays/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/tuples-and-arrays/tuples.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 5 3 | --- 4 | 5 | # Tuples 6 | 7 | 8 | 9 | ```rust,editable 10 | fn main() { 11 | let t: (i8, bool) = (7, true); 12 | dbg!(t.0); 13 | dbg!(t.1); 14 | } 15 | ``` 16 | 17 |
18 | 19 | - Like arrays, tuples have a fixed length. 20 | 21 | - Tuples group together values of different types into a compound type. 22 | 23 | - Fields of a tuple can be accessed by the period and the index of the value, 24 | e.g. `t.0`, `t.1`. 25 | 26 | - The empty tuple `()` is referred to as the "unit type" and signifies absence 27 | of a return value, akin to `void` in other languages. 28 | 29 |
30 | -------------------------------------------------------------------------------- /src/types-and-values.md: -------------------------------------------------------------------------------- 1 | # Types and Values 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/types-and-values/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "types-and-values" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "fizzbuzz" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/types-and-values/exercise.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 15 3 | --- 4 | 5 | # Exercise: Fibonacci 6 | 7 | The Fibonacci sequence begins with `[0,1]`. For n>1, the n'th Fibonacci number 8 | is calculated recursively as the sum of the n-1'th and n-2'th Fibonacci numbers. 9 | 10 | Write a function `fib(n)` that calculates the n'th Fibonacci number. When will 11 | this function panic? 12 | 13 | ```rust,editable,should_panic 14 | {{#include exercise.rs:fib}} 15 | if n < 2 { 16 | // The base case. 17 | return todo!("Implement this"); 18 | } else { 19 | // The recursive case. 20 | return todo!("Implement this"); 21 | } 22 | } 23 | 24 | {{#include exercise.rs:main}} 25 | ``` 26 | -------------------------------------------------------------------------------- /src/types-and-values/exercise.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // ANCHOR: solution 16 | // ANCHOR: fib 17 | fn fib(n: u32) -> u32 { 18 | // ANCHOR_END: fib 19 | if n < 2 { 20 | return n; 21 | } else { 22 | return fib(n - 1) + fib(n - 2); 23 | } 24 | } 25 | 26 | // ANCHOR: main 27 | fn main() { 28 | let n = 20; 29 | println!("fib({n}) = {}", fib(n)); 30 | } 31 | // ANCHOR_END: main 32 | -------------------------------------------------------------------------------- /src/types-and-values/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/types-and-values/variables.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 5 3 | --- 4 | 5 | # Variables 6 | 7 | Rust provides type safety via static typing. Variable bindings are made with 8 | `let`: 9 | 10 | ```rust,editable,warnunused 11 | fn main() { 12 | let x: i32 = 10; 13 | println!("x: {x}"); 14 | // x = 20; 15 | // println!("x: {x}"); 16 | } 17 | ``` 18 | 19 |
20 | 21 | - Uncomment the `x = 20` to demonstrate that variables are immutable by default. 22 | Add the `mut` keyword to allow changes. 23 | 24 | - Warnings are enabled for this slide, such as for unused variables or 25 | unnecessary `mut`. These are omitted in most slides to avoid distracting 26 | warnings. Try removing the mutation but leaving the `mut` keyword in place. 27 | 28 | - The `i32` here is the type of the variable. This must be known at compile 29 | time, but type inference (covered later) allows the programmer to omit it in 30 | many cases. 31 | 32 |
33 | -------------------------------------------------------------------------------- /src/unsafe-rust.md: -------------------------------------------------------------------------------- 1 | # Unsafe Rust 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/unsafe-rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "unsafe-rust" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | tempfile = "3.20.0" 9 | 10 | [[bin]] 11 | name = "listdir" 12 | path = "exercise.rs" 13 | -------------------------------------------------------------------------------- /src/unsafe-rust/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/unsafe-rust/unions.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 5 3 | --- 4 | 5 | # Unions 6 | 7 | Unions are like enums, but you need to track the active field yourself: 8 | 9 | ```rust,editable 10 | #[repr(C)] 11 | union MyUnion { 12 | i: u8, 13 | b: bool, 14 | } 15 | 16 | fn main() { 17 | let u = MyUnion { i: 42 }; 18 | println!("int: {}", unsafe { u.i }); 19 | println!("bool: {}", unsafe { u.b }); // Undefined behavior! 20 | } 21 | ``` 22 | 23 |
24 | 25 | Unions are very rarely needed in Rust as you can usually use an enum. They are 26 | occasionally needed for interacting with C library APIs. 27 | 28 | If you just want to reinterpret bytes as a different type, you probably want 29 | [`std::mem::transmute`](https://doc.rust-lang.org/stable/std/mem/fn.transmute.html) 30 | or a safe wrapper such as the [`zerocopy`](https://crates.io/crates/zerocopy) 31 | crate. 32 | 33 |
34 | -------------------------------------------------------------------------------- /src/unsafe-rust/unsafe-functions.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 15 3 | --- 4 | 5 | # Unsafe Functions 6 | 7 | A function or method can be marked `unsafe` if it has extra preconditions you 8 | must uphold to avoid undefined behaviour. 9 | 10 | There are two main categories: 11 | 12 | - Rust functions declared unsafe with `unsafe fn`. 13 | - Foreign functions in `extern "C"` blocks. 14 | 15 |
16 | 17 | We will look at the two kinds of unsafe functions next. 18 | 19 |
20 | -------------------------------------------------------------------------------- /src/user-defined-types.md: -------------------------------------------------------------------------------- 1 | # User-Defined Types 2 | 3 | {{%segment outline}} 4 | -------------------------------------------------------------------------------- /src/user-defined-types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "user-defined-types" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "elevator" 9 | path = "exercise.rs" 10 | -------------------------------------------------------------------------------- /src/user-defined-types/aliases.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 2 3 | --- 4 | 5 | # Type Aliases 6 | 7 | A type alias creates a name for another type. The two types can be used 8 | interchangeably. 9 | 10 | ```rust,editable 11 | enum CarryableConcreteItem { 12 | Left, 13 | Right, 14 | } 15 | 16 | type Item = CarryableConcreteItem; 17 | 18 | // Aliases are more useful with long, complex types: 19 | use std::cell::RefCell; 20 | use std::sync::{Arc, RwLock}; 21 | type PlayerInventory = RwLock>>>; 22 | ``` 23 | 24 |
25 | 26 | - A [newtype](tuple-structs.html) is often a better alternative since it creates 27 | a distinct type. Prefer `struct InventoryCount(usize)` to 28 | `type InventoryCount = usize`. 29 | 30 | - C programmers will recognize this as similar to a `typedef`. 31 | 32 |
33 | -------------------------------------------------------------------------------- /src/user-defined-types/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | ```rust,editable 4 | {{#include exercise.rs:solution}} 5 | ``` 6 | -------------------------------------------------------------------------------- /src/welcome-day-1-afternoon.md: -------------------------------------------------------------------------------- 1 | --- 2 | session: Day 1 Afternoon 3 | target_minutes: 180 4 | --- 5 | 6 | # Welcome Back 7 | 8 | {{%session outline}} 9 | -------------------------------------------------------------------------------- /src/welcome-day-2-afternoon.md: -------------------------------------------------------------------------------- 1 | --- 2 | session: Day 2 Afternoon 3 | target_minutes: 180 4 | --- 5 | 6 | # Welcome Back 7 | 8 | {{%session outline}} 9 | -------------------------------------------------------------------------------- /src/welcome-day-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 3 3 | course: Fundamentals 4 | session: Day 2 Morning 5 | target_minutes: 180 6 | --- 7 | 8 | # Welcome to Day 2 9 | 10 | Now that we have seen a fair amount of Rust, today will focus on Rust's type 11 | system: 12 | 13 | - Pattern matching: extracting data from structures. 14 | - Methods: associating functions with types. 15 | - Traits: behaviors shared by multiple types. 16 | - Generics: parameterizing types on other types. 17 | - Standard library types and traits: a tour of Rust's rich standard library. 18 | - Closures: function pointers with data. 19 | 20 | ## Schedule 21 | 22 | {{%session outline}} 23 | -------------------------------------------------------------------------------- /src/welcome-day-3-afternoon.md: -------------------------------------------------------------------------------- 1 | --- 2 | session: Day 3 Afternoon 3 | target_minutes: 180 4 | --- 5 | 6 | # Welcome Back 7 | 8 | {{%session outline}} 9 | -------------------------------------------------------------------------------- /src/welcome-day-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 3 3 | course: Fundamentals 4 | session: Day 3 Morning 5 | target_minutes: 180 6 | --- 7 | 8 | # Welcome to Day 3 9 | 10 | Today, we will cover: 11 | 12 | - Memory management, lifetimes, and the borrow checker: how Rust ensures memory 13 | safety. 14 | - Smart pointers: standard library pointer types. 15 | 16 | ## Schedule 17 | 18 | {{%session outline}} 19 | -------------------------------------------------------------------------------- /src/welcome-day-4-afternoon.md: -------------------------------------------------------------------------------- 1 | --- 2 | session: Day 4 Afternoon 3 | target_minutes: 180 4 | --- 5 | 6 | # Welcome Back 7 | 8 | {{%session outline}} 9 | -------------------------------------------------------------------------------- /src/welcome-day-4.md: -------------------------------------------------------------------------------- 1 | --- 2 | minutes: 3 3 | course: Fundamentals 4 | session: Day 4 Morning 5 | target_minutes: 180 6 | --- 7 | 8 | # Welcome to Day 4 9 | 10 | Today we will cover topics relating to building large-scale software in Rust: 11 | 12 | - Iterators: a deep dive on the `Iterator` trait. 13 | - Modules and visibility. 14 | - Testing. 15 | - Error handling: panics, `Result`, and the try operator `?`. 16 | - Unsafe Rust: the escape hatch when you can't express yourself in safe Rust. 17 | 18 | ## Schedule 19 | 20 | {{%session outline}} 21 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test", 3 | "type": "module", 4 | "devDependencies": { 5 | "@types/mocha": "^10.0.10", 6 | "@wdio/cli": "^9.5.3", 7 | "@wdio/local-runner": "^9.5.3", 8 | "@wdio/mocha-framework": "^9.5.0", 9 | "@wdio/static-server-service": "^9.5.0" 10 | }, 11 | "scripts": { 12 | "start": "npm run test", 13 | "test": "wdio run ./wdio.conf.ts", 14 | "test-mdbook": "wdio run ./wdio.conf-mdbook.ts" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/src/slides/slide-exemptions.list.ts: -------------------------------------------------------------------------------- 1 | // These slides are known to violate the slide style guide. 2 | // They are checked if they still violate and if not fail the test. 3 | // Please remove slides that become good so they don't regress. 4 | export const size_exemptions = [ 5 | "android/interoperability/java.html", 6 | "android/testing.html", 7 | "bare-metal/aps/entry-point.html", 8 | "exercises/bare-metal/compass.html", 9 | "exercises/bare-metal/solutions-afternoon.html", 10 | "exercises/bare-metal/rtc.html", 11 | "exercises/bare-metal/solutions-morning.html", 12 | "exercises/chromium/interoperability-with-cpp.html", 13 | "exercises/chromium/bringing-it-together.html", 14 | "concurrency/async-exercises/chat-app.html", 15 | "concurrency/async-exercises/solutions.html", 16 | "concurrency/sync-exercises/solutions.html", 17 | "concurrency/sync-exercises/link-checker.html", 18 | ]; 19 | 20 | export const playground_size_exemptions = [ 21 | "bare-metal/aps/better-uart/driver.html", 22 | "bare-metal/microcontrollers/type-state.html", 23 | "concurrency/async-pitfalls/cancellation.html", 24 | "iterators/intoiterator.html", 25 | ]; 26 | -------------------------------------------------------------------------------- /tests/src/slides/slides.list.ts: -------------------------------------------------------------------------------- 1 | // to enable local testing for slide size checks please (re)generate this file by executing: 2 | // $ ./tests/src/slides/create-slide.list.sh book/html 3 | // 4 | // This file is on purpose not pre-filled in the repository to avoid 5 | // a) manual maintenance of slide list 6 | // b) this takes some time to test 7 | export const slides = []; 8 | -------------------------------------------------------------------------------- /tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // This implicitly sets moduleResolution to "node16" 4 | // https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution-is-host-defined 5 | "module": "Node18", 6 | "target": "ES2024", 7 | "lib": [ 8 | // https://www.typescriptlang.org/tsconfig/#lib 9 | // include APIs that are enabled by ES2024 10 | "ES2024", 11 | // DOM definitions, window, document, etc. 12 | "DOM" 13 | ], 14 | "types": [ 15 | "node", 16 | "@wdio/globals/types", 17 | "expect-webdriverio", 18 | "@wdio/mocha-framework" 19 | ], 20 | "skipLibCheck": true, 21 | "noEmit": true, 22 | "allowImportingTsExtensions": true, 23 | "resolveJsonModule": true, 24 | "isolatedModules": true, 25 | "strict": true, 26 | "noUnusedLocals": true, 27 | "noUnusedParameters": true, 28 | "noFallthroughCasesInSwitch": true 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "wdio.conf.ts" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /tests/wdio.conf-mdbook.ts: -------------------------------------------------------------------------------- 1 | import { deepmerge } from "deepmerge-ts"; 2 | import { config as default_config } from "./wdio.conf.js"; 3 | 4 | // have main config file as default but overwrite how the code is served 5 | export const config = deepmerge( 6 | default_config, 7 | { 8 | // use the mdbook served content 9 | baseUrl: "http://localhost:3000", 10 | // clean services 11 | services: [], 12 | }, 13 | { clone: false }, 14 | ); 15 | -------------------------------------------------------------------------------- /theme/css/language-picker.css: -------------------------------------------------------------------------------- 1 | #language-list { 2 | left: auto; 3 | right: 10px; 4 | } 5 | 6 | [dir="rtl"] #language-list { 7 | left: 10px; 8 | right: auto; 9 | } 10 | 11 | #language-list a { 12 | color: inherit; 13 | } 14 | -------------------------------------------------------------------------------- /theme/css/redbox.css: -------------------------------------------------------------------------------- 1 | div#aspect-ratio-helper { 2 | position: fixed; 3 | top: 8px; 4 | left: 8px; 5 | right: 8px; 6 | z-index: 1000; 7 | pointer-events: none; 8 | } 9 | 10 | div#aspect-ratio-helper div { 11 | outline: 3px dashed red; 12 | margin: 0 auto; 13 | /* At this width, the theme fonts are readable in a 16 14 | person conference room. If the browser is wider, the 15 | text becomes too small to be legible. */ 16 | max-width: 980px; 17 | /* On a standard 16/9 monitor, we expect to lose a bit 18 | of vertical space to borders. */ 19 | aspect-ratio: 16/8.5; 20 | } 21 | 22 | #instructor-menu-list { 23 | margin-left: 55px; 24 | } 25 | -------------------------------------------------------------------------------- /theme/css/rtl.css: -------------------------------------------------------------------------------- 1 | [dir="rtl"] .hljs, 2 | [dir="rtl"] pre > code { 3 | text-align: left; 4 | } 5 | [dir="rtl"] #cookieBar { 6 | direction: ltr; 7 | } 8 | [dir="rtl"] svg { 9 | direction: ltr; 10 | } 11 | -------------------------------------------------------------------------------- /theme/css/speaker-notes.css: -------------------------------------------------------------------------------- 1 | .content details { 2 | background: var(--sidebar-bg); 3 | color: var(--sidebar-fg) !important; 4 | border-radius: 0.25em; 5 | padding: 0.25em; 6 | } 7 | 8 | .content details summary h4 { 9 | display: inline-block; 10 | list-style: none; 11 | font-weight: normal; 12 | font-style: italic; 13 | margin: 0.5em 0.25em; 14 | cursor: pointer; 15 | } 16 | 17 | .content details summary h4:target::before { 18 | margin-left: -40px; 19 | width: 40px; 20 | } 21 | 22 | .content details summary .pop-out { 23 | color: var(--icons); 24 | padding: 0 8px; 25 | cursor: pointer; 26 | transition: color 0.5s; 27 | } 28 | 29 | .content details summary .pop-out i:hover { 30 | color: var(--icons-hover); 31 | } 32 | -------------------------------------------------------------------------------- /theme/css/svgbob.css: -------------------------------------------------------------------------------- 1 | /* Ensure text is legible in all themes. */ 2 | svg text { 3 | fill: var(--fg); 4 | } 5 | -------------------------------------------------------------------------------- /third_party/README.md: -------------------------------------------------------------------------------- 1 | # Third-party Course Content 2 | 3 | The files in this directory are included in the course via the `{{#include ..}}` 4 | syntax. All third-party content must be placed here to clearly indicate its 5 | origin. 6 | 7 | When we publish a translation of the course, we `git restore` the `src/` and 8 | `third_party/` directories at the repository root back to the date listed in the 9 | POT-Creation-Date header of the translation. **It is crucial, that all 10 | translatable content lives in those two directories.** The other files (such as 11 | `book.toml` and `theme/`) are not restored and we always use the latest version 12 | of them. 13 | -------------------------------------------------------------------------------- /third_party/cxx/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /third_party/cxx/README.md: -------------------------------------------------------------------------------- 1 | # CXX 2 | 3 | This directory contains files copied from CXX. Please see 4 | for the full project. 5 | 6 | ## License 7 | 8 | CXX is licensed under either of 9 | 10 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 11 | ) 12 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 13 | ) 14 | 15 | at your option. 16 | -------------------------------------------------------------------------------- /third_party/cxx/blobstore/Android.bp: -------------------------------------------------------------------------------- 1 | cc_library_static { 2 | name: "blobstore_cpp", 3 | srcs: ["src/blobstore.cc"], 4 | generated_headers: [ 5 | "cxx-bridge-header", 6 | "blobstore_bridge_header" 7 | ], 8 | generated_sources: ["blobstore_bridge_code"], 9 | } 10 | 11 | genrule { 12 | name: "blobstore_bridge_header", 13 | tools: ["cxxbridge"], 14 | cmd: "$(location cxxbridge) $(in) --header > $(out)", 15 | srcs: ["src/main.rs"], 16 | out: ["main.rs.h"], 17 | } 18 | 19 | genrule { 20 | name: "blobstore_bridge_code", 21 | tools: ["cxxbridge"], 22 | cmd: "$(location cxxbridge) $(in) > $(out)", 23 | srcs: ["src/main.rs"], 24 | out: ["main.rs.cc"], 25 | } 26 | 27 | rust_binary { 28 | name: "blobstore", 29 | srcs: ["src/main.rs"], 30 | rustlibs: ["libcxx"], 31 | static_libs: ["blobstore_cpp"], 32 | } 33 | -------------------------------------------------------------------------------- /third_party/cxx/blobstore/BUILD: -------------------------------------------------------------------------------- 1 | load("@rules_cc//cc:defs.bzl", "cc_library") 2 | load("@rules_rust//rust:defs.bzl", "rust_binary") 3 | load("//tools/bazel:rust_cxx_bridge.bzl", "rust_cxx_bridge") 4 | 5 | rust_binary( 6 | name = "demo", 7 | srcs = glob(["src/**/*.rs"]), 8 | edition = "2021", 9 | deps = [ 10 | ":blobstore-sys", 11 | ":bridge", 12 | "//:cxx", 13 | ], 14 | ) 15 | 16 | rust_cxx_bridge( 17 | name = "bridge", 18 | src = "src/main.rs", 19 | deps = [":blobstore-include"], 20 | ) 21 | 22 | cc_library( 23 | name = "blobstore-sys", 24 | srcs = ["src/blobstore.cc"], 25 | deps = [ 26 | ":blobstore-include", 27 | ":bridge/include", 28 | ], 29 | ) 30 | 31 | cc_library( 32 | name = "blobstore-include", 33 | hdrs = ["include/blobstore.h"], 34 | deps = ["//:core"], 35 | ) 36 | -------------------------------------------------------------------------------- /third_party/cxx/blobstore/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "demo" 3 | version = "0.0.0" 4 | authors = ["David Tolnay "] 5 | description = "Toy project from https://github.com/dtolnay/cxx" 6 | edition = "2021" 7 | license = "MIT OR Apache-2.0" 8 | publish = false 9 | repository = "https://github.com/dtolnay/cxx" 10 | 11 | [dependencies] 12 | cxx = "1.0" 13 | 14 | [build-dependencies] 15 | cxx-build = "1.0" 16 | -------------------------------------------------------------------------------- /third_party/cxx/blobstore/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Find target directory, either from CARGO_TARGET_DIR or in-tree if unset. 3 | let mut src_dir = 4 | std::env::var_os("CARGO_TARGET_DIR").unwrap_or("../../../target".into()); 5 | src_dir.push("/cxxbridge/demo/src"); 6 | 7 | cxx_build::bridge("src/main.rs") 8 | .file("src/blobstore.cc") 9 | .flag_if_supported("-std=c++14") 10 | .include(".") 11 | .include(src_dir) 12 | .compile("cxxbridge-demo"); 13 | 14 | println!("cargo:rerun-if-changed=src/main.rs"); 15 | println!("cargo:rerun-if-changed=src/blobstore.cc"); 16 | println!("cargo:rerun-if-changed=include/blobstore.h"); 17 | } 18 | -------------------------------------------------------------------------------- /third_party/cxx/blobstore/include/blobstore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "rust/cxx.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace org { 9 | namespace blobstore { 10 | 11 | struct MultiBuf; 12 | struct BlobMetadata; 13 | 14 | class BlobstoreClient { 15 | public: 16 | BlobstoreClient(); 17 | uint64_t put(MultiBuf &buf); 18 | void tag(uint64_t blobid, rust::Str tag); 19 | BlobMetadata metadata(uint64_t blobid) const; 20 | 21 | private: 22 | using Blob = struct { 23 | std::string data; 24 | std::set tags; 25 | }; 26 | std::unordered_map blobs; 27 | }; 28 | 29 | std::unique_ptr new_blobstore_client(); 30 | 31 | } // namespace blobstore 32 | } // namespace org 33 | -------------------------------------------------------------------------------- /third_party/cxx/book/snippets.cc: -------------------------------------------------------------------------------- 1 | // This file contains various code snippets taken from the CXX book and 2 | // tutorial. Some have been modified to fit the course better. 3 | 4 | // ANCHOR: shared_enums_cpp 5 | enum class Suit : uint8_t { 6 | Clubs = 0, 7 | Diamonds = 1, 8 | Hearts = 2, 9 | Spades = 3, 10 | }; 11 | // ANCHOR_END: shared_enums_cpp 12 | -------------------------------------------------------------------------------- /third_party/mdbook/README.md: -------------------------------------------------------------------------------- 1 | # mdBook 2 | 3 | This directory contains files copied from mdBook. Please see 4 | for the full project. 5 | 6 | ## License 7 | 8 | mdBook is licensed under the Mozilla Public License v2.0 ([LICENSE](LICENSE)). 9 | -------------------------------------------------------------------------------- /third_party/mdbook/book.js: -------------------------------------------------------------------------------- 1 | ../../theme/book.js -------------------------------------------------------------------------------- /third_party/mdbook/index.hbs: -------------------------------------------------------------------------------- 1 | ../../theme/index.hbs -------------------------------------------------------------------------------- /third_party/rust-by-example/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Jorge Aparicio 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /third_party/rust-by-example/README.md: -------------------------------------------------------------------------------- 1 | # Rust By Example 2 | 3 | This directory contains examples copied from Rust by Example. Please see 4 | for the full project. 5 | 6 | ## License 7 | 8 | Rust by Example is licensed under either of 9 | 10 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 11 | ) 12 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 13 | ) 14 | 15 | at your option. 16 | -------------------------------------------------------------------------------- /third_party/rust-by-example/destructuring-arrays.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | fn main() { 3 | let triple = [0, -2, 3]; 4 | println!("Tell me about {triple:?}"); 5 | match triple { 6 | [0, y, z] => println!("First is 0, y = {y}, and z = {z}"), 7 | [1, ..] => println!("First is 1 and the rest were ignored"), 8 | _ => println!("All elements were ignored"), 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /third_party/rust-by-example/destructuring-structs.rs: -------------------------------------------------------------------------------- 1 | struct Foo { 2 | x: (u32, u32), 3 | y: u32, 4 | } 5 | 6 | #[rustfmt::skip] 7 | fn main() { 8 | let foo = Foo { x: (1, 2), y: 3 }; 9 | match foo { 10 | Foo { y: 2, x: i } => println!("y = 2, x = {i:?}"), 11 | Foo { x: (1, b), y } => println!("x.0 = 1, b = {b}, y = {y}"), 12 | Foo { y, .. } => println!("y = {y}, other fields were ignored"), 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /third_party/rust-by-example/match-guards.rs: -------------------------------------------------------------------------------- 1 | #[rustfmt::skip] 2 | fn main() { 3 | let pair = (2, -2); 4 | println!("Tell me about {pair:?}"); 5 | match pair { 6 | (x, y) if x == y => println!("These are twins"), 7 | (x, y) if x + y == 0 => println!("Antimatter, kaboom!"), 8 | (x, _) if x % 2 == 1 => println!("The first one is odd"), 9 | _ => println!("No correlation..."), 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /third_party/rust-by-example/webevent.rs: -------------------------------------------------------------------------------- 1 | enum WebEvent { 2 | PageLoad, // Variant without payload 3 | KeyPress(char), // Tuple struct variant 4 | Click { x: i64, y: i64 }, // Full struct variant 5 | } 6 | 7 | #[rustfmt::skip] 8 | fn inspect(event: WebEvent) { 9 | match event { 10 | WebEvent::PageLoad => println!("page loaded"), 11 | WebEvent::KeyPress(c) => println!("pressed '{c}'"), 12 | WebEvent::Click { x, y } => println!("clicked at x={x}, y={y}"), 13 | } 14 | } 15 | 16 | fn main() { 17 | let load = WebEvent::PageLoad; 18 | let press = WebEvent::KeyPress('x'); 19 | let click = WebEvent::Click { x: 20, y: 80 }; 20 | 21 | inspect(load); 22 | inspect(press); 23 | inspect(click); 24 | } 25 | -------------------------------------------------------------------------------- /third_party/rust-on-exercism/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Exercism 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /third_party/rust-on-exercism/README.md: -------------------------------------------------------------------------------- 1 | # Exercism Rust Track 2 | 3 | This directory contains exercises copied from the Exercism Rust Track. Please 4 | see for the full project. 5 | 6 | ## License 7 | 8 | The Exercism Rust Track is licensed under the MIT license ([LICENSE](LICENSE)). 9 | -------------------------------------------------------------------------------- /third_party/rust-on-exercism/health-statistics.md: -------------------------------------------------------------------------------- 1 | You're working on implementing a health-monitoring system. As part of that, you 2 | need to keep track of users' health statistics. 3 | 4 | You'll start with a stubbed function in an `impl` block as well as a `User` 5 | struct definition. Your goal is to implement the stubbed out method on the 6 | `User` `struct` defined in the `impl` block. 7 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | edition = "2024" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = "1.0.98" 9 | clap = { version = "4.5.39", features = ["derive"] } 10 | -------------------------------------------------------------------------------- /xtask/README.md: -------------------------------------------------------------------------------- 1 | # xtask 2 | 3 | The purpose of the xtask binary is to enable cross platform task automation 4 | within the project (somewhat similar to how `npm run` is used in Node.js 5 | projects to run scripts). Please see 6 | [cargo xtask](https://github.com/matklad/cargo-xtask) for more information. 7 | 8 | To add support for a new task, add a new arm to the `match` in the 9 | `execute_task` function, and add a new handler function that contains the logic. 10 | --------------------------------------------------------------------------------