mirror of
https://github.com/nkbai/book.git
synced 2026-08-24 20:23:29 +08:00
Merge #87
87: Have mdBook not wrap code snippets in "fn main() {}" r=therealprof a=adamgreen
This is the same change as in commit c5caa3b but for the instances I noticed in the rest of the chapters.
Before this change, clicking on the icon to copy a code snippet to the clipboard would result in something like:
```rust
fn main() {
// Exception handler for the SysTick (System Timer) exception
fn SysTick() {
// ..
}
}
```
After this change, the following would be copied to the clipboard instead:
```rust
// Exception handler for the SysTick (System Timer) exception
fn SysTick() {
// ..
}
```
Co-authored-by: Adam Green <adamgreen@users.noreply.github.com>
This commit is contained in:
@@ -27,7 +27,7 @@ The `alloc` crate is shipped with the standard Rust distribution. To import the
|
||||
crate you can directly `use` it *without* declaring it as a dependency in your
|
||||
`Cargo.toml` file.
|
||||
|
||||
``` rust
|
||||
``` rust,ignore
|
||||
#![feature(alloc)]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -46,7 +46,7 @@ implement a simple bump pointer allocator and use that as the global allocator.
|
||||
However, we *strongly* suggest you use a battle tested allocator from crates.io
|
||||
in your program instead of this allocator.
|
||||
|
||||
``` rust
|
||||
``` rust,ignore
|
||||
// Bump pointer allocator implementation
|
||||
|
||||
extern crate cortex_m;
|
||||
@@ -103,7 +103,7 @@ Apart from selecting a global allocator the user will also have to define how
|
||||
Out Of Memory (OOM) errors are handled using the *unstable*
|
||||
`alloc_error_handler` attribute.
|
||||
|
||||
``` rust
|
||||
``` rust,ignore
|
||||
#![feature(alloc_error_handler)]
|
||||
|
||||
use cortex_m::asm;
|
||||
|
||||
@@ -473,7 +473,7 @@ fn timer() {
|
||||
|
||||
That's quite a lot to take in, so let's break down the important lines.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
static MY_GPIO: Mutex<RefCell<Option<stm32f405::GPIOA>>> =
|
||||
Mutex::new(RefCell::new(None));
|
||||
```
|
||||
@@ -487,7 +487,7 @@ to something empty, and only later actually move the variable in. We cannot
|
||||
access the peripheral singleton statically, only at runtime, so this is
|
||||
required.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA)));
|
||||
```
|
||||
|
||||
@@ -495,7 +495,7 @@ Inside a critical section we can call `borrow()` on the mutex, which gives us
|
||||
a reference to the `RefCell`. We then call `replace()` to move our new value
|
||||
into the `RefCell`.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
interrupt::free(|cs| {
|
||||
let gpioa = MY_GPIO.borrow(cs).borrow();
|
||||
gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit());
|
||||
|
||||
@@ -29,7 +29,7 @@ void cool_function(int i, char c, CoolStruct* cs);
|
||||
|
||||
When translated to Rust, this interface would look as such:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
/* File: cool_bindings.rs */
|
||||
#[repr(C)]
|
||||
pub struct CoolStruct {
|
||||
@@ -46,27 +46,27 @@ pub extern "C" fn cool_function(
|
||||
|
||||
Let's take a look at this definition one piece at a time, to explain each of the parts.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
#[repr(C)]
|
||||
pub struct CoolStruct { ... }
|
||||
```
|
||||
|
||||
By default, Rust does not guarantee order, padding, or the size of data included in a `struct`. In order to guarantee compatibility with C code, we include the `#[repr(C)]` attribute, which instructs the Rust compiler to always use the same rules C does for organizing data within a struct.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
pub x: cty::c_int,
|
||||
pub y: cty::c_int,
|
||||
```
|
||||
|
||||
Due to the flexibility of how C or C++ defines an `int` or `char`, it is recommended to use primitive data types defined in `cty`, which will map types from C to types in Rust
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
pub extern "C" fn cool_function( ... );
|
||||
```
|
||||
|
||||
This statement defines the signature of a function that uses the C ABI, called `cool_function`. By defining the signature without defining the body of the function, the definition of this function will need to be provided elsewhere, or linked into the final library or binary from a static library.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
i: cty::c_int,
|
||||
c: cty::c_char,
|
||||
cs: *mut CoolStruct
|
||||
|
||||
@@ -34,7 +34,7 @@ most of the `std::os::raw` types in the [`cty`] crate.
|
||||
As mentioned above, primitive types can be converted
|
||||
by the compiler implicitly.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
unsafe fn foo(num: u32) {
|
||||
let c_num: c_uint = num;
|
||||
let r_num: u32 = c_num;
|
||||
|
||||
@@ -51,7 +51,7 @@ documented [here](https://doc.rust-lang.org/reference/items/external-blocks.html
|
||||
|
||||
Putting these parts together, you get a function that looks roughly like this.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_function() {
|
||||
|
||||
@@ -77,7 +77,7 @@ the function signatures.
|
||||
|
||||
Every function in your Rust-ffi API needs to have a corresponding header function.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_function() {}
|
||||
```
|
||||
|
||||
@@ -17,7 +17,7 @@ Let's look at the 'SysTick' peripheral - a simple timer which comes with every C
|
||||
|
||||
In Rust, we can represent a collection of registers in exactly the same way as we do in C - with a `struct`.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
#[repr(C)]
|
||||
struct SysTick {
|
||||
pub csr: u32,
|
||||
@@ -29,7 +29,7 @@ struct SysTick {
|
||||
|
||||
The qualifier `#[repr(C)]` tells the Rust compiler to lay this structure out like a C compiler would. That's very important, as Rust allows structure fields to be re-ordered, while C does not. You can imagine the debugging we'd have to do if these fields were silently re-arranged by the compiler! With this qualifier in place, we have our four 32-bit fields which correspond to the table above. But of course, this `struct` is of no use by itself - we need a variable.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
let systick = 0xE000_E010 as *mut SysTick;
|
||||
let time = unsafe { (*systick).cvr };
|
||||
```
|
||||
@@ -46,7 +46,7 @@ Now, there are a couple of problems with the approach above.
|
||||
|
||||
Now, the problem is that compilers are clever. If you make two writes to the same piece of RAM, one after the other, the compiler can notice this and just skip the first write entirely. In C, we can mark variables as `volatile` to ensure that every read or write occurs as intended. In Rust, we instead mark the *accesses* as volatile, not the variable.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
let systick = unsafe { &mut *(0xE000_E010 as *mut SysTick) };
|
||||
let time = unsafe { std::ptr::read_volatile(&mut systick.cvr) };
|
||||
```
|
||||
@@ -55,7 +55,7 @@ So, we've fixed one of our four problems, but now we have even more `unsafe` cod
|
||||
|
||||
[`volatile_register`]: https://crates.io/crates/volatile_register
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
use volatile_register::{RW, RO};
|
||||
|
||||
#[repr(C)]
|
||||
@@ -84,7 +84,7 @@ We need to wrap this `struct` up into a higher-layer API that is safe for our us
|
||||
|
||||
One example might be:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
use volatile_register::{RW, RO};
|
||||
|
||||
pub struct SystemTimer {
|
||||
@@ -124,7 +124,7 @@ pub fn example_usage() -> String {
|
||||
|
||||
Now, the problem with this approach is that the following code is perfectly acceptable to the compiler:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
fn thread1() {
|
||||
let mut st = SystemTimer::new();
|
||||
st.set_reload(2000);
|
||||
|
||||
@@ -27,7 +27,7 @@ But this has a few problems. It is a mutable global variable, and in Rust, these
|
||||
|
||||
Instead of just making our peripheral a global variable, we might instead decide to make a global variable, in this case called `PERIPHERALS`, which contains an `Option<T>` for each of our peripherals.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
struct Peripherals {
|
||||
serial: Option<SerialPort>,
|
||||
}
|
||||
@@ -75,7 +75,7 @@ fn main() {
|
||||
|
||||
Additionally, if you use `cortex-m-rtfm`, the entire process of defining and obtaining these peripherals are abstracted for you, and you are instead handed a `Peripherals` structure that contains a non-`Option<T>` version of all of the items you define.
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
// cortex-m-rtfm v0.3.x
|
||||
app! {
|
||||
resources: {
|
||||
@@ -95,7 +95,7 @@ fn init(p: init::Peripherals) -> init::LateResources {
|
||||
|
||||
But how do these Singletons make a noticeable difference in how our Rust code works?
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
impl SerialPort {
|
||||
const SER_PORT_SPEED_REG: *mut u32 = 0x4000_1000 as _;
|
||||
|
||||
@@ -134,7 +134,7 @@ Additionally, because some references are mutable, and some are immutable, it be
|
||||
|
||||
This is allowed to change hardware settings:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
fn setup_spi_port(
|
||||
spi: &mut SpiPort,
|
||||
cs_pin: &mut GpioPin
|
||||
@@ -145,7 +145,7 @@ fn setup_spi_port(
|
||||
|
||||
This isn't:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
fn read_button(gpio: &GpioPin) -> bool {
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ In our last chapter, we wrote an interface that *didn't* enforce design contract
|
||||
|
||||
If we instead checked the state before making use of the underlying hardware, enforcing our design contracts at runtime, we might write code that looks like this instead:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
/// GPIO interface
|
||||
struct GpioConfig {
|
||||
/// GPIO Configuration structure generated by svd2rust
|
||||
@@ -103,7 +103,7 @@ Because we need to enforce the restrictions on the hardware, we end up doing a l
|
||||
|
||||
But what if instead, we used Rust's type system to enforce the state transition rules? Take this example:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
/// GPIO interface
|
||||
struct GpioConfig<ENABLED, DIRECTION, MODE> {
|
||||
/// GPIO Configuration structure generated by svd2rust
|
||||
@@ -211,7 +211,7 @@ impl<IN_MODE> GpioConfig<Enabled, Input, IN_MODE> {
|
||||
|
||||
Now let's see what the code using this would look like:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
/*
|
||||
* Example 1: Unconfigured to High-Z input
|
||||
*/
|
||||
|
||||
@@ -53,7 +53,7 @@ Typically the states listed above are set by writing values to given registers m
|
||||
|
||||
We could simple expose the following structure in Rust to control this GPIO:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
/// GPIO interface
|
||||
struct GpioConfig {
|
||||
/// GPIO Configuration structure generated by svd2rust
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Type states are also an excellent example of Zero Cost Abstractions - the ability to move certain behaviors to compile time execution or analysis. These type states contain no actual data, and are instead used as markers. Since they contain no data, they have no actual representation in memory at runtime:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
use core::mem::size_of;
|
||||
|
||||
let _ = size_of::<Enabled>(); // == 0
|
||||
@@ -13,7 +13,7 @@ let _ = size_of::<GpioConfig<Enabled, Input, PulledHigh>>(); // == 0
|
||||
|
||||
## Zero Sized Types
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
struct Enabled;
|
||||
```
|
||||
|
||||
@@ -21,7 +21,7 @@ Structures defined like this are called Zero Sized Types, as they contain no act
|
||||
|
||||
In this snippet of code:
|
||||
|
||||
```rust
|
||||
```rust,ignore
|
||||
pub fn into_input_high_z(self) -> GpioConfig<Enabled, Input, HighZ> {
|
||||
self.periph.modify(|_r, w| w.input_mode().high_z());
|
||||
GpioConfig {
|
||||
|
||||
Reference in New Issue
Block a user