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:
commit
c212a473ba
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/book/
|
||||
/target/
|
28
CONTRIBUTING.md
Normal file
28
CONTRIBUTING.md
Normal 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
49
Cargo.toml
Normal 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
202
LICENSE.txt
Normal 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
40
README.md
Normal 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
23
book.toml
Normal 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
87
ga4.js
Normal 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
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
||||
max_width = 90
|
237
src/SUMMARY.md
Normal file
237
src/SUMMARY.md
Normal 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
6
src/android.md
Normal 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
7
src/android/aidl.md
Normal 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.
|
38
src/android/aidl/birthday_service/Android.bp
Normal file
38
src/android/aidl/birthday_service/Android.bp
Normal 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
|
10
src/android/aidl/birthday_service/aidl/Android.bp
Normal file
10
src/android/aidl/birthday_service/aidl/Android.bp
Normal 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,
|
||||
},
|
||||
},
|
||||
}
|
@ -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);
|
||||
}
|
42
src/android/aidl/birthday_service/src/client.rs
Normal file
42
src/android/aidl/birthday_service/src/client.rs
Normal 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(())
|
||||
}
|
31
src/android/aidl/birthday_service/src/lib.rs
Normal file
31
src/android/aidl/birthday_service/src/lib.rs
Normal 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!"
|
||||
))
|
||||
}
|
||||
}
|
33
src/android/aidl/birthday_service/src/server.rs
Normal file
33
src/android/aidl/birthday_service/src/server.rs
Normal 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()
|
||||
}
|
14
src/android/aidl/changing.md
Normal file
14
src/android/aidl/changing.md
Normal 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);
|
||||
}
|
||||
```
|
24
src/android/aidl/client.md
Normal file
24
src/android/aidl/client.md
Normal 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!
|
||||
```
|
29
src/android/aidl/deploy.md
Normal file
29
src/android/aidl/deploy.md
Normal 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.!..... ')
|
||||
```
|
15
src/android/aidl/implementation.md
Normal file
15
src/android/aidl/implementation.md
Normal 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}}
|
||||
```
|
18
src/android/aidl/interface.md
Normal file
18
src/android/aidl/interface.md
Normal 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.
|
15
src/android/aidl/server.md
Normal file
15
src/android/aidl/server.md
Normal 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
27
src/android/bpfmt.sh
Executable 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
|
16
src/android/build-rules.md
Normal file
16
src/android/build-rules.md
Normal 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.
|
23
src/android/build-rules/binary.md
Normal file
23
src/android/build-rules/binary.md
Normal 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!
|
||||
```
|
5
src/android/build-rules/binary/Android.bp
Normal file
5
src/android/build-rules/binary/Android.bp
Normal file
@ -0,0 +1,5 @@
|
||||
rust_binary {
|
||||
name: "hello_rust",
|
||||
crate_name: "hello_rust",
|
||||
srcs: ["src/main.rs"],
|
||||
}
|
21
src/android/build-rules/binary/src/main.rs
Normal file
21
src/android/build-rules/binary/src/main.rs
Normal 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!");
|
||||
}
|
37
src/android/build-rules/library.md
Normal file
37
src/android/build-rules/library.md
Normal 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!
|
||||
```
|
16
src/android/build-rules/library/Android.bp
Normal file
16
src/android/build-rules/library/Android.bp
Normal 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"],
|
||||
}
|
21
src/android/build-rules/library/src/lib.rs
Normal file
21
src/android/build-rules/library/src/lib.rs
Normal 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!")
|
||||
}
|
24
src/android/build-rules/library/src/main.rs
Normal file
24
src/android/build-rules/library/src/main.rs
Normal 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
130
src/android/build_all.sh
Executable 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
|
10
src/android/interoperability.md
Normal file
10
src/android/interoperability.md
Normal 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.
|
13
src/android/interoperability/cpp.md
Normal file
13
src/android/interoperability/cpp.md
Normal 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
|
1
src/android/interoperability/cpp/overview.svg
Symbolic link
1
src/android/interoperability/cpp/overview.svg
Symbolic link
@ -0,0 +1 @@
|
||||
../../../../third_party/cxx/overview.svg
|
39
src/android/interoperability/java.md
Normal file
39
src/android/interoperability/java.md
Normal 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}}
|
||||
```
|
17
src/android/interoperability/java/Android.bp
Normal file
17
src/android/interoperability/java/Android.bp
Normal 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
|
29
src/android/interoperability/java/HelloWorld.java
Normal file
29
src/android/interoperability/java/HelloWorld.java
Normal 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);
|
||||
}
|
||||
}
|
33
src/android/interoperability/java/src/lib.rs
Normal file
33
src/android/interoperability/java/src/lib.rs
Normal 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()
|
||||
}
|
26
src/android/interoperability/with-c.md
Normal file
26
src/android/interoperability/with-c.md
Normal 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.
|
75
src/android/interoperability/with-c/bindgen.md
Normal file
75
src/android/interoperability/with-c/bindgen.md
Normal 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}}
|
||||
```
|
36
src/android/interoperability/with-c/bindgen/Android.bp
Normal file
36
src/android/interoperability/with-c/bindgen/Android.bp
Normal 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
|
20
src/android/interoperability/with-c/bindgen/c-library.md
Normal file
20
src/android/interoperability/with-c/bindgen/c-library.md
Normal 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}}
|
||||
```
|
||||
|
26
src/android/interoperability/with-c/bindgen/libbirthday.c
Normal file
26
src/android/interoperability/with-c/bindgen/libbirthday.c
Normal 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");
|
||||
}
|
23
src/android/interoperability/with-c/bindgen/libbirthday.h
Normal file
23
src/android/interoperability/with-c/bindgen/libbirthday.h
Normal 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);
|
@ -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"
|
29
src/android/interoperability/with-c/bindgen/main.rs
Normal file
29
src/android/interoperability/with-c/bindgen/main.rs
Normal 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);
|
||||
}
|
||||
}
|
1
src/android/interoperability/with-c/calling-rust.md
Normal file
1
src/android/interoperability/with-c/calling-rust.md
Normal file
@ -0,0 +1 @@
|
||||
# Calling Rust from C
|
21
src/android/interoperability/with-c/hand-written.md
Normal file
21
src/android/interoperability/with-c/hand-written.md
Normal 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.
|
42
src/android/interoperability/with-c/rust.md
Normal file
42
src/android/interoperability/with-c/rust.md
Normal 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}}
|
||||
```
|
@ -0,0 +1,5 @@
|
||||
cc_binary {
|
||||
name: "analyze_numbers",
|
||||
srcs: ["main.c"],
|
||||
static_libs: ["libanalyze_ffi"],
|
||||
}
|
24
src/android/interoperability/with-c/rust/analyze/main.c
Normal file
24
src/android/interoperability/with-c/rust/analyze/main.c
Normal 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;
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
rust_ffi {
|
||||
name: "libanalyze_ffi",
|
||||
crate_name: "analyze_ffi",
|
||||
srcs: ["analyze.rs"],
|
||||
include_dirs: ["."],
|
||||
}
|
@ -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
|
@ -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
31
src/android/logging.md
Normal 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!
|
||||
```
|
11
src/android/logging/Android.bp
Normal file
11
src/android/logging/Android.bp
Normal 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,
|
||||
}
|
29
src/android/logging/src/main.rs
Normal file
29
src/android/logging/src/main.rs
Normal 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
13
src/android/setup.md
Normal 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
9
src/basic-syntax.md
Normal 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 `==`.
|
26
src/basic-syntax/compound-types.md
Normal file
26
src/basic-syntax/compound-types.md
Normal 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);
|
||||
}
|
||||
```
|
23
src/basic-syntax/functions-interlude.md
Normal file
23
src/basic-syntax/functions-interlude.md
Normal 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));
|
||||
}
|
||||
```
|
31
src/basic-syntax/functions.md
Normal file
31
src/basic-syntax/functions.md
Normal 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);
|
||||
}
|
||||
}
|
||||
```
|
30
src/basic-syntax/methods.md
Normal file
30
src/basic-syntax/methods.md
Normal 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.
|
19
src/basic-syntax/references-dangling.md
Normal file
19
src/basic-syntax/references-dangling.md
Normal 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.
|
18
src/basic-syntax/references.md
Normal file
18
src/basic-syntax/references.md
Normal 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`).
|
18
src/basic-syntax/scalar-types.md
Normal file
18
src/basic-syntax/scalar-types.md
Normal 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.
|
21
src/basic-syntax/scopes-shadowing.md
Normal file
21
src/basic-syntax/scopes-shadowing.md
Normal 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}");
|
||||
}
|
||||
```
|
16
src/basic-syntax/slices.md
Normal file
16
src/basic-syntax/slices.md
Normal 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]`?
|
39
src/basic-syntax/static-and-const.md
Normal file
39
src/basic-syntax/static-and-const.md
Normal 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.
|
20
src/basic-syntax/string-slices.md
Normal file
20
src/basic-syntax/string-slices.md
Normal 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
|
22
src/basic-syntax/type-inference.md
Normal file
22
src/basic-syntax/type-inference.md
Normal 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);
|
||||
}
|
||||
```
|
13
src/basic-syntax/variables.md
Normal file
13
src/basic-syntax/variables.md
Normal 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
18
src/cargo.md
Normal 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
20
src/cargo/code-samples.md
Normal 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.
|
66
src/cargo/running-locally.md
Normal file
66
src/cargo/running-locally.md
Normal 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
|
17
src/cargo/rust-ecosystem.md
Normal file
17
src/cargo/rust-ecosystem.md
Normal 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
8
src/concurrency.md
Normal 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.
|
23
src/concurrency/channels.md
Normal file
23
src/concurrency/channels.md
Normal 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());
|
||||
}
|
||||
```
|
27
src/concurrency/channels/bounded.md
Normal file
27
src/concurrency/channels/bounded.md
Normal 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);
|
||||
}
|
||||
}
|
||||
```
|
27
src/concurrency/channels/unbounded.md
Normal file
27
src/concurrency/channels/unbounded.md
Normal 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);
|
||||
}
|
||||
}
|
||||
```
|
33
src/concurrency/scoped-threads.md
Normal file
33
src/concurrency/scoped-threads.md
Normal 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
|
11
src/concurrency/send-sync.md
Normal file
11
src/concurrency/send-sync.md
Normal 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
|
41
src/concurrency/send-sync/examples.md
Normal file
41
src/concurrency/send-sync/examples.md
Normal 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.
|
9
src/concurrency/send-sync/send.md
Normal file
9
src/concurrency/send-sync/send.md
Normal 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
|
10
src/concurrency/send-sync/sync.md
Normal file
10
src/concurrency/send-sync/sync.md
Normal 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
|
11
src/concurrency/shared_state.md
Normal file
11
src/concurrency/shared_state.md
Normal 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
|
25
src/concurrency/shared_state/arc.md
Normal file
25
src/concurrency/shared_state/arc.md
Normal 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
|
19
src/concurrency/shared_state/example.md
Normal file
19
src/concurrency/shared_state/example.md
Normal 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:?}");
|
||||
}
|
||||
```
|
28
src/concurrency/shared_state/mutex.md
Normal file
28
src/concurrency/shared_state/mutex.md
Normal 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
|
26
src/concurrency/threads.md
Normal file
26
src/concurrency/threads.md
Normal 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
6
src/control-flow.md
Normal 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.
|
36
src/control-flow/blocks.md
Normal file
36
src/control-flow/blocks.md
Normal 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));
|
||||
}
|
||||
```
|
25
src/control-flow/break-continue.md
Normal file
25
src/control-flow/break-continue.md
Normal 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.
|
16
src/control-flow/for-expressions.md
Normal file
16
src/control-flow/for-expressions.md
Normal 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.
|
27
src/control-flow/if-expressions.md
Normal file
27
src/control-flow/if-expressions.md
Normal 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
|
||||
};
|
||||
}
|
||||
```
|
17
src/control-flow/if-let-expressions.md
Normal file
17
src/control-flow/if-let-expressions.md
Normal 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.
|
21
src/control-flow/loop-expressions.md
Normal file
21
src/control-flow/loop-expressions.md
Normal 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}");
|
||||
}
|
||||
```
|
23
src/control-flow/match-expressions.md
Normal file
23
src/control-flow/match-expressions.md
Normal 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
Loading…
x
Reference in New Issue
Block a user