summaryrefslogtreecommitdiffstats
path: root/src/doc/rust-by-example
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:29 +0000
commit631cd5845e8de329d0e227aaa707d7ea228b8f8f (patch)
treea1b87c8f8cad01cf18f7c5f57a08f102771ed303 /src/doc/rust-by-example
parentAdding debian version 1.69.0+dfsg1-1. (diff)
downloadrustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.tar.xz
rustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.zip
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/doc/rust-by-example')
-rw-r--r--src/doc/rust-by-example/src/crates/lib.md2
-rw-r--r--src/doc/rust-by-example/src/flow_control/match/guard.md1
-rw-r--r--src/doc/rust-by-example/src/hello/print.md2
-rw-r--r--src/doc/rust-by-example/src/hello/print/fmt.md8
-rw-r--r--src/doc/rust-by-example/src/macros.md4
-rw-r--r--src/doc/rust-by-example/src/primitives/array.md2
-rw-r--r--src/doc/rust-by-example/src/scope/lifetime.md2
-rw-r--r--src/doc/rust-by-example/src/scope/raii.md3
-rw-r--r--src/doc/rust-by-example/src/std/panic.md3
-rw-r--r--src/doc/rust-by-example/src/std_misc/file/read_lines.md79
-rw-r--r--src/doc/rust-by-example/src/unsafe/asm.md38
-rw-r--r--src/doc/rust-by-example/src/variable_bindings/mut.md3
12 files changed, 99 insertions, 48 deletions
diff --git a/src/doc/rust-by-example/src/crates/lib.md b/src/doc/rust-by-example/src/crates/lib.md
index 44593f3bb..729ccb890 100644
--- a/src/doc/rust-by-example/src/crates/lib.md
+++ b/src/doc/rust-by-example/src/crates/lib.md
@@ -2,6 +2,8 @@
Let's create a library, and then see how to link it to another crate.
+In `rary.rs`:
+
```rust,ignore
pub fn public_function() {
println!("called rary's `public_function()`");
diff --git a/src/doc/rust-by-example/src/flow_control/match/guard.md b/src/doc/rust-by-example/src/flow_control/match/guard.md
index 63008a743..af81f64c9 100644
--- a/src/doc/rust-by-example/src/flow_control/match/guard.md
+++ b/src/doc/rust-by-example/src/flow_control/match/guard.md
@@ -3,6 +3,7 @@
A `match` *guard* can be added to filter the arm.
```rust,editable
+#[allow(dead_code)]
enum Temperature {
Celsius(i32),
Fahrenheit(i32),
diff --git a/src/doc/rust-by-example/src/hello/print.md b/src/doc/rust-by-example/src/hello/print.md
index 55f6ed520..d578337ad 100644
--- a/src/doc/rust-by-example/src/hello/print.md
+++ b/src/doc/rust-by-example/src/hello/print.md
@@ -39,7 +39,6 @@ fn main() {
println!("Base 16 (hexadecimal): {:x}", 69420); // 10f2c
println!("Base 16 (hexadecimal): {:X}", 69420); // 10F2C
-
// You can right-justify text with a specified width. This will
// output " 1". (Four white spaces and a "1", for a total width of 5.)
println!("{number:>5}", number=1);
@@ -51,7 +50,6 @@ fn main() {
// You can use named arguments in the format specifier by appending a `$`.
println!("{number:0>width$}", number=1, width=5);
-
// Rust even checks to make sure the correct number of arguments are used.
println!("My name is {0}, {1} {0}", "Bond");
// FIXME ^ Add the missing argument: "James"
diff --git a/src/doc/rust-by-example/src/hello/print/fmt.md b/src/doc/rust-by-example/src/hello/print/fmt.md
index 5332b4903..c3c78f6b1 100644
--- a/src/doc/rust-by-example/src/hello/print/fmt.md
+++ b/src/doc/rust-by-example/src/hello/print/fmt.md
@@ -49,17 +49,17 @@ fn main() {
City { name: "Dublin", lat: 53.347778, lon: -6.259722 },
City { name: "Oslo", lat: 59.95, lon: 10.75 },
City { name: "Vancouver", lat: 49.25, lon: -123.1 },
- ].iter() {
- println!("{}", *city);
+ ] {
+ println!("{}", city);
}
for color in [
Color { red: 128, green: 255, blue: 90 },
Color { red: 0, green: 3, blue: 254 },
Color { red: 0, green: 0, blue: 0 },
- ].iter() {
+ ] {
// Switch this to use {} once you've added an implementation
// for fmt::Display.
- println!("{:?}", *color);
+ println!("{:?}", color);
}
}
```
diff --git a/src/doc/rust-by-example/src/macros.md b/src/doc/rust-by-example/src/macros.md
index 3f12fcc41..f01cf8dc7 100644
--- a/src/doc/rust-by-example/src/macros.md
+++ b/src/doc/rust-by-example/src/macros.md
@@ -16,12 +16,12 @@ macro_rules! say_hello {
// `()` indicates that the macro takes no argument.
() => {
// The macro will expand into the contents of this block.
- println!("Hello!");
+ println!("Hello!")
};
}
fn main() {
- // This call will expand into `println!("Hello");`
+ // This call will expand into `println!("Hello")`
say_hello!()
}
```
diff --git a/src/doc/rust-by-example/src/primitives/array.md b/src/doc/rust-by-example/src/primitives/array.md
index 3811bb6d7..9cec24d69 100644
--- a/src/doc/rust-by-example/src/primitives/array.md
+++ b/src/doc/rust-by-example/src/primitives/array.md
@@ -63,7 +63,7 @@ fn main() {
}
}
- // Out of bound indexing causes runtime error.
+ // Out of bound indexing causes compile time error.
//println!("{}", xs[5]);
}
```
diff --git a/src/doc/rust-by-example/src/scope/lifetime.md b/src/doc/rust-by-example/src/scope/lifetime.md
index 01c4bf405..68b42d380 100644
--- a/src/doc/rust-by-example/src/scope/lifetime.md
+++ b/src/doc/rust-by-example/src/scope/lifetime.md
@@ -1,6 +1,6 @@
# Lifetimes
-A *lifetime* is a construct the compiler (or more specifically, its *borrow
+A *lifetime* is a construct of the compiler (or more specifically, its *borrow
checker*) uses to ensure all borrows are valid. Specifically, a variable's
lifetime begins when it is created and ends when it is destroyed. While
lifetimes and scopes are often referred to together, they are not the same.
diff --git a/src/doc/rust-by-example/src/scope/raii.md b/src/doc/rust-by-example/src/scope/raii.md
index 7b6bca618..6d94b0713 100644
--- a/src/doc/rust-by-example/src/scope/raii.md
+++ b/src/doc/rust-by-example/src/scope/raii.md
@@ -41,6 +41,8 @@ fn main() {
Of course, we can double check for memory errors using [`valgrind`][valgrind]:
+<!-- REUSE-IgnoreStart -->
+<!-- Prevent REUSE from parsing the copyright statement in the sample code -->
```shell
$ rustc raii.rs && valgrind ./raii
==26873== Memcheck, a memory error detector
@@ -58,6 +60,7 @@ $ rustc raii.rs && valgrind ./raii
==26873== For counts of detected and suppressed errors, rerun with: -v
==26873== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
```
+<!-- REUSE-IgnoreEnd -->
No leaks here!
diff --git a/src/doc/rust-by-example/src/std/panic.md b/src/doc/rust-by-example/src/std/panic.md
index b22000494..d08d1f4e2 100644
--- a/src/doc/rust-by-example/src/std/panic.md
+++ b/src/doc/rust-by-example/src/std/panic.md
@@ -34,6 +34,8 @@ fn main() {
Let's check that `panic!` doesn't leak memory.
+<!-- REUSE-IgnoreStart -->
+<!-- Prevent REUSE from parsing the copyright statement in the sample code -->
```shell
$ rustc panic.rs && valgrind ./panic
==4401== Memcheck, a memory error detector
@@ -52,3 +54,4 @@ thread '<main>' panicked at 'division by zero', panic.rs:5
==4401== For counts of detected and suppressed errors, rerun with: -v
==4401== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
```
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/doc/rust-by-example/src/std_misc/file/read_lines.md b/src/doc/rust-by-example/src/std_misc/file/read_lines.md
index 641eb972a..216b0181c 100644
--- a/src/doc/rust-by-example/src/std_misc/file/read_lines.md
+++ b/src/doc/rust-by-example/src/std_misc/file/read_lines.md
@@ -1,44 +1,51 @@
# `read_lines`
-## Beginner friendly method
-This method is NOT efficient. It's here for beginners
-who can't understand the efficient method yet.
+## A naive approach
-```rust,no_run
-use std::fs::File;
-use std::io::{ self, BufRead, BufReader };
+This might be a reasonable first attempt for a beginner's first
+implementation for reading lines from a file.
-fn read_lines(filename: String) -> io::Lines<BufReader<File>> {
- // Open the file in read-only mode.
- let file = File::open(filename).unwrap();
- // Read the file line by line, and return an iterator of the lines of the file.
- return io::BufReader::new(file).lines();
-}
+```rust,norun
+use std::fs::read_to_string;
-fn main() {
- // Stores the iterator of lines of the file in lines variable.
- let lines = read_lines("./hosts".to_string());
- // Iterate over the lines of the file, and in this case print them.
- for line in lines {
- println!("{}", line.unwrap());
+fn read_lines(filename: &str) -> Vec<String> {
+ let mut result = Vec::new();
+
+ for line in read_to_string(filename).unwrap().lines() {
+ result.push(line.to_string())
}
+
+ result
}
```
-Running this program simply prints the lines individually.
-```shell
-$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts
-$ rustc read_lines.rs && ./read_lines
-127.0.0.1
-192.168.0.1
+Since the method `lines()` returns an iterator over the lines in the file,
+we can also perform a map inline and collect the results, yielding a more
+concise and fluent expression.
+
+```rust,norun
+use std::fs::read_to_string;
+
+fn read_lines(filename: &str) -> Vec<String> {
+ read_to_string(filename)
+ .unwrap() // panic on possible file-reading errors
+ .lines() // split the string into an iterator of string slices
+ .map(String::from) // make each slice into a string
+ .collect() // gather them together into a vector
+}
```
-## Efficient method
-The method `lines()` returns an iterator over the lines
-of a file.
+Note that in both examples above, we must convert the `&str` reference
+returned from `lines()` to the owned type `String`, using `.to_string()`
+and `String::from` respectively.
-`File::open` expects a generic, `AsRef<Path>`. That's what
-`read_lines()` expects as input.
+## A more efficient approach
+
+Here we pass ownership of the open `File` to a `BufReader` struct. `BufReader` uses an internal
+buffer to reduce intermediate allocations.
+
+We also update `read_lines` to return an iterator instead of allocating new
+`String` objects in memory for each line.
```rust,no_run
use std::fs::File;
@@ -46,8 +53,8 @@ use std::io::{self, BufRead};
use std::path::Path;
fn main() {
- // File hosts must exist in current path before this produces output
- if let Ok(lines) = read_lines("./hosts") {
+ // File hosts.txt must exist in the current path
+ if let Ok(lines) = read_lines("./hosts.txt") {
// Consumes the iterator, returns an (Optional) String
for line in lines {
if let Ok(ip) = line {
@@ -68,11 +75,15 @@ where P: AsRef<Path>, {
Running this program simply prints the lines individually.
```shell
-$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts
+$ echo -e "127.0.0.1\n192.168.0.1\n" > hosts.txt
$ rustc read_lines.rs && ./read_lines
127.0.0.1
192.168.0.1
```
-This process is more efficient than creating a `String` in memory
-especially working with larger files. \ No newline at end of file
+(Note that since `File::open` expects a generic `AsRef<Path>` as argument, we define our
+generic `read_lines()` method with the same generic constraint, using the `where` keyword.)
+
+This process is more efficient than creating a `String` in memory with all of the file's
+contents. This can especially cause performance issues when working with larger files.
+
diff --git a/src/doc/rust-by-example/src/unsafe/asm.md b/src/doc/rust-by-example/src/unsafe/asm.md
index 7ad6e0c5e..1a3fab904 100644
--- a/src/doc/rust-by-example/src/unsafe/asm.md
+++ b/src/doc/rust-by-example/src/unsafe/asm.md
@@ -18,11 +18,13 @@ Inline assembly is currently supported on the following architectures:
Let us start with the simplest possible example:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
unsafe {
asm!("nop");
}
+# }
```
This will insert a NOP (no operation) instruction into the assembly generated by the compiler.
@@ -36,6 +38,7 @@ Now inserting an instruction that does nothing is rather boring. Let us do somet
actually acts on data:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let x: u64;
@@ -43,6 +46,7 @@ unsafe {
asm!("mov {}, 5", out(reg) x);
}
assert_eq!(x, 5);
+# }
```
This will write the value `5` into the `u64` variable `x`.
@@ -61,6 +65,7 @@ the template and will read the variable from there after the inline assembly fin
Let us see another example that also uses an input:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let i: u64 = 3;
@@ -74,6 +79,7 @@ unsafe {
);
}
assert_eq!(o, 8);
+# }
```
This will add `5` to the input in variable `i` and write the result to variable `o`.
@@ -97,6 +103,7 @@ readability, and allows reordering instructions without changing the argument or
We can further refine the above example to avoid the `mov` instruction:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let mut x: u64 = 3;
@@ -104,6 +111,7 @@ unsafe {
asm!("add {0}, 5", inout(reg) x);
}
assert_eq!(x, 8);
+# }
```
We can see that `inout` is used to specify an argument that is both input and output.
@@ -112,6 +120,7 @@ This is different from specifying an input and output separately in that it is g
It is also possible to specify different variables for the input and output parts of an `inout` operand:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let x: u64 = 3;
@@ -120,6 +129,7 @@ unsafe {
asm!("add {0}, 5", inout(reg) x => y);
}
assert_eq!(y, 8);
+# }
```
## Late output operands
@@ -135,6 +145,7 @@ There is also a `inlateout` variant of this specifier.
Here is an example where `inlateout` *cannot* be used in `release` mode or other optimized cases:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let mut a: u64 = 4;
@@ -150,6 +161,7 @@ unsafe {
);
}
assert_eq!(a, 12);
+# }
```
The above could work well in unoptimized cases (`Debug` mode), but if you want optimized performance (`release` mode or other optimized cases), it could not work.
@@ -158,6 +170,7 @@ That is because in optimized cases, the compiler is free to allocate the same re
However the following example can use `inlateout` since the output is only modified after all input registers have been read:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let mut a: u64 = 4;
@@ -166,6 +179,7 @@ unsafe {
asm!("add {0}, {1}", inlateout(reg) a, in(reg) b);
}
assert_eq!(a, 8);
+# }
```
As you can see, this assembly fragment will still work correctly if `a` and `b` are assigned to the same register.
@@ -177,12 +191,14 @@ Therefore, Rust inline assembly provides some more specific constraint specifier
While `reg` is generally available on any architecture, explicit registers are highly architecture specific. E.g. for x86 the general purpose registers `eax`, `ebx`, `ecx`, `edx`, `ebp`, `esi`, and `edi` among others can be addressed by their name.
```rust,no_run
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let cmd = 0xd1;
unsafe {
asm!("out 0x64, eax", in("eax") cmd);
}
+# }
```
In this example we call the `out` instruction to output the content of the `cmd` variable to port `0x64`. Since the `out` instruction only accepts `eax` (and its sub registers) as operand we had to use the `eax` constraint specifier.
@@ -192,6 +208,7 @@ In this example we call the `out` instruction to output the content of the `cmd`
Consider this example which uses the x86 `mul` instruction:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
fn mul(a: u64, b: u64) -> u128 {
@@ -211,6 +228,7 @@ fn mul(a: u64, b: u64) -> u128 {
((hi as u128) << 64) + lo as u128
}
+# }
```
This uses the `mul` instruction to multiply two 64-bit inputs with a 128-bit result.
@@ -229,6 +247,7 @@ We need to tell the compiler about this since it may need to save and restore th
```rust
use std::arch::asm;
+# #[cfg(target_arch = "x86_64")]
fn main() {
// three entries of four bytes each
let mut name_buf = [0_u8; 12];
@@ -262,6 +281,9 @@ fn main() {
let name = core::str::from_utf8(&name_buf).unwrap();
println!("CPU Manufacturer ID: {}", name);
}
+
+# #[cfg(not(target_arch = "x86_64"))]
+# fn main() {}
```
In the example above we use the `cpuid` instruction to read the CPU manufacturer ID.
@@ -269,13 +291,14 @@ This instruction writes to `eax` with the maximum supported `cpuid` argument and
Even though `eax` is never read we still need to tell the compiler that the register has been modified so that the compiler can save any values that were in these registers before the asm. This is done by declaring it as an output but with `_` instead of a variable name, which indicates that the output value is to be discarded.
-This code also works around the limitation that `ebx` is a reserved register by LLVM. That means that LLVM assumes that it has full control over the register and it must be restored to its original state before exiting the asm block, so it cannot be used as an input or output **except** if the compiler uses it to fulfill a general register class (e.g. `in(reg)`). This makes `reg` operands dangerous when using reserved registers as we could unknowingly corrupt out input or output because they share the same register.
+This code also works around the limitation that `ebx` is a reserved register by LLVM. That means that LLVM assumes that it has full control over the register and it must be restored to its original state before exiting the asm block, so it cannot be used as an input or output **except** if the compiler uses it to fulfill a general register class (e.g. `in(reg)`). This makes `reg` operands dangerous when using reserved registers as we could unknowingly corrupt our input or output because they share the same register.
-To work around this we use `rdi` to store the pointer to the output array, save `ebx` via `push`, read from `ebx` inside the asm block into the array and then restoring `ebx` to its original state via `pop`. The `push` and `pop` use the full 64-bit `rbx` version of the register to ensure that the entire register is saved. On 32 bit targets the code would instead use `ebx` in the `push`/`pop`.
+To work around this we use `rdi` to store the pointer to the output array, save `ebx` via `push`, read from `ebx` inside the asm block into the array and then restore `ebx` to its original state via `pop`. The `push` and `pop` use the full 64-bit `rbx` version of the register to ensure that the entire register is saved. On 32 bit targets the code would instead use `ebx` in the `push`/`pop`.
This can also be used with a general register class to obtain a scratch register for use inside the asm code:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
// Multiply x by 6 using shifts and adds
@@ -291,6 +314,7 @@ unsafe {
);
}
assert_eq!(x, 4 * 6);
+# }
```
## Symbol operands and ABI clobbers
@@ -300,6 +324,7 @@ By default, `asm!` assumes that any register not specified as an output will hav
[`clobber_abi`]: ../../reference/inline-assembly.html#abi-clobbers
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
extern "C" fn foo(arg: i32) -> i32 {
@@ -325,6 +350,7 @@ fn call_foo(arg: i32) -> i32 {
result
}
}
+# }
```
## Register template modifiers
@@ -336,6 +362,7 @@ By default the compiler will always choose the name that refers to the full regi
This default can be overridden by using modifiers on the template string operands, just like you would with format strings:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let mut x: u16 = 0xab;
@@ -345,6 +372,7 @@ unsafe {
}
assert_eq!(x, 0xabab);
+# }
```
In this example, we use the `reg_abcd` register class to restrict the register allocator to the 4 legacy x86 registers (`ax`, `bx`, `cx`, `dx`) of which the first two bytes can be addressed independently.
@@ -361,6 +389,7 @@ You have to manually use the memory address syntax specified by the target archi
For example, on x86/x86_64 using Intel assembly syntax, you should wrap inputs/outputs in `[]` to indicate they are memory operands:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
fn load_fpu_control_word(control: u16) {
@@ -368,6 +397,7 @@ fn load_fpu_control_word(control: u16) {
asm!("fldcw [{}]", in(reg) &control, options(nostack));
}
}
+# }
```
## Labels
@@ -383,6 +413,7 @@ As a consequence, you should only use GNU assembler **numeric** [local labels] i
Moreover, on x86 when using the default Intel syntax, due to [an LLVM bug], you shouldn't use labels exclusively made of `0` and `1` digits, e.g. `0`, `11` or `101010`, as they may end up being interpreted as binary values. Using `options(att_syntax)` will avoid any ambiguity, but that affects the syntax of the _entire_ `asm!` block. (See [Options](#options), below, for more on `options`.)
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let mut a = 0;
@@ -400,6 +431,7 @@ unsafe {
);
}
assert_eq!(a, 5);
+# }
```
This will decrement the `{0}` register value from 10 to 3, then add 2 and store it in `a`.
@@ -419,6 +451,7 @@ By default, an inline assembly block is treated the same way as an external FFI
Let's take our previous example of an `add` instruction:
```rust
+# #[cfg(target_arch = "x86_64")] {
use std::arch::asm;
let mut a: u64 = 4;
@@ -431,6 +464,7 @@ unsafe {
);
}
assert_eq!(a, 8);
+# }
```
Options can be provided as an optional final argument to the `asm!` macro. We specified three options here:
diff --git a/src/doc/rust-by-example/src/variable_bindings/mut.md b/src/doc/rust-by-example/src/variable_bindings/mut.md
index 0925132f2..c75f034ec 100644
--- a/src/doc/rust-by-example/src/variable_bindings/mut.md
+++ b/src/doc/rust-by-example/src/variable_bindings/mut.md
@@ -15,9 +15,8 @@ fn main() {
println!("After mutation: {}", mutable_binding);
- // Error!
+ // Error! Cannot assign a new value to an immutable variable
_immutable_binding += 1;
- // FIXME ^ Comment out this line
}
```