1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-25 19:56:09 +02:00

Publish Comprehensive Rust 🦀

This commit is contained in:
Martin Geisler 2022-12-21 16:36:30 +01:00
commit c212a473ba
252 changed files with 8047 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/book/
/target/

28
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,28 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code Reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
## Community Guidelines
This project follows [Google's Open Source Community
Guidelines](https://opensource.google/conduct/).

49
Cargo.toml Normal file
View File

@ -0,0 +1,49 @@
[package]
name = "comprehensive-rust"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "for-loops"
path = "src/exercises/day-1/for-loops.rs"
[[bin]]
name = "book-library"
path = "src/exercises/day-1/book-library.rs"
[[bin]]
name = "points-polygons"
path = "src/exercises/day-2/points-polygons.rs"
[[bin]]
name = "luhn"
path = "src/exercises/day-2/luhn.rs"
[[bin]]
name = "strings-iterators"
path = "src/exercises/day-2/strings-iterators.rs"
[[bin]]
name = "safe-ffi-wrapper"
path = "src/exercises/day-3/safe-ffi-wrapper.rs"
[[bin]]
name = "simple-gui"
path = "src/exercises/day-3/simple-gui.rs"
[[bin]]
name = "dining-philosophers"
path = "src/exercises/day-4/dining-philosophers.rs"
[[bin]]
name = "link-checker"
path = "src/exercises/day-4/link-checker.rs"
[dependencies]
reqwest = { version = "0.11.12", features = ["blocking"] }
scraper = "0.13.0"
thiserror = "1.0.37"

202
LICENSE.txt Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

40
README.md Normal file
View File

@ -0,0 +1,40 @@
# Comprehensive Rust 🦀
This repository has the source code for Comprehensive Rust 🦀, a four day Rust
course developed by the Android team. The course covers all aspects of Rust,
from basic syntax to generics and error handling. It also includes
Android-specific content on the last day.
## Building
The course is built using [mdBook](https://github.com/rust-lang/mdBook) and its
[Svgbob plugin](https://github.com/boozook/mdbook-svgbob). Install both tools
with
```shell
$ cargo install mdbook
$ cargo install mdbook-svgbob
```
Then run
```shell
$ mdbook test
```
to test all included Rust snippets. Run
```shell
$ mdbook serve
```
to start a web server with the course. You'll find the content on
<http://localhost:3000>. You can use `mdbook build` to create a static version
of the course in the `book/` directory.
## Contact
For questions or comments, please contact [Martin
Geisler](mailto:mgeisler@google.com) or start a [discussion on
GitHub](https://github.com/google/comprehensive-rust/discussions). We would love
to hear from you.

23
book.toml Normal file
View File

@ -0,0 +1,23 @@
[book]
authors = ["Martin Geisler"]
language = "en"
multilingual = false
src = "src"
title = "Comprehensive Rust 🦀"
[rust]
edition = "2021"
[preprocessor.svgbob]
class = "bob"
[output.html]
curly-quotes = true
additional-js = ["ga4.js"]
[output.html.fold]
enable = true
level = 0
[output.html.playground]
editable = true

87
ga4.js Normal file
View File

@ -0,0 +1,87 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Setup cookie consent banner.
var consent = document.createElement('script');
consent.setAttribute('data-autoload-cookie-consent-bar', 'true');
consent.setAttribute('src', 'https://www.gstatic.com/brandstudio/kato/cookie_choice_component/cookie_consent_bar.v3.js');
document.head.appendChild(consent);
// Load and configure Google Analytics.
var ga4 = document.createElement('script');
ga4.setAttribute('src', 'https://www.googletagmanager.com/gtag/js?id=G-ZN78TEJMRW');
document.head.appendChild(ga4);
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-ZN78TEJMRW');
// Look through all Playgrounds on the page to determine if the code snippet
// matches one of the. If the code is different from all Playgrounds, we
// conclude that the user modified the Playground before submitting it.
function isPlaygroundCodeModified(code) {
// It sounds expensive to look through every Playground, but there are
// normally at most two Playground instances on a page.
let playgrounds = Array.from(document.querySelectorAll(".playground"));
return playgrounds.every(playground => {
let code_block = playground.querySelector("code");
if (window.ace && code_block.classList.contains("editable")) {
let editor = window.ace.edit(code_block);
return code != editor.originalCode;
} else {
return code != code_block.textContent;
}
});
}
// Monkey-patch the window.fetch function so we can track the Playground
// executions.
const playgroundUrl = 'https://play.rust-lang.org/evaluate.json';
const { fetch: originalFetch } = window;
window.fetch = async (...args) => {
let [resource, config ] = args;
if (resource != playgroundUrl) {
return originalFetch(resource, config);
}
const startTime = window.performance.now();
let endTime, errorMessage;
try {
// The fetch_with_timeout function defaults to a 6000 ms timeout. We use a
// slightly shorter timeout so that we can catch and log the error.
config.signal = AbortSignal.timeout(5500);
let response = await originalFetch(resource, config);
payload = await response.json();
errorMessage = (payload.error == null) ? null : 'compilation_error';
// Return object compatible with the unpackign done in book.js.
return {'json': () => payload};
} catch (error) {
// fetch seems to always return AbortError, despite the example on
// https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout.
if (error.name == 'AbortError' || error.name == 'TimeoutError') {
error = new Error('timeout');
}
errorMessage = error.message;
throw error;
} finally {
endTime = window.performance.now();
let code = JSON.parse(config.body).code;
gtag("event", "playground", {
"modified": isPlaygroundCodeModified(code),
"error": errorMessage,
"latency": (endTime - startTime) / 1000,
});
}
};

1
rustfmt.toml Normal file
View File

@ -0,0 +1 @@
max_width = 90

237
src/SUMMARY.md Normal file
View File

@ -0,0 +1,237 @@
# Summary
[Welcome to Comprehensive Rust 🦀](welcome.md)
- [Using Cargo](cargo.md)
- [Rust Ecosystem](cargo/rust-ecosystem.md)
- [Code Samples](cargo/code-samples.md)
- [Running Cargo Locally](cargo/running-locally.md)
- [Course Structure](structure.md)
# Day 1: Morning
----
- [Welcome](welcome-day-1.md)
- [What is Rust?](welcome-day-1/what-is-rust.md)
- [Hello World!](hello-world.md)
- [Small Example](hello-world/small-example.md)
- [Why Rust?](why-rust.md)
- [Compile Time Guarantees](why-rust/compile-time.md)
- [Runtime Guarantees](why-rust/runtime.md)
- [Modern Features](why-rust/modern.md)
- [Basic Syntax](basic-syntax.md)
- [Scalar Types](basic-syntax/scalar-types.md)
- [Compound Types](basic-syntax/compound-types.md)
- [References](basic-syntax/references.md)
- [Dangling References](basic-syntax/references-dangling.md)
- [Slices](basic-syntax/slices.md)
- [`String` vs `str`](basic-syntax/string-slices.md)
- [Functions](basic-syntax/functions.md)
- [Methods](basic-syntax/methods.md)
- [Overloading](basic-syntax/functions-interlude.md)
- [Exercises](exercises/day-1/morning.md)
- [Implicit Conversions](exercises/day-1/implicit-conversions.md)
- [Arrays and `for` Loops](exercises/day-1/for-loops.md)
# Day 1: Afternoon
- [Variables](basic-syntax/variables.md)
- [Type Inference](basic-syntax/type-inference.md)
- [`static` & `const`](basic-syntax/static-and-const.md))
- [Scopes and Shadowing](basic-syntax/scopes-shadowing.md)
- [Memory Management](memory-management.md)
- [Stack vs Heap](memory-management/stack-vs-heap.md)
- [Stack Memory](memory-management/stack.md)
- [Manual Memory Management](memory-management/manual.md)
- [Scope-Based Memory Management](memory-management/scope-based.md)
- [Garbage Collection](memory-management/garbage-collection.md)
- [Rust Memory Management](memory-management/rust.md)
- [Comparison](memory-management/comparison.md)
- [Ownership](ownership.md)
- [Move Semantics](ownership/move-semantics.md)
- [Moved Strings in Rust](ownership/moved-strings-rust.md)
- [Double Frees in Modern C++](ownership/double-free-modern-cpp.md)
- [Moves in Function Calls](ownership/moves-function-calls.md)
- [Copying and Cloning](ownership/copy-clone.md)
- [Borrowing](ownership/borrowing.md)
- [Shared and Unique Borrows](ownership/shared-unique-borrows.md)
- [Lifetimes](ownership/lifetimes.md)
- [Lifetimes in Function Calls](ownership/lifetimes-function-calls.md)
- [Lifetimes in Data Structures](ownership/lifetimes-data-structures.md)
- [Exercises](exercises/day-1/afternoon.md)
- [Designing a Library](exercises/day-1/book-library.md)
- [Iterators and Ownership](exercises/day-1/iterators-and-ownership.md)
# Day 2: Morning
----
- [Welcome](welcome-day-2.md)
- [Structs](structs.md)
- [Tuple Structs](structs/tuple-structs.md)
- [Field Shorthand Syntax](structs/field-shorthand.md)
- [Enums](enums.md)
- [Variant Payloads](enums/variant-payloads.md)
- [Enum Sizes](enums/sizes.md)
- [Methods](methods.md)
- [Method Receiver](methods/receiver.md)
- [Example](methods/example.md)
- [Pattern Matching](pattern-matching.md)
- [Destructuring Enums](pattern-matching/destructuring-enums.md)
- [Destructuring Structs](pattern-matching/destructuring-structs.md)
- [Destructuring Arrays](pattern-matching/destructuring-arrays.md)
- [Match Guards](pattern-matching/match-guards.md)
- [Exercises](exercises/day-2/morning.md)
- [Health Statistics](exercises/day-2/health-statistics.md)
- [Points and Polygons](exercises/day-2/points-polygons.md)
# Day 2: Afternoon
- [Control Flow](control-flow.md)
- [Blocks](control-flow/blocks.md)
- [`if` expressions](control-flow/if-expressions.md)
- [`if let` expressions](control-flow/if-let-expressions.md)
- [`while` expressions](control-flow/while-expressions.md)
- [`while let` expressions](control-flow/while-let-expressions.md)
- [`for` expressions](control-flow/for-expressions.md)
- [`loop` expressions](control-flow/loop-expressions.md)
- [`match` expressions](control-flow/match-expressions.md)
- [`break` & `continue`](control-flow/break-continue.md)
- [Standard Library](std.md)
- [`String`](std/string.md)
- [`Option` and `Result`](std/option-result.md)
- [`Vec`](std/vec.md)
- [`HashMap`](std/hashmap.md)
- [`Box`](std/box.md)
- [`Recursive Data Types`](std/box-recursive.md)
- [`Niche Optimization`](std/box-niche.md)
- [`Rc`](std/rc.md)
- [Modules](modules.md)
- [Visibility](modules/visibility.md)
- [Paths](modules/paths.md)
- [Filesystem Hierarchy](modules/filesystem.md)
- [Exercises](exercises/day-2/afternoon.md)
- [Luhn Algorithm](exercises/day-2/luhn.md)
- [Strings and Iterators](exercises/day-2/strings-iterators.md)
# Day 3: Morning
----
- [Welcome](welcome-day-3.md)
- [Traits](traits.md)
- [Deriving Traits](traits/deriving-traits.md)
- [Default Methods](traits/default-methods.md)
- [Important Traits](traits/important-traits.md)
- [`Iterator`](traits/iterator.md)
- [`From` and `Into`](traits/from-into.md)
- [`Read` and `Write`](traits/read-write.md)
- [`Add`, `Mul`, ...](traits/operators.md)
- [`Drop`](traits/drop.md)
- [Generics](generics.md)
- [Generic Data Types](generics/data-types.md)
- [Generic Methods](generics/methods.md)
- [Trait Bounds](generics/trait-bounds.md)
- [`impl Trait`](generics/impl-trait.md)
- [Closures](generics/closures.md)
- [Monomorphization](generics/monomorphization.md)
- [Trait Objects](generics/trait-objects.md)
- [Exercises](exercises/day-3/morning.md)
- [A Simple GUI Library](exercises/day-3/simple-gui.md)
# Day 3: Afternoon
- [Error Handling](error-handling.md)
- [Panics](error-handling/panics.md)
- [Catching Stack Unwinding](error-handling/panic-unwind.md)
- [Structured Error Handling](error-handling/result.md)
- [Propagating Errors with `?`](error-handling/try-operator.md)
- [Converting Error Types](error-handling/converting-error-types.md)
- [Deriving Error Enums](error-handling/deriving-error-enums.md)
- [Adding Context to Errors](error-handling/error-contexts.md)
- [Testing](testing.md)
- [Unit Tests](testing/unit-tests.md)
- [Test Modules](testing/test-modules.md)
- [Documentation Tests](testing/doc-tests.md)
- [Integration Tests](testing/integration-tests.md)
- [Unsafe Rust](unsafe.md)
- [Dereferencing Raw Pointers](unsafe/raw-pointers.md)
- [Mutable Static Variables](unsafe/mutable-static-variables.md)
- [Calling Unsafe Functions](unsafe/unsafe-functions.md)
- [Extern Functions](unsafe/extern-functions.md)
- [Unions](unsafe/unions.md)
- [Exercises](exercises/day-3/afternoon.md)
- [Safe FFI Wrapper](exercises/day-3/safe-ffi-wrapper.md)
# Day 4: Morning
----
- [Welcome](welcome-day-4.md)
- [Concurrency](concurrency.md)
- [Threads](concurrency/threads.md)
- [Scoped Threads](concurrency/scoped-threads.md)
- [Channels](concurrency/channels.md)
- [Unbounded Channels](concurrency/channels/unbounded.md)
- [Bounded Channels](concurrency/channels/bounded.md)
- [Shared State](concurrency/shared_state.md)
- [`Arc`](concurrency/shared_state/arc.md)
- [`Mutex`](concurrency/shared_state/mutex.md)
- [Example](concurrency/shared_state/example.md)
- [`Send` and `Sync`](concurrency/send-sync.md)
- [`Send`](concurrency/send-sync/send.md)
- [`Sync`](concurrency/send-sync/sync.md)
- [Examples](concurrency/send-sync/examples.md)
- [Exercises](exercises/day-4/morning.md)
- [Dining Philosophers](exercises/day-4/dining-philosophers.md)
- [Multi-threaded Link Checker](exercises/day-4/link-checker.md)
# Day 4: Afternoon
----
- [Android](android.md)
- [Setup](android/setup.md)
- [Build Rules](android/build-rules.md)
- [Binary](android/build-rules/binary.md)
- [Library](android/build-rules/library.md)
- [AIDL](android/aidl.md)
- [Interface](android/aidl/interface.md)
- [Implementation](android/aidl/implementation.md)
- [Server](android/aidl/server.md)
- [Deploy](android/aidl/deploy.md)
- [Client](android/aidl/client.md)
- [Changing API](android/aidl/changing.md)
- [Logging](android/logging.md)
- [Interoperability](android/interoperability.md)
- [With C](android/interoperability/with-c.md)
- [Calling C with Bindgen](android/interoperability/with-c/bindgen.md)
- [Calling Rust from C](android/interoperability/with-c/rust.md)
- [With C++](android/interoperability/cpp.md))
- [With Java](android/interoperability/java.md)
- [Exercises](exercises/day-4/afternoon.md)
# Final Words
- [Thanks!](thanks.md)
- [Other Resources](other-resources.md)
- [Credits](credits.md)
----
# Solutions
----
- [Solutions](exercises/solutions.md)
- [Day 1 Morning](exercises/day-1/solutions-morning.md)
- [Day 1 Afternoon](exercises/day-1/solutions-afternoon.md)
- [Day 2 Morning](exercises/day-2/solutions-morning.md)
- [Day 2 Afternoon](exercises/day-2/solutions-afternoon.md)
- [Day 3 Morning](exercises/day-3/solutions-morning.md)
- [Day 3 Afternoon](exercises/day-3/solutions-afternoon.md)
- [Day 4 Morning](exercises/day-4/solutions-morning.md)

6
src/android.md Normal file
View File

@ -0,0 +1,6 @@
# Android
Rust is supported for native platform development on Android. This means that
you can write new operating system services in Rust, as well as extending
existing services.

7
src/android/aidl.md Normal file
View File

@ -0,0 +1,7 @@
# AIDL
The [Android Interface Definition Language
(AIDL)](https://developer.android.com/guide/components/aidl) is support in Rust:
* Rust code can call existing AIDL servers,
* You can create new AIDL servers in Rust.

View File

@ -0,0 +1,38 @@
// ANCHOR: libbirthdayservice
rust_library {
name: "libbirthdayservice",
srcs: ["src/lib.rs"],
crate_name: "birthdayservice",
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
],
}
// ANCHOR_END: libbirthdayservice
// ANCHOR: birthday_server
rust_binary {
name: "birthday_server",
crate_name: "birthday_server",
srcs: ["src/server.rs"],
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
"libbirthdayservice",
],
prefer_rlib: true,
}
// ANCHOR_END: birthday_server
// ANCHOR: birthday_client
rust_binary {
name: "birthday_client",
crate_name: "birthday_client",
srcs: ["src/client.rs"],
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
],
prefer_rlib: true,
}
// ANCHOR_END: birthday_client

View File

@ -0,0 +1,10 @@
aidl_interface {
name: "com.example.birthdayservice",
srcs: ["com/example/birthdayservice/*.aidl"],
unstable: true,
backend: {
rust: { // Rust is not enabled by default
enabled: true,
},
},
}

View File

@ -0,0 +1,22 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: IBirthdayService
package com.example.birthdayservice;
/** Birthday service interface. */
interface IBirthdayService {
/** Generate a Happy Birthday message. */
String wishHappyBirthday(String name, int years);
}

View File

@ -0,0 +1,42 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Birthday service.
use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService;
use com_example_birthdayservice::binder;
const SERVICE_IDENTIFIER: &str = "birthdayservice";
/// Connect to the BirthdayService.
pub fn connect() -> Result<binder::Strong<dyn IBirthdayService>, binder::StatusCode> {
binder::get_interface(SERVICE_IDENTIFIER)
}
/// Call the birthday service.
fn main() -> Result<(), binder::Status> {
let name = std::env::args()
.nth(1)
.unwrap_or_else(|| String::from("Bob"));
let years = std::env::args()
.nth(2)
.and_then(|arg| arg.parse::<i32>().ok())
.unwrap_or(42);
binder::ProcessState::start_thread_pool();
let service = connect().expect("Failed to connect to BirthdayService");
let msg = service.wishHappyBirthday(&name, years)?;
println!("{msg}");
Ok(())
}

View File

@ -0,0 +1,31 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: IBirthdayService
//! Implementation of the `IBirthdayService` AIDL interface.
use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::IBirthdayService;
use com_example_birthdayservice::binder;
/// The `IBirthdayService` implementation.
pub struct BirthdayService;
impl binder::Interface for BirthdayService {}
impl IBirthdayService for BirthdayService {
fn wishHappyBirthday(&self, name: &str, years: i32) -> binder::Result<String> {
Ok(format!(
"Happy Birthday {name}, congratulations with the {years} years!"
))
}
}

View File

@ -0,0 +1,33 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Birthday service.
use birthdayservice::BirthdayService;
use com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayService::BnBirthdayService;
use com_example_birthdayservice::binder;
const SERVICE_IDENTIFIER: &str = "birthdayservice";
/// Entry point for birthday service.
fn main() {
let birthday_service = BirthdayService;
let birthday_service_binder = BnBirthdayService::new_binder(
birthday_service,
binder::BinderFeatures::default(),
);
binder::add_service(SERVICE_IDENTIFIER, birthday_service_binder.as_binder())
.expect("Failed to register service");
binder::ProcessState::join_thread_pool()
}

View File

@ -0,0 +1,14 @@
# Changing API
Let us extend the API with more functionality: we want to let clients specify a
list of lines for the birthday card:
```java
package com.example.birthdayservice;
/** Birthday service interface. */
interface IBirthdayService {
/** Generate a Happy Birthday message. */
String wishHappyBirthday(String name, int years, in String[] text);
}
```

View File

@ -0,0 +1,24 @@
# AIDL Client
Finally, we can create a Rust client for our new service.
*birthday_service/src/client.rs*:
```rust,ignore
{{#include birthday_service/src/client.rs:main}}
```
*birthday_service/Android.bp*:
```javascript
{{#include birthday_service/Android.bp:birthday_client}}
```
Notice that the client does not depend on `libbirthdayservice`.
Build, push, and run the client on your device:
```shell
{{#include ../build_all.sh:birthday_client}}
Happy Birthday Charlie, congratulations with the 60 years!
```

View File

@ -0,0 +1,29 @@
# Deploy
We can now build, push, and start the service:
```shell
{{#include ../build_all.sh:birthday_server}}
```
In another terminal, check that the service runs:
```shell
{{#include ../build_all.sh:service_check_birthday_server}}
Service birthdayservice: found
```
You can also call the service with `service call`:
```shell
$ {{#include ../build_all.sh:service_call_birthday_server}}
Result: Parcel(
0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.'
0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.'
0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'
0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'
0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'
0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'
0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'
0x00000070: 00210073 00000000 's.!..... ')
```

View File

@ -0,0 +1,15 @@
# Service Implementation
We can now implement the AIDL service:
*birthday_service/src/lib.rs*:
```rust,ignore
{{#include birthday_service/src/lib.rs:IBirthdayService}}
```
*birthday_service/Android.bp*:
```javascript
{{#include birthday_service/Android.bp:libbirthdayservice}}
```

View File

@ -0,0 +1,18 @@
# AIDL Interfaces
You declare the API of your service using an AIDL interface:
*birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl*:
```java
{{#include birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:IBirthdayService}}
```
*birthday_service/aidl/Android.bp*:
```javascript
{{#include birthday_service/aidl/Android.bp}}
```
Add `vendor_available: true` if your AIDL file is used by a binary in the vendor
partition.

View File

@ -0,0 +1,15 @@
# AIDL Server
Finally, we can create a server which exposes the service:
*birthday_service/src/server.rs*:
```rust,ignore
{{#include birthday_service/src/server.rs:main}}
```
*birthday_service/Android.bp*:
```javascript
{{#include birthday_service/Android.bp:birthday_server}}
```

27
src/android/bpfmt.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/zsh
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Simple wrapper for bpfmt which will remove unnecessary newlines before the
# mdbook anchors.
if ! type bpfmt > /dev/null; then
echo 'Can not find bpfmt, do you need to run "m bpfmt"?'
exit 1
fi
for f in comprehensive_rust/**/Android.bp; do
bpfmt -s -w $f
sed -zi 's|\n// ANCHOR_END|// ANCHOR_END|g' $f
done

View File

@ -0,0 +1,16 @@
# Build Rules
The Android build system (Soong) supports Rust via a number of modules:
| Module Type | Description |
|-------------------|----------------------------------------------------------------------------------------------------|
| `rust_binary` | Produces a Rust binary. |
| `rust_library` | Produces a Rust library, and provides both `rlib` and `dylib` variants. |
| `rust_ffi` | Produces a Rust C library usable by `cc` modules, and provides both static and shared variants. |
| `rust_proc_macro` | Produces a `proc-macro` Rust library. These are analogous to compiler plugins. |
| `rust_test` | Produces a Rust test binary that uses the standard Rust test harness. |
| `rust_fuzz` | Produces a Rust fuzz binary leveraging `libfuzzer`. |
| `rust_protobuf` | Generates source and produces a Rust library that provides an interface for a particular protobuf. |
| `rust_bindgen` | Generates source and produces a Rust library containing Rust bindings to C libraries. |
We will look at `rust_binary` and `rust_library` next.

View File

@ -0,0 +1,23 @@
# Rust Binaries
Let us start with a simple application. At the root of an AOSP checkout, create
the following files:
_hello_rust/Android.bp_:
```javascript
{{#include binary/Android.bp}}
```
_hello_rust/src/main.rs_:
```rust
{{#include binary/src/main.rs:main}}
```
You can now build, push, and run the binary:
```shell
{{#include ../build_all.sh:hello_rust}}
Hello from Rust!
```

View File

@ -0,0 +1,5 @@
rust_binary {
name: "hello_rust",
crate_name: "hello_rust",
srcs: ["src/main.rs"],
}

View File

@ -0,0 +1,21 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Rust demo.
/// Prints a greeting to standard output.
fn main() {
println!("Hello from Rust!");
}

View File

@ -0,0 +1,37 @@
# Rust Libraries
You use `rust_library` to create a new Rust library for Android.
Here we declare a dependency on two libraries:
* `libgreeting`, which we define below,
* `libtextwrap`, which is a crate already vendored in
[`external/rust/crates/`][crates].
[crates]: https://cs.android.com/android/platform/superproject/+/master:external/rust/crates/
_hello_rust/Android.bp_:
```javascript
{{#include library/Android.bp}}
```
_hello_rust/src/main.rs_:
```rust,ignore
{{#include library/src/main.rs:main}}
```
_hello_rust/src/lib.rs_:
```rust,ignore
{{#include library/src/lib.rs:greeting}}
```
You build, push, and run the binary like before:
```shell
{{#include ../build_all.sh:hello_rust_with_dep}}
Hello Bob, it is very
nice to meet you!
```

View File

@ -0,0 +1,16 @@
rust_binary {
name: "hello_rust_with_dep",
crate_name: "hello_rust_with_dep",
srcs: ["src/main.rs"],
rustlibs: [
"libgreetings",
"libtextwrap",
],
prefer_rlib: true,
}
rust_library {
name: "libgreetings",
crate_name: "greetings",
srcs: ["src/lib.rs"],
}

View File

@ -0,0 +1,21 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: greeting
//! Greeting library.
/// Greet `name`.
pub fn greeting(name: &str) -> String {
format!("Hello {name}, it is very nice to meet you!")
}

View File

@ -0,0 +1,24 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Rust demo.
use greetings::greeting;
use textwrap::fill;
/// Prints a greeting to standard output.
fn main() {
println!("{}", fill(&greeting("Bob"), 24));
}

130
src/android/build_all.sh Executable file
View File

@ -0,0 +1,130 @@
#!/bin/bash
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -e
function run_example() {
while read -r line; do
if [[ "$line" != \#* ]]; then
echo "$line"
eval "${line#$ }"
fi
done
}
cd $ANDROID_BUILD_TOP
source build/envsetup.sh
lunch aosp_cf_x86_64_phone-userdebug
#acloud reconnect --autoconnect adb
adb root
adb shell rm -rf '/data/local/tmp/*'
run_example <<EOF
# ANCHOR: hello_rust
$ m hello_rust
$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust /data/local/tmp
$ adb shell /data/local/tmp/hello_rust
# ANCHOR_END: hello_rust
EOF
run_example <<EOF
# ANCHOR: hello_rust_with_dep
$ m hello_rust_with_dep
$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep /data/local/tmp
$ adb shell /data/local/tmp/hello_rust_with_dep
# ANCHOR_END: hello_rust_with_dep
EOF
function birthday_server() {
run_example <<EOF
# ANCHOR: birthday_server
$ m birthday_server
$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp
$ adb shell /data/local/tmp/birthday_server
# ANCHOR_END: birthday_server
EOF
}
pkill -f birthday_server || true
birthday_server &
BIRTHDAY_SERVER_PID=$!
while adb shell service check birthdayservice | grep -q 'not found'; do
echo "Waiting on birthdayservice..."
sleep 3
done
echo "Found birthdayservice..."
run_example <<EOF
# ANCHOR: service_check_birthday_server
$ adb shell service check birthdayservice
# ANCHOR_END: service_check_birthday_server
EOF
run_example <<EOF
# ANCHOR: service_call_birthday_server
$ adb shell service call birthdayservice 1 s16 Bob i32 24
# ANCHOR_END: service_call_birthday_server
EOF
run_example <<EOF
# ANCHOR: birthday_client
$ m birthday_client
$ adb push $ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/tmp
$ adb shell /data/local/tmp/birthday_client Charlie 60
# ANCHOR_END: birthday_client
EOF
pkill -f birthday_server
run_example <<EOF
# ANCHOR: hello_rust_logs
$ m hello_rust_logs
$ adb push $ANDROID_PRODUCT_OUT/system/bin/hello_rust_logs /data/local/tmp
$ adb shell /data/local/tmp/hello_rust_logs
# ANCHOR_END: hello_rust_logs
EOF
run_example <<EOF
# ANCHOR: print_birthday_card
$ m print_birthday_card
$ adb push $ANDROID_PRODUCT_OUT/system/bin/print_birthday_card /data/local/tmp
$ adb shell /data/local/tmp/print_birthday_card
# ANCHOR_END: print_birthday_card
EOF
run_example <<EOF
# ANCHOR: libbirthday_bindgen_test
$ atest libbirthday_bindgen_test
# ANCHOR_END: libbirthday_bindgen_test
EOF
run_example <<EOF
# ANCHOR: analyze_numbers
$ m analyze_numbers
$ adb push $ANDROID_PRODUCT_OUT/system/bin/analyze_numbers /data/local/tmp
$ adb shell /data/local/tmp/analyze_numbers
# ANCHOR_END: analyze_numbers
EOF
run_example <<EOF
# ANCHOR: helloworld_jni
$ m helloworld_jni
$ adb sync # requires adb root && adb remount
$ adb shell /system/bin/helloworld_jni
# ANCHOR_END: helloworld_jni
EOF

View File

@ -0,0 +1,10 @@
# Interoperability
Rust has excellent support for interoperability with other languages. This means
that you can
* Call Rust functions from other languages
* Call functions written in other languages from Rust
When you call functions in a foreign language we say that you're using a
_foreign function interface_, also known as FFI.

View File

@ -0,0 +1,13 @@
# With C++
The [CXX crate][1] makes it possible to do safe interoperability between Rust
and C++.
The overall approach looks like this:
<img src="cpp/overview.svg">
See the [CXX tutorial][2] for an full example of using this.
[1]: https://cxx.rs/
[2]: https://cxx.rs/tutorial.html

View File

@ -0,0 +1 @@
../../../../third_party/cxx/overview.svg

View File

@ -0,0 +1,39 @@
# Interoperability with Java
Java can load shared objects via [Java Native Interface
(JNI)](https://en.wikipedia.org/wiki/Java_Native_Interface). The [`jni`
crate](https://docs.rs/jni/) allows you to create a compatible library.
First, we create a Rust function to export to Java:
_interoperability/java/src/lib.rs_:
```rust,compile_fail
{{#include java/src/lib.rs:hello}}
```
_interoperability/java/Android.bp_:
```javascript
{{#include java/Android.bp:libhello_jni}}
```
Finally, we can call this function from Java:
_interoperability/java/HelloWorld.java_:
```java
{{#include java/HelloWorld.java:HelloWorld}}
```
_interoperability/java/Android.bp_:
```javascript
{{#include java/Android.bp:helloworld_jni}}
```
Finally, you can build, sync, and run the binary:
```shell
{{#include ../build_all.sh:helloworld_jni}}
```

View File

@ -0,0 +1,17 @@
// ANCHOR: libhello_jni
rust_ffi_shared {
name: "libhello_jni",
crate_name: "hello_jni",
srcs: ["src/lib.rs"],
rustlibs: ["libjni"],
}
// ANCHOR_END: libhello_jni
// ANCHOR: helloworld_jni
java_binary {
name: "helloworld_jni",
srcs: ["HelloWorld.java"],
main_class: "HelloWorld",
required: ["libhello_jni"],
}
// ANCHOR_END: helloworld_jni

View File

@ -0,0 +1,29 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: HelloWorld
class HelloWorld {
private static native String hello(String name);
static {
System.loadLibrary("hello_jni");
}
public static void main(String[] args) {
String output = HelloWorld.hello("Alice");
System.out.println(output);
}
}

View File

@ -0,0 +1,33 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: hello
//! Rust <-> Java FFI demo.
use jni::objects::{JClass, JString};
use jni::sys::jstring;
use jni::JNIEnv;
/// HelloWorld::hello method implementation.
#[no_mangle]
pub extern "system" fn Java_HelloWorld_hello(
env: JNIEnv,
_class: JClass,
name: JString,
) -> jstring {
let input: String = env.get_string(name).unwrap().into();
let greeting = format!("Hello, {input}!");
let output = env.new_string(greeting).unwrap();
output.into_inner()
}

View File

@ -0,0 +1,26 @@
# Interoperability with C
Rust has full support for linking object files with a C calling convention.
Similarly, you can export Rust functions and call them from C.
You can do it by hand if you want:
```rust
extern "C" {
fn abs(x: i32) -> i32;
}
fn main() {
let x = -42;
let abs_x = unsafe { abs(x) };
println!("{x}, {abs_x}");
}
```
We already saw this in the [Safe FFI Wrapper
exercise](../../exercises/day-3/safe-ffi-wrapper.md).
> This assumes full knowledge of the target platform. Not recommended for
> production.
We will look at better options next.

View File

@ -0,0 +1,75 @@
# Using Bindgen
The [bindgen](https://rust-lang.github.io/rust-bindgen/introduction.html) tool
can auto-generate bindings from a C header file.
First create a small C library:
_interoperability/bindgen/libbirthday.h_:
```c
{{#include bindgen/libbirthday.h:card}}
```
_interoperability/bindgen/libbirthday.c_:
```c
{{#include bindgen/libbirthday.c:print_card}}
```
Add this to your `Android.bp` file:
_interoperability/bindgen/Android.bp_:
```javascript
{{#include bindgen/Android.bp:libbirthday}}
```
Create a wrapper header file for the library (not strictly needed in this
example):
_interoperability/bindgen/libbirthday_wrapper.h_:
```c
{{#include bindgen/libbirthday_wrapper.h:include}}
```
You can now auto-generate the bindings:
_interoperability/bindgen/Android.bp_:
```javascript
{{#include bindgen/Android.bp:libbirthday_bindgen}}
```
Finally, we can use the bindings in our Rust program:
_interoperability/bindgen/Android.bp_:
```javascript
{{#include bindgen/Android.bp:print_birthday_card}}
```
_interoperability/bindgen/main.rs_:
```rust,compile_fail
{{#include bindgen/main.rs:main}}
```
Build, push, and run the binary on your device:
```shell
{{#include ../../build_all.sh:print_birthday_card}}
```
Finally, we can run auto-generated tests to ensure the bindings work:
_interoperability/bindgen/Android.bp_:
```javascript
{{#include bindgen/Android.bp:libbirthday_bindgen_test}}
```
```shell
{{#include ../../build_all.sh:libbirthday_bindgen_test}}
```

View File

@ -0,0 +1,36 @@
// ANCHOR: libbirthday
cc_library {
name: "libbirthday",
srcs: ["libbirthday.c"],
}
// ANCHOR_END: libbirthday
// ANCHOR: libbirthday_bindgen
rust_bindgen {
name: "libbirthday_bindgen",
crate_name: "birthday_bindgen",
wrapper_src: "libbirthday_wrapper.h",
source_stem: "bindings",
static_libs: ["libbirthday"],
}
// ANCHOR_END: libbirthday_bindgen
// ANCHOR: libbirthday_bindgen_test
rust_test {
name: "libbirthday_bindgen_test",
srcs: [":libbirthday_bindgen"],
crate_name: "libbirthday_bindgen_test",
test_suites: ["general-tests"],
auto_gen_config: true,
clippy_lints: "none", // Generated file, skip linting
lints: "none",
}
// ANCHOR_END: libbirthday_bindgen_test
// ANCHOR: print_birthday_card
rust_binary {
name: "print_birthday_card",
srcs: ["main.rs"],
rustlibs: ["libbirthday_bindgen"],
}
// ANCHOR_END: print_birthday_card

View File

@ -0,0 +1,20 @@
# Create a C library
_interoperability/c/libbirthday/Android.bp_:
```javascript
{{#include c/libbirthday/Android.bp:libbirthday}}
```
_interoperability/c/libbirthday/libbirthday.h_:
```c
{{#include c/libbirthday/libbirthday.h}}
```
_interoperability/c/libbirthday/libbirthday.c_:
```c
{{#include c/libbirthday/libbirthday.c}}
```

View File

@ -0,0 +1,26 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: print_card
#include <stdio.h>
#include "libbirthday.h"
void print_card(const card* card) {
printf("+--------------\n");
printf("| Happy Birthday %s!\n", card->name);
printf("| Congratulations with the %i years!\n", card->years);
printf("+--------------\n");
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: card
typedef struct card {
const char* name;
int years;
} card;
void print_card(const card* card);

View File

@ -0,0 +1,18 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: include
#include "libbirthday.h"

View File

@ -0,0 +1,29 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Bindgen demo.
use birthday_bindgen::{card, print_card};
fn main() {
let name = std::ffi::CString::new("Peter").unwrap();
let card = card {
name: name.as_ptr(),
years: 42,
};
unsafe {
print_card(&card as *const card);
}
}

View File

@ -0,0 +1 @@
# Calling Rust from C

View File

@ -0,0 +1,21 @@
# Handwritten FFI
We can declare external functions by hand:
```rust
extern "C" {
fn abs(x: i32) -> i32;
}
fn main() {
let x = -42;
let abs_x = unsafe { abs(x) };
println!("{x}, {abs_x}");
}
```
We already saw this in the [Safe FFI Wrapper
exercise](../../exercises/day-3/safe-ffi-wrapper.md).
> This assumes full knowledge of the target platform. Not recommended for
> production.

View File

@ -0,0 +1,42 @@
# Calling Rust
Exporting Rust functions and types to C is easy:
_interoperability/rust/libanalyze/analyze.rs_
```rust,editable
{{#include rust/libanalyze/analyze.rs:analyze_numbers}}
```
_interoperability/rust/libanalyze/analyze.h_
```c
{{#include rust/libanalyze/analyze.h:analyze_numbers}}
```
_interoperability/rust/libanalyze/Android.bp_
```javascript
{{#include rust/libanalyze/Android.bp}}
```
We can now call this from a C binary:
_interoperability/rust/analyze/main.c_
```c
{{#include rust/analyze/main.c:main}}
```
_interoperability/rust/analyze/Android.bp_
```javascript
{{#include rust/analyze/Android.bp}}
```
Build, push, and run the binary on your device:
```shell
{{#include ../../build_all.sh:analyze_numbers}}
```

View File

@ -0,0 +1,5 @@
cc_binary {
name: "analyze_numbers",
srcs: ["main.c"],
static_libs: ["libanalyze_ffi"],
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: main
#include "analyze.h"
int main() {
analyze_numbers(10, 20);
analyze_numbers(123, 123);
return 0;
}

View File

@ -0,0 +1,6 @@
rust_ffi {
name: "libanalyze_ffi",
crate_name: "analyze_ffi",
srcs: ["analyze.rs"],
include_dirs: ["."],
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ANCHOR: analyze_numbers
#ifndef ANALYSE_H
#define ANALYSE_H
extern "C" {
void analyze_numbers(int x, int y);
}
#endif

View File

@ -0,0 +1,29 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: analyze_numbers
//! Rust FFI demo.
#![deny(improper_ctypes_definitions)]
use std::os::raw::c_int;
/// Analyze the numbers.
#[no_mangle]
pub extern "C" fn analyze_numbers(x: c_int, y: c_int) {
if x < y {
println!("x ({x}) is smallest!");
} else {
println!("y ({y}) is probably larger than x ({x})");
}
}

31
src/android/logging.md Normal file
View File

@ -0,0 +1,31 @@
# Logging
You should use the `log` crate to automatically log to `logcat` (on-device) or
`stdout` (on-host):
_hello_rust_logs/Android.bp_:
```javascript
{{#include logging/Android.bp}}
```
_hello_rust_logs/src/main.rs_:
```rust,ignore
{{#include logging/src/main.rs:main}}
```
Build, push, and run the binary on your device:
```shell
{{#include build_all.sh:hello_rust_logs}}
```
The logs show up in `adb logcat`:
```shell
$ adb logcat -s rust
09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting program.
09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going fine.
09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went wrong!
```

View File

@ -0,0 +1,11 @@
rust_binary {
name: "hello_rust_logs",
crate_name: "hello_rust_logs",
srcs: ["src/main.rs"],
rustlibs: [
"liblog_rust",
"liblogger",
],
prefer_rlib: true,
host_supported: true,
}

View File

@ -0,0 +1,29 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ANCHOR: main
//! Rust logging demo.
use log::{debug, error};
/// Logs a greeting.
fn main() {
logger::init(
logger::Config::default()
.with_tag_on_device("rust")
.with_min_level(log::Level::Trace),
);
debug!("Starting program.");
error!("Something went wrong!");
}

13
src/android/setup.md Normal file
View File

@ -0,0 +1,13 @@
# Setup
We will be using an Android Virtual Device to test our code. Make sure you have
access to one or create a new one with:
```shell
$ source build/envsetup.sh
$ lunch aosp_cf_x86_64_phone-userdebug
$ acloud create
```
Please see the [Android Developer
Codelab](https://source.android.com/docs/setup/start) for details.

9
src/basic-syntax.md Normal file
View File

@ -0,0 +1,9 @@
# Basic Syntax
Much of the Rust syntax will be familiar to you from C or C++:
* Blocks and scopes are delimited by curly braces.
* Line comments are started with `//`, block comments are delimited by `/* ...
*/`.
* Keywords like `if` and `while` work the same.
* Variable assigment is done with `=`, comparison is done with `==`.

View File

@ -0,0 +1,26 @@
# Compound Types
| | Types | Literals |
|--------|---------------------|--------------------------|
| Arrays | `[T; N]` | `[20, 30, 40]`, `[0; 3]` |
| Tuples | `(T1, T2, T3, ...)` | `('x', 1.2, 0)` |
Array assignment and access:
```rust,editable
fn main() {
let mut a: [i8; 10] = [42; 10];
a[5] = 0;
println!("a: {:?}", a);
}
```
Tuple assignment and access:
```rust,editable
fn main() {
let t: (i8, bool) = (7, true);
println!("1st index: {}", t.0);
println!("2nd index: {}", t.1);
}
```

View File

@ -0,0 +1,23 @@
# Function Overloading
Overloading is not supported:
* Each function has a single implementation:
* Always takes a fixed number of parameters.
* Always takes a single set of parameter types.
* Default values are not supported:
* All call sites have the same number of arguments.
* Macros are sometimes used as an alternative.
However, function parameters can be generic:
```rust,editable
fn pick_one<T>(a: T, b: T) -> T {
if std::process::id() % 2 == 0 { a } else { b }
}
fn main() {
println!("coin toss: {}", pick_one("heads", "tails"));
println!("cash prize: {}", pick_one(500, 1000));
}
```

View File

@ -0,0 +1,31 @@
# Functions
A Rust version of the famous FizzBuzz interview question:
```rust,editable
fn main() {
fizzbuzz_to(20); // Defined below, no forward declaration needed
}
fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
if rhs == 0 {
return false; // Corner case, early return
}
lhs % rhs == 0 // The last expression is the return value
}
fn fizzbuzz(n: u32) -> () { // No return value means returning the unit type `()`
match (is_divisible_by(n, 3), is_divisible_by(n, 5)) {
(true, true) => println!("fizzbuzz"),
(true, false) => println!("fizz"),
(false, true) => println!("buzz"),
(false, false) => println!("{n}"),
}
}
fn fizzbuzz_to(n: u32) { // `-> ()` is normally omitted
for n in 1..=n {
fizzbuzz(n);
}
}
```

View File

@ -0,0 +1,30 @@
# Methods
Rust has methods, they are simply functions that are associated with a particular type. The
first argument of a method is an instance of the type it is associated with:
```rust,editable
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn inc_width(&mut self, delta: u32) {
self.width += delta;
}
}
fn main() {
let mut rect = Rectangle { width: 10, height: 5 };
println!("old area: {}", rect.area());
rect.inc_width(5);
println!("new area: {}", rect.area());
}
```
* We will look much more at methods in today's exercise and in tomorrow's class.

View File

@ -0,0 +1,19 @@
# Dangling References
Rust will statically forbid dangling references:
```rust,editable,compile_fail
fn main() {
let ref_x: &i32;
{
let x: i32 = 10;
ref_x = &x;
}
println!("ref_x: {ref_x}");
}
```
* A reference is said to "borrow" the value is refers to.
* Rust is tracking the lifetimes of all references to ensure they live long
enough.
* We will talk more about borrowing when we get to ownership.

View File

@ -0,0 +1,18 @@
# References
Like C++, Rust has references:
```rust,editable
fn main() {
let mut x: i32 = 10;
let ref_x: &mut i32 = &mut x;
*ref_x = 20;
println!("x: {x}");
}
```
Some differences from C++:
* We must dereference `ref_x` when assigning to it, similar to C pointers,
* Rust will auto-dereference in some cases, in particular when invoking
methods (try `count_ones`).

View File

@ -0,0 +1,18 @@
# Scalar Types
| | Types | Literals |
|------------------------|--------------------------------------------|-------------------------------|
| Signed integers | `i8`, `i16`, `i32`, `i64`, `i128`, `isize` | `-10`, `0`, `1_000`, `123i64` |
| Unsigned integers | `u8`, `u16`, `u32`, `u64`, `u128`, `usize` | `0`, `123`, `10u16` |
| Floating point numbers | `f32`, `f64` | `3.14`, `-10.0e20`, `2f32` |
| Strings | `&str` | `"foo"`, `r#"\\"#` |
| Unicode scalar values | `char` | `'a'`, `'α'`, `'∞'` |
| Byte strings | `&[u8]` | `b"abc"`, `br#" " "#` |
| Booleans | `bool` | `true`, `false` |
The types have widths as follows:
* `iN`, `uN`, and `fN` are _n_ bits wide,
* `isize` and `usize` are the width of a pointer,
* `char` is 32 bit wide,
* `bool` is 8 bit wide.

View File

@ -0,0 +1,21 @@
# Scopes and Shadowing
You can shadow variables, both those from outer scopes and variables from the
same scope:
```rust,editable
fn main() {
let a = 10;
println!("before: {a}");
{
let a = "hello";
println!("inner scope: {a}");
let a = true;
println!("shadowed in inner scope: {a}");
}
println!("after: {a}");
}
```

View File

@ -0,0 +1,16 @@
# Slices
A slice gives you a view into a larger collection:
```rust,editable
fn main() {
let a: [i32; 6] = [10, 20, 30, 40, 50, 60];
println!("a: {a:?}");
let s: &[i32] = &a[2..4];
println!("s: {s:?}");
}
```
* Slices borrow data from the sliced type.
* Question: What happens if you modify `a[3]`?

View File

@ -0,0 +1,39 @@
# Static and Constant Variables
Global state is managed with static and constant variables
## `const`
You can declare compile-time constants:
```rust,editable
const DIGEST_SIZE: usize = 3;
const ZERO: Option<u8> = Some(42);
fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] {
let mut digest = [ZERO.unwrap_or(0); DIGEST_SIZE];
for (idx, &b) in text.as_bytes().iter().enumerate() {
digest[idx % DIGEST_SIZE] = digest[idx % DIGEST_SIZE].wrapping_add(b);
}
digest
}
fn main() {
let digest = compute_digest("Hello");
println!("Digest: {digest:?}");
}
```
## `static`
You can also declare static variables:
```rust,editable
static BANNER: &str = "Welcome to RustOS 3.14";
fn main() {
println!("{BANNER}");
}
```
We will look at mutating static data in the chapter on Unsafe Rust.

View File

@ -0,0 +1,20 @@
# `String` vs `str`
We can now understand the two string types in Rust:
```rust,editable
fn main() {
let s1: &str = "Hello";
println!("s1: {s1}");
let mut s2: String = String::from("Hello ");
println!("s2: {s2}");
s2.push_str(s1);
println!("s2: {s2}");
}
```
Rust terminology:
* `&str` an immutable reference to a string slice.
* `String` a mutable string buffer

View File

@ -0,0 +1,22 @@
# Type Inference
Rust will look at how the variable is _used_ to determine the type:
```rust,editable
fn takes_u32(x: u32) {
println!("u32: {x}");
}
fn takes_i8(y: i8) {
println!("i8: {y}");
}
fn main() {
let x = 10;
let y = 20;
takes_u32(x);
takes_i8(y);
// takes_u32(y);
}
```

View File

@ -0,0 +1,13 @@
# Variables
Rust provides type safety via static typing. Variable bindings are immutable by
default:
```rust,editable
fn main() {
let x: i32 = 10;
println!("x: {x}");
// x = 20;
// println!("x: {x}");
}
```

18
src/cargo.md Normal file
View File

@ -0,0 +1,18 @@
# Using Cargo
When you start reading about Rust, you will soon meet Cargo, the standard tool
used in the Rust ecosystem to build and run Rust applications. Here we want to
give a brief overview of what Cargo is and how it fits into the wider ecosystem
and how it fits into this training.
On Debian/Ubuntu, you can install Cargo and the Rust source with
```shell
$ sudo apt install cargo rust-src
```
This will allow [rust-analyzer][1] to jump to the definitions. We suggest using
[VS Code][2] to edit the code (but any LSP compatible editor works).
[1]: https://rust-analyzer.github.io/
[2]: https://code.visualstudio.com/

20
src/cargo/code-samples.md Normal file
View File

@ -0,0 +1,20 @@
# Code Samples in This Training
For this training, we will mostly explore the Rust language through examples
which can be executed through your browser. This makes the setup much easier and
ensures a consistent experience for everyone.
Installing Cargo is still encouraged: it will make it easier for you to do the
exercises. On the last day, we will do a larger exercise which shows you how to
work with dependencies and for that you need Cargo.
The code blocks in this course are fully interactive:
```rust,editable
fn main() {
println!("Edit me!");
}
```
You can use <kbd>Ctrl-Enter</kbd> to execute the code when focus is in the text
box.

View File

@ -0,0 +1,66 @@
# Running Code Locally with Cargo
If you want to experiment with the code on your own system, then you will need
to first install Rust. Do this by following the [instructions in the Rust
Book][1]. This should give you a working `rustc` and `cargo`. At the time of
writing, the latest stable Rust release has these version numbers:
```shell
% rustc --version
rustc 1.61.0 (fe5b13d68 2022-05-18)
% cargo --version
cargo 1.61.0 (a028ae4 2022-04-29)
```
With this is in place, then follow these steps to build a Rust binary from one
of the examples in this training:
1. Click the "Copy to clipboard" button on the example you want to copy.
2. Use `cargo new exercise` to create a new `exercise/` directory for your code:
```shell
$ cargo new exercise
Created binary (application) `exercise` package
```
3. Navigate into `exercise/` and use `cargo run` to build and run your binary:
```shell
$ cd exercise
$ cargo run
Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)
Finished dev [unoptimized + debuginfo] target(s) in 0.75s
Running `target/debug/exercise`
Hello, world!
```
4. Replace the boiler-plate code in `src/main.rs` with your own code. For
example, using the example on the previous page, make `src/main.rs` look like
```rust
fn main() {
println!("Edit me!");
}
```
5. Use `cargo run` to build and run your updated binary:
```shell
$ cargo run
Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)
Finished dev [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/exercise`
Edit me!
```
6. Use `cargo check` to quickly check your project for errors, use `cargo build`
to compile it without running it. You will find the output in `target/debug/`
for a normal debug build. Use `cargo build --release` to produce an optimized
release build in `target/release/`.
7. You can add dependencies for your project by editing `Cargo.toml`. When you
run `cargo` commands, it will automatically download and compile missing
dependencies for you.
[1]: https://doc.rust-lang.org/book/ch01-01-installation.html

View File

@ -0,0 +1,17 @@
# The Rust Ecosystem
The Rust ecosystem consists of a number of tools, of which the main ones are:
* `rustc`: the Rust compiler which turn `.rs` files into binaries and other
intermediate formats.
* `cargo`: the Rust dependency manager and build tool. Cargo knows how to
download dependencies hosted on <https://crates.io> and it will pass them to
`rustc` when building your project. Cargo also comes with a built-in test
runner which is used to execute unit tests.
* `rustup`: the Rust toolchain installer and updater. This tool is used to
install and update `rustc` and `cargo` when new versions of Rust is released.
In addition, `rustup` can also download documentation for the standard
library. You can have multiple versions of Rust installed at once and `rustup`
will let you switch between them as needed.

8
src/concurrency.md Normal file
View File

@ -0,0 +1,8 @@
# Fearless Concurrency
Rust has full support for concurrency using OS threads with mutexes and
channels.
The Rust type system plays an important role in making many concurrency bugs
compile time bugs. This is often referred to a _fearless concurrency_ since you
can rely on the compiler to ensure correctness at runtime.

View File

@ -0,0 +1,23 @@
# Channels
Rust channels have two parts: a `Sender<T>` and a `Receiver<T>`. The two parts
are connected via the channel, but you only see the end-points.
```rust,editable
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
tx.send(10).unwrap();
tx.send(20).unwrap();
println!("Received: {:?}", rx.recv());
println!("Received: {:?}", rx.recv());
let tx2 = tx.clone();
tx2.send(30).unwrap();
println!("Received: {:?}", rx.recv());
}
```

View File

@ -0,0 +1,27 @@
# Bounded Channels
Bounded and synchronous channels make `send` block the current thread:
```rust,editable
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::sync_channel(3);
thread::spawn(move || {
let thread_id = thread::current().id();
for i in 1..10 {
tx.send(format!("Message {i}")).unwrap();
println!("{thread_id:?}: sent Message {i}");
}
println!("{thread_id:?}: done");
});
thread::sleep(Duration::from_millis(100));
for msg in rx.iter() {
println!("Main: got {}", msg);
}
}
```

View File

@ -0,0 +1,27 @@
# Unbounded Channels
You get an unbounded and asynchronous channel with `mpsc::channel()`:
```rust,editable
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let thread_id = thread::current().id();
for i in 1..10 {
tx.send(format!("Message {i}")).unwrap();
println!("{thread_id:?}: sent Message {i}");
}
println!("{thread_id:?}: done");
});
thread::sleep(Duration::from_millis(100));
for msg in rx.iter() {
println!("Main: got {}", msg);
}
}
```

View File

@ -0,0 +1,33 @@
# Scoped Threads
Normal threads cannot borrow from their environment:
```rust,editable,compile_fail
use std::thread;
fn main() {
let s = String::from("Hello");
thread::spawn(|| {
println!("Length: {}", s.len());
});
}
```
However, you can use a [scoped thread][1] for this:
```rust,editable
use std::thread;
fn main() {
let s = String::from("Hello");
thread::scope(|scope| {
scope.spawn(|| {
println!("Length: {}", s.len());
});
});
}
```
[1]: https://doc.rust-lang.org/std/thread/fn.scope.html

View File

@ -0,0 +1,11 @@
# `Send` and `Sync`
How does Rust know to forbid shared access across thread? The answer is in two traits:
* [`Send`][1]: a type `T` is `Send` if it is safe to move a `T` across a thread
boundary.
* [`Sync`][2]: a type `T` is `Sync` if it is safe to move a `&T` across a thread
boundary.
[1]: https://doc.rust-lang.org/std/marker/trait.Send.html
[2]: https://doc.rust-lang.org/std/marker/trait.Sync.html

View File

@ -0,0 +1,41 @@
# Examples
## `Send + Sync`
Most types you come across are `Send + Sync`:
* `i8`, `f32`, `bool`, `char`, `&str`, ...
* `(T1, T2)`, `[T; N]`, `&[T]`, `struct { x: T }`, ...
* `String`, `Option<T>`, `Vec<T>`, `Box<T>`, ...
* `Arc<T>`: Explicitly thread-safe via atomic reference count.
* `Mutex<T>`: Explicitly thread-safe via internal locking.
* `AtomicBool`, `AtomicU8`, ...: Uses special atomic instructions.
The generic types are typically `Send + Sync` when the type parameters are
`Send + Sync`.
## `Send + !Sync`
These types can be moved to other threads, but they're not thread-safe.
Typically because of interior mutability:
* `mpsc::Sender<T>`
* `mpsc::Receiver<T>`
* `Cell<T>`
* `RefCell<T>`
## `!Send + Sync`
These types are thread-safe, but they cannot be moved to another thread:
* `MutexGuard<T>`: Uses OS level primitives which must be deallocated on the
thread which created them.
## `!Send + !Sync`
These types are not thread-safe and cannot be moved to other threads:
* `Rc<T>`: each `Rc<T>` has a reference to an `RcBox<T>`, which contains a
non-atomic reference count.
* `*const T`, `*mut T`: Rust that there are special lifetime considerations for the
pointer.

View File

@ -0,0 +1,9 @@
# `Send`
> A type `T` is [`Send`][1] if it is safe to move a `T` value to another thread.
The effect of moving ownership to another thread is that _destructors_ will run
in that thread. So the question is when you can allocate a value in one thread
and deallocate it in another.
[1]: https://doc.rust-lang.org/std/marker/trait.Send.html

View File

@ -0,0 +1,10 @@
# `Sync`
> A type `T` is [`Sync`][1] if it is safe to access a `T` value from multiple
> threads at the same time.
More precisely, the definitions is
> `T` is `Sync` if and only if `&T` is `Send`
[1]: https://doc.rust-lang.org/std/marker/trait.Sync.html

View File

@ -0,0 +1,11 @@
# Shared State
Rust uses the type system to enforce synchronization of shared data. This is
primarily done via two types:
* [`Arc<T>`][1], atomic reference counted `T`: handled sharing between threads and
takes care to deallocate `T` when the last thread exists,
* [`Mutex<T>`][2]: ensures mutual exclusion for to the `T` value.
[1]: https://doc.rust-lang.org/std/sync/struct.Arc.html
[2]: https://doc.rust-lang.org/std/sync/struct.Mutex.html

View File

@ -0,0 +1,25 @@
# `Arc`
[`Arc<T>`][1] allows shared read-only access via its `clone` method:
```rust,editable
use std::thread;
use std::sync::Arc;
fn main() {
let v = Arc::new(vec![10, 20, 30]);
let mut handles = Vec::new();
for _ in 1..5 {
let v = v.clone();
handles.push(thread::spawn(move || {
let thread_id = thread::current().id();
println!("{thread_id:?}: {v:?}");
}));
}
handles.into_iter().for_each(|h| h.join().unwrap());
println!("v: {v:?}");
}
```
[1]: https://doc.rust-lang.org/std/sync/struct.Arc.html

View File

@ -0,0 +1,19 @@
# Example
Let us see `Arc` and `Mutex` in action:
```rust,editable,compile_fail
use std::thread;
// use std::sync::{Arc, Mutex};
fn main() {
let mut v = vec![10, 20, 30];
let handle = thread::spawn(|| {
v.push(10);
});
v.push(1000);
handle.join().unwrap();
println!("v: {v:?}");
}
```

View File

@ -0,0 +1,28 @@
# `Mutex`
[`Mutex<T>`][1] ensures mutual exclusion _and_ allows mutable access to `T`
behind a read-only interface:
```rust,editable
use std::sync::Mutex;
fn main() {
let v: Mutex<Vec<i32>> = Mutex::new(vec![10, 20, 30]);
println!("v: {:?}", v.lock().unwrap());
{
let v: &Mutex<Vec<i32>> = &v;
let mut guard = v.lock().unwrap();
guard.push(40);
}
println!("v: {:?}", v.lock().unwrap());
}
```
Notice how we have a [`impl<T: Send> Sync for Mutex<T>`][2] blanket
implementation.
[1]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
[2]: https://doc.rust-lang.org/std/sync/struct.Mutex.html#impl-Sync-for-Mutex%3CT%3E
[3]: https://doc.rust-lang.org/std/sync/struct.Arc.html

View File

@ -0,0 +1,26 @@
# Threads
Rust threads work similarly to threads in other languages:
```rust,editable
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 1..10 {
println!("Count in thread: {i}!");
thread::sleep(Duration::from_millis(5));
}
});
for i in 1..5 {
println!("Main thread: {i}");
thread::sleep(Duration::from_millis(5));
}
}
```
* Threads are all daemon threads, the main thread does not wait for them.
* Thread panics are independent of each other.
* Panics can carry a payload, which can be unpacked with `downcast_ref`.

6
src/control-flow.md Normal file
View File

@ -0,0 +1,6 @@
# Control Flow
As we have seen, `if` is an expression in Rust. It is used to conditionally
evaluate one of two blocks, but the blocks can have a value which then becomes
the value of the `if` expression. Other control flow expressions work similarly
in Rust.

View File

@ -0,0 +1,36 @@
# Blocks
A block in Rust has a value and a type: the value is the last expression of the
block:
```rust,editable
fn main() {
let x = {
let y = 10;
println!("y: {y}");
let z = {
let w = {
3 + 4
};
println!("w: {w}");
y * w
};
println!("z: {z}");
z - y
};
println!("x: {x}");
}
```
The same rule is used for functions: the value of the function body is the
return value:
```rust,editable
fn double(x: i32) -> i32 {
x + x
}
fn main() {
println!("doubled: {}", double(7));
}
```

View File

@ -0,0 +1,25 @@
# `break` and `continue`
If you want to exit a loop early, use `break`, if you want to immediately start
the next iteration use `continue`. Both `continue` and `break` can optionally
take a label argument which is used to break out of nested loops:
```rust,editable
fn main() {
let v = vec![10, 20, 30];
let mut iter = v.into_iter();
'outer: while let Some(x) = iter.next() {
println!("x: {x}");
let mut i = 0;
while i < x {
println!("x: {x}, i: {i}");
i += 1;
if i == 3 {
break 'outer;
}
}
}
}
```
In this case we break the outer loop after 3 iterations of the inner loop.

View File

@ -0,0 +1,16 @@
# `for` expressions
The `for` expression is closely related to the `while let` expression. It will
automatically call `into_iter()` on the expression and then iterate over it:
```rust,editable
fn main() {
let v = vec![10, 20, 30];
for x in v {
println!("x: {x}");
}
}
```
You can use `break` and `continue` here as usual.

View File

@ -0,0 +1,27 @@
# `if` expressions
You use `if` very similarly to how you would in other languages:
```rust,editable
fn main() {
let mut x = 10;
if x % 2 == 0 {
x = x / 2;
} else {
x = 3 * x + 1;
}
}
```
In addition, you can use it as an expression. This does the same as above:
```rust,editable
fn main() {
let mut x = 10;
x = if x % 2 == 0 {
x / 2
} else {
3 * x + 1
};
}
```

View File

@ -0,0 +1,17 @@
# `if let` expressions
If you want to match a value against a pattern, you can use `if let`:
```rust,editable
fn main() {
let arg = std::env::args().next();
if let Some(value) = arg {
println!("Program name: {value}");
} else {
println!("Missing name?");
}
}
```
See [pattern matching](../pattern-matching.md) for more details on patterns in
Rust.

View File

@ -0,0 +1,21 @@
# `loop` expressions
Finally, there is a `loop` keyword which creates an endless loop. Here you must
either `break` or `return` to stop the loop:
```rust,editable
fn main() {
let mut x = 10;
loop {
x = if x % 2 == 0 {
x / 2
} else {
3 * x + 1
};
if x == 1 {
break;
}
}
println!("Final x: {x}");
}
```

View File

@ -0,0 +1,23 @@
# `match` expressions
The `match` keyword is used to match a value against one or more patterns. In
that sense, it works like a series of `if let` expressions:
```rust,editable
fn main() {
match std::env::args().next().as_deref() {
Some("cat") => println!("Will do cat things"),
Some("ls") => println!("Will ls some files"),
Some("mv") => println!("Let's move some files"),
Some("rm") => println!("Uh, dangerous!"),
None => println!("Hmm, no program name?"),
_ => println!("Unknown program name!"),
}
}
```
Like `if let`, each match arm must have the same type. The type is the last
expression of the block, if any. In the example above, the type is `()`.
See [pattern matching](../pattern-matching.md) for more details on patterns in
Rust.

Some files were not shown because too many files have changed in this diff Show More