You've already forked comprehensive-rust
mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-06-24 17:56:45 +02:00
Publish Comprehensive Rust 🦀
This commit is contained in:
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.
|
18
src/control-flow/while-expressions.md
Normal file
18
src/control-flow/while-expressions.md
Normal file
@ -0,0 +1,18 @@
|
||||
# `while` expressions
|
||||
|
||||
The `while` keyword works very similar to other languages:
|
||||
|
||||
```rust,editable
|
||||
fn main() {
|
||||
let mut x = 10;
|
||||
while x != 1 {
|
||||
x = if x % 2 == 0 {
|
||||
x / 2
|
||||
} else {
|
||||
3 * x + 1
|
||||
};
|
||||
}
|
||||
println!("Final x: {x}");
|
||||
}
|
||||
```
|
||||
|
22
src/control-flow/while-let-expressions.md
Normal file
22
src/control-flow/while-let-expressions.md
Normal file
@ -0,0 +1,22 @@
|
||||
# `while let` expressions
|
||||
|
||||
Like with `if`, there is a `while let` variant which repeatedly tests a value
|
||||
against a pattern:
|
||||
|
||||
```rust,editable
|
||||
fn main() {
|
||||
let v = vec![10, 20, 30];
|
||||
let mut iter = v.into_iter();
|
||||
|
||||
while let Some(x) = iter.next() {
|
||||
println!("x: {x}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here the iterator returned by `v.iter()` will return a `Option<i32>` on every
|
||||
call to `next()`. It returns `Some(x)` until it is done, after which it will
|
||||
return `None`. The `while let` lets us keep iterating through all items.
|
||||
|
||||
See [pattern matching](../pattern-matching.md) for more details on patterns in
|
||||
Rust.
|
28
src/credits.md
Normal file
28
src/credits.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Credits
|
||||
|
||||
The material here builds on top of the many great sources of Rust documentation.
|
||||
See the page on [other resources](other-resources.md) for a full list of useful
|
||||
resources.
|
||||
|
||||
The material of Comprehensive Rust is licensed under the terms of the Apache 2.0
|
||||
license, please see [`LICENSE.txt`](../LICENSE.txt) for details.
|
||||
|
||||
## Rust by Example
|
||||
|
||||
Some examples and exercises have been copied and adapted from [Rust by
|
||||
Example](https://doc.rust-lang.org/rust-by-example/). Please see the
|
||||
`third_party/rust-by-example/` directory for details, including the license
|
||||
terms.
|
||||
|
||||
## Rust on Exercism
|
||||
|
||||
Some exercises have been copied and adapted from [Rust on
|
||||
Exercism](https://exercism.org/tracks/rust). Please see the
|
||||
`third_party/rust-on-exercism/` directory for details, including the license
|
||||
terms.
|
||||
|
||||
## CXX
|
||||
|
||||
The [Interoperability with C++](android/interoperability/cpp.md) section uses an
|
||||
image from [CXX](https://cxx.rs/). Please see the `third_party/cxx/` directory
|
||||
for details, including the license terms.
|
29
src/enums.md
Normal file
29
src/enums.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Enums
|
||||
|
||||
The `enum` keyword allows the creation of a type which has a few
|
||||
different variants:
|
||||
|
||||
```rust,editable
|
||||
fn generate_random_number() -> i32 {
|
||||
4 // Chosen by fair dice roll. Guaranteed to be random.
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CoinFlip {
|
||||
Heads,
|
||||
Tails,
|
||||
}
|
||||
|
||||
fn flip_coin() -> CoinFlip {
|
||||
let random_number = generate_random_number();
|
||||
if random_number % 2 == 0 {
|
||||
return CoinFlip::Heads;
|
||||
} else {
|
||||
return CoinFlip::Tails;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("You got: {:?}", flip_coin());
|
||||
}
|
||||
```
|
25
src/enums/sizes.md
Normal file
25
src/enums/sizes.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Enum Sizes
|
||||
|
||||
Rust enums are packed tightly, taking constraints due to alignment into account:
|
||||
|
||||
```rust,editable
|
||||
use std::mem::{align_of, size_of};
|
||||
|
||||
macro_rules! dbg_size {
|
||||
($t:ty) => {
|
||||
println!("{}: size {} bytes, align: {} bytes",
|
||||
stringify!($t), size_of::<$t>(), align_of::<$t>());
|
||||
};
|
||||
}
|
||||
|
||||
enum Foo {
|
||||
A,
|
||||
B,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
dbg_size!(Foo);
|
||||
}
|
||||
```
|
||||
|
||||
* See the [Rust Reference](https://doc.rust-lang.org/reference/type-layout.html).
|
8
src/enums/variant-payloads.md
Normal file
8
src/enums/variant-payloads.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Variant Payloads
|
||||
|
||||
You can define richer enums where the variants carry data. You can then use the
|
||||
`match` statement to extract the data from each variant:
|
||||
|
||||
```rust,editable
|
||||
{{#include ../../third_party/rust-by-example/webevent.rs}}
|
||||
```
|
7
src/error-handling.md
Normal file
7
src/error-handling.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Error Handling
|
||||
|
||||
Error handling in Rust is done using explicit control flow:
|
||||
|
||||
* Functions that can have errors list this in their return type,
|
||||
* There are no exceptions.
|
||||
|
51
src/error-handling/converting-error-types.md
Normal file
51
src/error-handling/converting-error-types.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Converting Error Types
|
||||
|
||||
The actual expansion of `?` is a little more complicated than previously indicated:
|
||||
|
||||
```rust,ignore
|
||||
expression?
|
||||
```
|
||||
|
||||
actually becomes
|
||||
|
||||
```rust,ignore
|
||||
match expression {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Err(From::from(err)),
|
||||
}
|
||||
```
|
||||
|
||||
The `From::from` call here means we attempt to convert the error type to the
|
||||
type returned by the function:
|
||||
|
||||
```rust,editable
|
||||
use std::{fs, io};
|
||||
use std::io::Read;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ReadUsernameError {
|
||||
IoError(io::Error),
|
||||
EmptyUsername(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for ReadUsernameError {
|
||||
fn from(err: io::Error) -> ReadUsernameError {
|
||||
ReadUsernameError::IoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_username(path: &str) -> Result<String, ReadUsernameError> {
|
||||
let mut username = String::with_capacity(100);
|
||||
fs::File::open(path)?.read_to_string(&mut username)?;
|
||||
if username.is_empty() {
|
||||
return Err(ReadUsernameError::EmptyUsername(String::from(path)));
|
||||
}
|
||||
Ok(username)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
//fs::write("config.dat", "").unwrap();
|
||||
let username = read_username("config.dat");
|
||||
println!("username: {username:?}");
|
||||
}
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user