Skip to content
This repository has been archived by the owner on Sep 5, 2023. It is now read-only.

Latest trpl, trpl 2nd edition and rustonomicon sources. Dockerized all the things. #52

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
FROM ubuntu:16.04

RUN \
apt-get update && \
apt-get -y upgrade && \
apt-get install -y build-essential && \
apt-get install -y software-properties-common && \
apt-get install -y curl git htop unzip vim wget fontconfig && \
apt-get install -y texlive texlive-latex-extra texlive-generic-extra texlive-xetex && \
apt-get install -y librsvg2-bin && \
apt-get install -y ttf-dejavu && \
curl -o setup.sh https://sh.rustup.rs -sS && \
sh setup.sh -y && \
rm -rf /var/lib/apt/lists/*

ENV PATH="~/.cargo/bin:${PATH}"

RUN \
curl -o pandoc2.deb -L https://github.com/jgm/pandoc/releases/download/2.2.1/pandoc-2.2.1-1-amd64.deb && \
file pandoc2.deb && \
dpkg -i pandoc2.deb && \
rm pandoc2.deb

RUN \
curl -o ipafont.zip https://oscdl.ipa.go.jp/IPAexfont/IPAexfont00301.zip && \
unzip -j ipafont.zip -d ~/.fonts && \
rm ipafont.zip && \
fc-cache -fv

# Workaround to build dependencies beforehand.

WORKDIR /
RUN USER=root ~/.cargo/bin/cargo new trpl-ebook
WORKDIR /trpl-ebook
COPY Cargo.toml /trpl-ebook
COPY Cargo.lock /trpl-ebook
RUN ~/.cargo/bin/cargo build
RUN ~/.cargo/bin/cargo clean -p compile-trpl


COPY . /trpl-ebook
RUN chmod +x build.sh && chmod +x adjust_book_src.sh && ./adjust_book_src.sh
RUN ~/.cargo/bin/cargo build
CMD ["bash"]
25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
help:
@echo "Compile all books to ./dist use 'make all'"
@echo "Compile a specific book to ./dist use 'make bookname'. Booknames are rustonomicon, trpl and trpl2"

MAKEFILE_DIR?=$(shell pwd)
LOCAL_DIST_DIR=$(MAKEFILE_DIR)/dist
DIST_DIR=/trpl-ebook/dist

build:
docker build -t trpl-ebook -f Dockerfile .

interactive: build
docker run --rm -it -v $(MAKEFILE_DIR):/trpl-ebook trpl-ebook bash

all: build
docker run --rm -it -v $(LOCAL_DIST_DIR):$(DIST_DIR) trpl-ebook bash build.sh

rustonomicon: build
docker run --rm -it -v $(LOCAL_DIST_DIR):$(DIST_DIR) trpl-ebook bash -c "cargo run -- --prefix=rustonomicon --source=book_src/nomicon"

trpl: build
docker run --rm -it -v $(LOCAL_DIST_DIR):$(DIST_DIR) trpl-ebook bash -c "cargo run -- --source=book_src/trpl"

trpl2: build
docker run --rm -it -v $(LOCAL_DIST_DIR):$(DIST_DIR) trpl-ebook bash -c "cargo run -- --source=book_src/trpl2"
24 changes: 22 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,26 @@ This repository contains stuff to convert [this book](http://doc.rust-lang.org/b

[![Build Status](https://travis-ci.org/killercup/trpl-ebook.svg?branch=master)](https://travis-ci.org/killercup/trpl-ebook)

## Docker
The simplest way to execute this is to use docker. With a installed and running docker service you can use the Makefile to build all books:

```sh
$ make all
```
Or just build a specific book (rustonomicon, trpl or trpl2):

```sh
$ make trpl
```

Results will appear into the *dist* directory.


## DIY

Install:

- pandoc
- pandoc version >= 2.0
- Rust and cargo
- XeLaTeX, up to date (`sudo tlmgr update -all`) and probably some additional packages (`sudo tlmgr install $pkg`) such as:
+ framed
Expand Down Expand Up @@ -40,8 +55,13 @@ There are some CLI arguments that you can use to compile books other than the de
You can build it like this:

```sh
$ cargo run --release -- --prefix=nomicon --source=nomicon --meta=nomicon_meta.yml
$ cargo run --release -- --prefix=rustonomicon --source=book_src/nomicon
```

```sh
$ cargo run --release -- --source=book_src/trpl
```
If your books meta.yml is not in the document directory supply the `--meta=$path-to-file` argument.

## License

Expand Down
5 changes: 5 additions & 0 deletions adjust_book_src.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/sh

find book_src -type f \( -iname "*.md" ! -iname "SUMMARY.md" \) -exec sed -i '0,/# /{s/#/% /}' {} \;
find book_src -type f -name "SUMMARY.md" -exec sed -i 's/- \[/* \[/g' {} \;
find book_src -type f -name "SUMMARY.md" -exec sed -i 's/^\[/* \[/g' {} \;
41 changes: 41 additions & 0 deletions book_src/nomicon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# The Rustonomicon

#### The Dark Arts of Advanced and Unsafe Rust Programming

**NOTE: This is a draft document that discusses several unstable aspects of Rust, and may contain serious errors or outdated information.**

> Instead of the programs I had hoped for, there came only a shuddering blackness
and ineffable loneliness; and I saw at last a fearful truth which no one had
ever dared to breathe before — the unwhisperable secret of secrets — The fact
that this language of stone and stridor is not a sentient perpetuation of Rust
as London is of Old London and Paris of Old Paris, but that it is in fact
quite unsafe, its sprawling body imperfectly embalmed and infested with queer
animate things which have nothing to do with it as it was in compilation.

This book digs into all the awful details that are necessary to understand in
order to write correct Unsafe Rust programs. Due to the nature of this problem,
it may lead to unleashing untold horrors that shatter your psyche into a billion
infinitesimal fragments of despair.

Should you wish a long and happy career of writing Rust programs, you should
turn back now and forget you ever saw this book. It is not necessary. However
if you intend to write unsafe code — or just want to dig into the guts of the
language — this book contains lots of useful information.

Unlike *[The Rust Programming Language][trpl]*, we will be assuming considerable
prior knowledge. In particular, you should be comfortable with basic systems
programming and Rust. If you don't feel comfortable with these topics, you
should consider [reading The Book][trpl] first. That said, we won't assume you
have read it, and we will take care to occasionally give a refresher on the
basics where appropriate. You can skip straight to this book if you want;
just know that we won't be explaining everything from the ground up.

We're going to dig into exception-safety, pointer aliasing, memory models,
compiler and hardware implementation details, and even some type-theory.
Much text will be devoted to exotic corner cases that no one *should* ever have
to care about, but suddenly become important because we wrote `unsafe`.

We will also be spending a lot of time talking about the different kinds of
safety and guarantees that programs could care about.

[trpl]: ../book/index.html
5 changes: 5 additions & 0 deletions nomicon/SUMMARY.md → book_src/nomicon/SUMMARY.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# Summary

[Introduction](README.md)

* [Meet Safe and Unsafe](meet-safe-and-unsafe.md)
* [How Safe and Unsafe Interact](safe-unsafe-meaning.md)
* [What Unsafe Can Do](what-unsafe-does.md)
* [Working with Unsafe](working-with-unsafe.md)
* [Data Layout](data.md)
* [repr(Rust)](repr-rust.md)
* [Exotically Sized Types](exotic-sizes.md)
* [Other reprs](other-reprs.md)
* [Ownership](ownership.md)
* [References](references.md)
* [Aliasing](aliasing.md)
* [Lifetimes](lifetimes.md)
* [Limits of Lifetimes](lifetime-mismatch.md)
* [Lifetime Elision](lifetime-elision.md)
Expand Down Expand Up @@ -51,3 +55,4 @@
* [Handling Zero-Sized Types](vec-zsts.md)
* [Final Code](vec-final.md)
* [Implementing Arc and Mutex](arc-and-mutex.md)
* [FFI](ffi.md)
135 changes: 135 additions & 0 deletions book_src/nomicon/aliasing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Aliasing

First off, let's get some important caveats out of this way:

* We will be using the broadest possible definition of aliasing for the sake
of discussion. Rust's definition will probably be more restricted to factor
in mutations and liveness.

* We will be assuming a single-threaded, interrupt-free, execution. We will also
be ignoring things like memory-mapped hardware. Rust assumes these things
don't happen unless you tell it otherwise. For more details, see the
[Concurrency Chapter](concurrency.html).

With that said, here's our working definition: variables and pointers *alias*
if they refer to overlapping regions of memory.




# Why Aliasing Matters

So why should we care about aliasing?

Consider this simple function:

```rust
fn compute(input: &u32, output: &mut u32) {
if *input > 10 {
*output = 1;
}
if *input > 5 {
*output *= 2;
}
}
```

We would *like* to be able to optimize it to the following function:

```rust
fn compute(input: &u32, output: &mut u32) {
let cached_input = *input; // keep *input in a register
if cached_input > 10 {
*output = 2; // x > 10 implies x > 5, so double and exit immediately
} else if cached_input > 5 {
*output *= 2;
}
}
```

In Rust, this optimization should be sound. For almost any other language, it
wouldn't be (barring global analysis). This is because the optimization relies
on knowing that aliasing doesn't occur, which most languages are fairly liberal
with. Specifically, we need to worry about function arguments that make `input`
and `output` overlap, such as `compute(&x, &mut x)`.

With that input, we could get this execution:

```rust,ignore
// input == output == 0xabad1dea
// *input == *output == 20
if *input > 10 { // true (*input == 20)
*output = 1; // also overwrites *input, because they are the same
}
if *input > 5 { // false (*input == 1)
*output *= 2;
}
// *input == *output == 1
```

Our optimized function would produce `*output == 2` for this input, so the
correctness of our optimization relies on this input being impossible.

In Rust we know this input should be impossible because `&mut` isn't allowed to be
aliased. So we can safely reject its possibility and perform this optimization.
In most other languages, this input would be entirely possible, and must be considered.

This is why alias analysis is important: it lets the compiler perform useful
optimizations! Some examples:

* keeping values in registers by proving no pointers access the value's memory
* eliminating reads by proving some memory hasn't been written to since last we read it
* eliminating writes by proving some memory is never read before the next write to it
* moving or reordering reads and writes by proving they don't depend on each other

These optimizations also tend to prove the soundness of bigger optimizations
such as loop vectorization, constant propagation, and dead code elimination.

In the previous example, we used the fact that `&mut u32` can't be aliased to prove
that writes to `*output` can't possibly affect `*input`. This let us cache `*input`
in a register, eliminating a read.

By caching this read, we knew that the the write in the `> 10` branch couldn't
affect whether we take the `> 5` branch, allowing us to also eliminate a
read-modify-write (doubling `*output`) when `*input > 10`.

The key thing to remember about alias analysis is that writes are the primary
hazard for optimizations. That is, the only thing that prevents us
from moving a read to any other part of the program is the possibility of us
re-ordering it with a write to the same location.

For instance, we have no concern for aliasing in the following modified version
of our function, because we've moved the only write to `*output` to the very
end of our function. This allows us to freely reorder the reads of `*input` that
occur before it:

```rust
fn compute(input: &u32, output: &mut u32) {
let mut temp = *output;
if *input > 10 {
temp = 1;
}
if *input > 5 {
temp *= 2;
}
*output = temp;
}
```

We're still relying on alias analysis to assume that `temp` doesn't alias
`input`, but the proof is much simpler: the value of a local variable can't be
aliased by things that existed before it was declared. This is an assumption
every language freely makes, and so this version of the function could be
optimized the way we want in any language.

This is why the definition of "alias" that Rust will use likely involves some
notion of liveness and mutation: we don't actually care if aliasing occurs if
there aren't any actual writes to memory happening.

Of course, a full aliasing model for Rust must also take into consideration things like
function calls (which may mutate things we don't see), raw pointers (which have
no aliasing requirements on their own), and UnsafeCell (which lets the referent
of an `&` be mutated).



2 changes: 1 addition & 1 deletion nomicon/arc-and-mutex.md → book_src/nomicon/arc-and-mutex.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Implementing Arc and Mutex
# Implementing Arc and Mutex

Knowing the theory is all fine and good, but the *best* way to understand
something is to use it. To better understand atomics and interior mutability,
Expand Down
12 changes: 6 additions & 6 deletions nomicon/atomics.md → book_src/nomicon/atomics.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Atomics
# Atomics

Rust pretty blatantly just inherits C11's memory model for atomics. This is not
due to this model being particularly excellent or easy to understand. Indeed,
Expand All @@ -24,10 +24,10 @@ exactly what we said but, you know, fast. Wouldn't that be great?

# Compiler Reordering

Compilers fundamentally want to be able to do all sorts of crazy transformations
to reduce data dependencies and eliminate dead code. In particular, they may
radically change the actual order of events, or make events never occur! If we
write something like
Compilers fundamentally want to be able to do all sorts of complicated
transformations to reduce data dependencies and eliminate dead code. In
particular, they may radically change the actual order of events, or make events
never occur! If we write something like

```rust,ignore
x = 1;
Expand Down Expand Up @@ -196,7 +196,7 @@ reordered to occur before it.

When thread A releases a location in memory and then thread B subsequently
acquires *the same* location in memory, causality is established. Every write
that happened before A's release will be observed by B after its release.
that happened before A's release will be observed by B after its acquisition.
However no causality is established with any other threads. Similarly, no
causality is established if A and B access *different* locations in memory.

Expand Down
2 changes: 1 addition & 1 deletion nomicon/borrow-splitting.md → book_src/nomicon/borrow-splitting.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Splitting Borrows
# Splitting Borrows

The mutual exclusion property of mutable references can be very limiting when
working with a composite structure. The borrow checker understands some basic
Expand Down
15 changes: 5 additions & 10 deletions nomicon/casts.md → book_src/nomicon/casts.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Casts
# Casts

Casts are a superset of coercions: every coercion can be explicitly
invoked via a cast. However some conversions require a cast.
Expand Down Expand Up @@ -28,7 +28,7 @@ primitive:
* `*T as integer`
* `integer as *T`
* `number as number`
* `C-like-enum as integer`
* `field-less enum as integer`
* `bool as integer`
* `char as integer`
* `u8 as char`
Expand Down Expand Up @@ -56,16 +56,11 @@ For numeric casts, there are quite a few cases to consider:
value cannot be represented by the target integer type][float-int]**.
This includes Inf and NaN. This is a bug and will be fixed.
* casting from an integer to float will produce the floating point
representation of the integer, rounded if necessary (rounding strategy
unspecified)
representation of the integer, rounded if necessary (rounding to
nearest, ties to even)
* casting from an f32 to an f64 is perfect and lossless
* casting from an f64 to an f32 will produce the closest possible value
(rounding strategy unspecified)
* **[NOTE: currently this will cause Undefined Behavior if the value
is finite but larger or smaller than the largest or smallest finite
value representable by f32][float-float]**. This is a bug and will
be fixed.
(rounding to nearest, ties to even)


[float-int]: https://github.com/rust-lang/rust/issues/10184
[float-float]: https://github.com/rust-lang/rust/issues/15536
Loading