update rust
This commit is contained in:
parent
932389a689
commit
838e762a47
1 changed files with 215 additions and 2 deletions
|
@ -4,7 +4,7 @@ source: https://doc.rust-lang.org/book
|
|||
mime: "text/rust"
|
||||
extension: "rs"
|
||||
obj: concept
|
||||
rev: 2024-07-17
|
||||
rev: 2024-08-29
|
||||
---
|
||||
|
||||
# Rust
|
||||
|
@ -209,6 +209,7 @@ impl Person {
|
|||
|
||||
### Comments
|
||||
Rust has support for comments. There are regular comments, multiline comments and documentation comments. Documentation comments get used when generating documation via `cargo doc` and inside the IDE.
|
||||
|
||||
```rust
|
||||
// This is a comment
|
||||
|
||||
|
@ -224,6 +225,7 @@ comment
|
|||
/// # Example
|
||||
///
|
||||
/// If you place a rust code block here, you can provide examples on how to use this function and `cargo test` will automatically run this code block when testing.
|
||||
/// If you don't want cargo to run the test inside doc comments, add "ignore" to the head of the code block.
|
||||
fn do_something() {
|
||||
|
||||
}
|
||||
|
@ -606,6 +608,7 @@ Some functions on them include:
|
|||
- `take(n)`: An iterator that only iterates over the first `n` iterations of `iter`.
|
||||
- `take_while(f)`: An iterator that only accepts elements while `predicate` returns `true`.
|
||||
- `zip(a, b)`: An iterator that iterates two other iterators simultaneously.
|
||||
- `sum()`: Sum up an iterators values
|
||||
|
||||
### Standard Library
|
||||
Rust, a systems programming language known for its focus on safety and performance, comes with a rich standard library that provides a wide range of modules to handle common tasks.
|
||||
|
@ -705,7 +708,184 @@ Common Rust Macros:
|
|||
- `unreachable!()`: Indicates unreachable code.
|
||||
- `vec![]`: Creates a Vec containing the arguments.
|
||||
|
||||
## `unsafe` Rust
|
||||
### Procedural Macros
|
||||
Procedural macros allow creating syntax extensions as execution of a function. Procedural macros come in one of three flavors:
|
||||
- Function-like macros - `custom!(...)`
|
||||
- Derive macros - `#[derive(CustomDerive)]`
|
||||
- Attribute macros - `#[CustomAttribute]`
|
||||
|
||||
Procedural macros allow you to run code at compile time that operates over Rust syntax, both consuming and producing Rust syntax. You can sort of think of procedural macros as functions from an AST to another AST.
|
||||
|
||||
As functions, they must either return syntax, panic, or loop endlessly. Returned syntax either replaces or adds the syntax depending on the kind of procedural macro. Panics are caught by the compiler and are turned into a compiler error. Endless loops are not caught by the compiler which hangs the compiler.
|
||||
|
||||
Procedural macros have two ways of reporting errors. The first is to panic. The second is to emit a `compile_error!` macro invocation.
|
||||
|
||||
Procedural macros must be in their own crate. Use this `Cargo.toml` as a start:
|
||||
|
||||
```toml
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "2.0"
|
||||
quote = "1.0"
|
||||
```
|
||||
|
||||
Defining the macro:
|
||||
```rust
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
#[proc_macro_derive(HelloMacro)]
|
||||
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
|
||||
// Construct a representation of Rust code as a syntax tree
|
||||
// that we can manipulate
|
||||
let ast = syn::parse(input).unwrap();
|
||||
|
||||
// Build the trait implementation
|
||||
let name = &ast.ident;
|
||||
let gen = quote! {
|
||||
impl HelloMacro for #name {
|
||||
fn hello_macro() {
|
||||
println!("Hello, Macro! My name is {}!", stringify!(#name));
|
||||
}
|
||||
}
|
||||
};
|
||||
gen.into()
|
||||
}
|
||||
```
|
||||
|
||||
We’ve introduced three new crates: `proc_macro`, `syn`, and `quote`. The `proc_macro` crate comes with Rust, so we didn’t need to add that to the dependencies in `Cargo.toml`. The `proc_macro` crate is the compiler’s API that allows us to read and manipulate Rust code from our code. The `syn` crate parses Rust code from a string into a data structure that we can perform operations on. The `quote` crate turns `syn` data structures back into Rust code. These crates make it much simpler to parse any sort of Rust code we might want to handle.
|
||||
|
||||
The `hello_macro_derive` function will be called when a user of our library specifies `#[derive(HelloMacro)]` on a type. This is possible because we’ve annotated the `hello_macro_derive` function here with `proc_macro_derive` and specified the name `HelloMacro`, which matches our trait name; this is the convention most procedural macros follow.
|
||||
|
||||
The `quote!` macro lets us define the Rust code that we want to return. The compiler expects something different to the direct result of the `quote!` macro’s execution, so we need to convert it to a `TokenStream`. We do this by calling the `.into()` method, which consumes this intermediate representation and returns a value of the required `TokenStream` type.
|
||||
|
||||
The `quote!` macro also provides some very cool templating mechanics: we can enter `#name`, and `quote!` will replace it with the value in the variable name. You can even do some repetition similar to the way regular macros work. Check out the [quote crate’s docs](https://docs.rs/quote/latest/quote/) for a thorough introduction.
|
||||
|
||||
#### Attribute-like macros
|
||||
Attribute-like macros are similar to custom derive macros, but instead of generating code for the derive attribute, they allow you to create new attributes. They’re also more flexible: derive only works for structs and enums; attributes can be applied to other items as well, such as functions. Here’s an example of using an attribute-like macro: say you have an attribute named route that annotates functions when using a web application framework:
|
||||
|
||||
```rust
|
||||
#[route(GET, "/")]
|
||||
fn index() {}
|
||||
```
|
||||
|
||||
This `#[route]` attribute would be defined by the framework as a procedural macro. The signature of the macro definition function would look like this:
|
||||
|
||||
```rust
|
||||
#[proc_macro_attribute]
|
||||
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {}
|
||||
```
|
||||
|
||||
Here, we have two parameters of type `TokenStream`. The first is for the contents of the attribute: the `GET, "/"` part. The second is the body of the item the attribute is attached to: in this case, `fn index() {}` and the rest of the function’s body.
|
||||
|
||||
Other than that, attribute-like macros work the same way as custom derive macros: you create a crate with the `proc-macro` crate type and implement a function that generates the code you want!
|
||||
|
||||
#### Function-like macros
|
||||
Function-like macros define macros that look like function calls. Similarly to `macro_rules!` macros, they’re more flexible than functions; for example, they can take an unknown number of arguments. However, `macro_rules!` macros can be defined only using the match-like syntax. Function-like macros take a `TokenStream` parameter and their definition manipulates that `TokenStream` using Rust code as the other two types of procedural macros do. An example of a function-like macro is an `sql!` macro that might be called like so:
|
||||
|
||||
```rust
|
||||
let sql = sql!(SELECT * FROM posts WHERE id=1);
|
||||
```
|
||||
|
||||
This macro would parse the SQL statement inside it and check that it’s syntactically correct, which is much more complex processing than a `macro_rules!` macro can do. The `sql!` macro would be defined like this:
|
||||
```rust
|
||||
#[proc_macro]
|
||||
pub fn sql(input: TokenStream) -> TokenStream {}
|
||||
```
|
||||
|
||||
This definition is similar to the custom derive macro’s signature: we receive the tokens that are inside the parentheses and return the code we wanted to generate.
|
||||
|
||||
## Conditional Compilation
|
||||
Conditionally compiled source code is source code that may or may not be considered a part of the source code depending on certain conditions. Source code can be conditionally compiled using the attributes `cfg` and `cfg_attr` and the built-in `cfg` macro. These conditions are based on the target architecture of the compiled crate, arbitrary values passed to the compiler, and a few other miscellaneous things further described below in detail.
|
||||
|
||||
Each form of conditional compilation takes a configuration predicate that evaluates to true or false. The predicate is one of the following:
|
||||
- A configuration option. It is true if the option is set and false if it is unset.
|
||||
- `all()` with a comma separated list of configuration predicates. It is false if at least one predicate is false. If there are no predicates, it is true.
|
||||
- `any()` with a comma separated list of configuration predicates. It is true if at least one predicate is true. If there are no predicates, it is false.
|
||||
- `not()` with a configuration predicate. It is true if its predicate is false and false if its predicate is true.
|
||||
|
||||
Configuration options are names and key-value pairs that are either set or unset. Names are written as a single identifier such as, for example, unix. Key-value pairs are written as an identifier, `=`, and then a string. For example, `target_arch = "x86_64"` is a configuration option.
|
||||
|
||||
> **Note**: For `rustc`, arbitrary-set configuration options are set using the `--cfg` flag.
|
||||
|
||||
The cfg attribute conditionally includes the thing it is attached to based on a configuration predicate.
|
||||
If the predicate is true, the thing is rewritten to not have the cfg attribute on it. If the predicate is false, the thing is removed from the source code.
|
||||
|
||||
Some examples on functions:
|
||||
```rust
|
||||
// The function is only included in the build when compiling for macOS
|
||||
#[cfg(target_os = "macos")]
|
||||
fn macos_only() {
|
||||
// ...
|
||||
}
|
||||
|
||||
// This function is only included when either foo or bar is defined
|
||||
#[cfg(any(foo, bar))]
|
||||
fn needs_foo_or_bar() {
|
||||
// ...
|
||||
}
|
||||
|
||||
// This function is only included when compiling for a unixish OS with a 32-bit
|
||||
// architecture
|
||||
#[cfg(all(unix, target_pointer_width = "32"))]
|
||||
fn on_32bit_unix() {
|
||||
// ...
|
||||
}
|
||||
|
||||
// This function is only included when foo is not defined
|
||||
#[cfg(not(foo))]
|
||||
fn needs_not_foo() {
|
||||
// ...
|
||||
}
|
||||
|
||||
// This function is only included when the panic strategy is set to unwind
|
||||
#[cfg(panic = "unwind")]
|
||||
fn when_unwinding() {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The `cfg_attr` attribute conditionally includes attributes based on a configuration predicate.
|
||||
When the configuration predicate is true, this attribute expands out to the attributes listed after the predicate. For example, the following module will either be found at `linux.rs` or `windows.rs` based on the target.
|
||||
|
||||
```rust
|
||||
#[cfg_attr(target_os = "linux", path = "linux.rs")]
|
||||
#[cfg_attr(windows, path = "windows.rs")]
|
||||
mod os;
|
||||
```
|
||||
|
||||
Zero, one, or more attributes may be listed. Multiple attributes will each be expanded into separate attributes. For example:
|
||||
|
||||
```rust
|
||||
#[cfg_attr(feature = "magic", sparkles, crackles)]
|
||||
fn bewitched() {}
|
||||
|
||||
// When the `magic` feature flag is enabled, the above will expand to:
|
||||
#[sparkles]
|
||||
#[crackles]
|
||||
fn bewitched() {}
|
||||
```
|
||||
|
||||
> **Note**: The `cfg_attr` can expand to another `cfg_attr`. For example, `#[cfg_attr(target_os = "linux", cfg_attr(feature = "multithreaded", some_other_attribute))]` is valid. This example would be equivalent to `#[cfg_attr(all(target_os = "linux", feature ="multithreaded"), some_other_attribute)]`.
|
||||
|
||||
The built-in `cfg` macro takes in a single configuration predicate and evaluates to the true literal when the predicate is true and the false literal when it is false.
|
||||
|
||||
For example:
|
||||
```rust
|
||||
let machine_kind = if cfg!(unix) {
|
||||
"unix"
|
||||
} else if cfg!(windows) {
|
||||
"windows"
|
||||
} else {
|
||||
"unknown"
|
||||
};
|
||||
|
||||
println!("I'm running on a {} machine!", machine_kind);
|
||||
```
|
||||
|
||||
## unsafe Rust
|
||||
Rust is focused strongly on safety, but sometimes doing something dangerous is necessary. In this case you can use the `unsafe` keyword. `unsafe` should be used only when needed as it may cause undefinied behaviour, but when debugging you can solely focus on your `unsafe` blocks as all potential dangerous operations are neatly packaged in them.
|
||||
|
||||
There are two types of using `unsafe`:
|
||||
|
@ -727,6 +907,39 @@ unsafe fn write_to_serial_unchecked(data: &[u8]) {
|
|||
}
|
||||
```
|
||||
|
||||
### Inline Assembly
|
||||
Support for inline assembly is provided via the `asm!` and `global_asm!` macros. It can be used to embed handwritten assembly in the assembly output generated by the compiler.
|
||||
|
||||
```rust
|
||||
use std::arch::asm;
|
||||
|
||||
// Multiply x by 6 using shifts and adds
|
||||
let mut x: u64 = 4;
|
||||
unsafe {
|
||||
asm!(
|
||||
"mov {tmp}, {x}",
|
||||
"shl {tmp}, 1",
|
||||
"shl {x}, 2",
|
||||
"add {x}, {tmp}",
|
||||
x = inout(reg) x,
|
||||
tmp = out(reg) _,
|
||||
);
|
||||
}
|
||||
assert_eq!(x, 4 * 6);
|
||||
```
|
||||
|
||||
The assembler template uses the same syntax as format strings (i.e. placeholders are specified by curly braces). The corresponding arguments are accessed in order, by index, or by name. However, implicit named arguments (introduced by RFC #2795) are not supported.
|
||||
|
||||
An `asm!` invocation may have one or more template string arguments; an `asm!` with multiple template string arguments is treated as if all the strings were concatenated with a `\n` between them. The expected usage is for each template string argument to correspond to a line of assembly code. All template string arguments must appear before any other arguments.
|
||||
|
||||
As with format strings, positional arguments must appear before named arguments and explicit register operands.
|
||||
|
||||
Explicit register operands cannot be used by placeholders in the template string. All other named and positional operands must appear at least once in the template string, otherwise a compiler error is generated.
|
||||
|
||||
The exact assembly code syntax is target-specific and opaque to the compiler except for the way operands are substituted into the template string to form the code passed to the assembler.
|
||||
|
||||
Currently, all supported targets follow the assembly code syntax used by LLVM's internal assembler which usually corresponds to that of the GNU assembler (GAS). On x86, the .intel_syntax noprefix mode of GAS is used by default. On ARM, the .syntax unified mode is used. These targets impose an additional restriction on the assembly code: any assembler state (e.g. the current section which can be changed with `.section`) must be restored to its original value at the end of the asm string. Assembly code that does not conform to the GAS syntax will result in assembler-specific behavior. Further constraints on the directives used by inline assembly are indicated by Directives Support.
|
||||
|
||||
## [Crates](https://lib.rs/)
|
||||
- [anyhow](https://lib.rs/crates/anyhow): Flexible concrete Error type built on `std::error::Error`
|
||||
- [itertools](https://lib.rs/crates/itertools): Extra iterator adaptors, iterator methods, free functions, and macros
|
||||
|
|
Loading…
Reference in a new issue