# The generated files are shipped only for the benefit of Windows users, who
# typically don't have the necessary tools for generating the workspace
# available.
-workspace: $(WORKSPACEFILES)
+workspace: $(WORKSPACEFILES) docs/workspace.zip
workspace/src/%.rs: src/%.rs Makefile dup-unimpl.sed
- @mkdir -p .tmp/docs
+ @mkdir -p .tmp/docs workspace/src/
@echo "$< -> $@"
@# sed-fu: remove lines starting with "//@", and replace those ending in "/*@*/" or "/*@@*/" by "unimplemented!()".
@# Also coalesce multiple adjacent such lines to one.
workspace/src/main.rs:
# Don't touch this file
+docs/workspace.zip: $(WORKSPACEFILES) workspace/Cargo.toml workspace/Cargo.lock
+ @rm -f $@
+ zip $@ $^
+
## Crates
crates: $(WORKSPACEFILES)
@cargo build
# Rust-101
This documents Rust-101, a tutorial for the [Rust
-language](http://www.rust-lang.org/). You are probably looking for the
-[actual tutorial](https://www.ralfj.de/projects/rust-101/main.html).
+language](http://www.rust-lang.org/).
+## Online tutorial
+
+The easiest way to the toturial is to check out its
+[online version](https://www.ralfj.de/projects/rust-101/main.html).
## Usage
fonts/
*.html
+*.zip
pycco.css
// [the second chapter of The Book](https://doc.rust-lang.org/stable/book/installing-rust.html).
// This will also install `cargo`, the tool responsible for building rust projects (or *crates*).
//
-// Next, fetch the Rust-101 source code from the [git repository](https://www.ralfj.de/git/rust-101.git)
-// (also available [on GitHub](https://github.com/RalfJung/rust-101), and as a
-// [zip archive](https://github.com/RalfJung/rust-101/archive/master.zip) in case you don't have git installed).
+// Next, we have to prepare a workspace for you to conduct your Rust-101 work in, so that you don't
+// have to start with an empty file. The easiest way is to [download the workspace](https://www.ralfj.de/projects/rust-101/workspace.zip)
+// matching the online tutorial. Try `cargo build` in that new folder to check that compiling your workspace succeeds.
+// (You can also execute it with `cargo run`, but you'll need to do some work before this does anything useful.)
//
-// There is a workspace prepared for you in the `workspace` folder. I suggest you copy this
-// folder somewhere else. Try `cargo build` in that new folder to check that compiling your workspace succeeds.
-// (You can also execute it with `cargo run`, but you'll need to do some work before this will succeed.)
+// Alternatively, you can build the workspace from source by fetching the [git repository](https://www.ralfj.de/git/rust-101.git)
+// and running `make workspace`.
// Course Content
// --------------
//@ computed that ourselves, but that's beside the point. More importantly:
//@ You completed the first part of the course.
-//@ [index](main.html) | previous | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part00.rs) | [next](part01.html)
+//@ [index](main.html) | previous | [raw source](workspace/src/part00.rs) | [next](part01.html)
// **Exercise 01.2**: Write a function `vec_print` that takes a vector and prints all its elements.
-//@ [index](main.html) | [previous](part00.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part01.rs) | [next](part02.html)
+//@ [index](main.html) | [previous](part00.html) | [raw source](workspace/src/part01.rs) | [next](part02.html)
// **Exercise 02.1**: Change your program such that it computes the minimum of a `Vec<f32>` (where `f32` is the type
// of 32-bit floating-point numbers). You should not change `vec_min` in any way, obviously!
-//@ [index](main.html) | [previous](part01.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part02.rs) | [next](part03.html)
+//@ [index](main.html) | [previous](part01.html) | [raw source](workspace/src/part02.rs) | [next](part03.html)
// **Exercise 03.2**: Building on exercise 02.2, implement all the things you need on `f32` to make your
// program work with floating-point numbers.
-//@ [index](main.html) | [previous](part02.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part03.rs) | [next](part04.html)
+//@ [index](main.html) | [previous](part02.html) | [raw source](workspace/src/part03.rs) | [next](part04.html)
// As it turns out, combined with the abstraction facilities of Rust, this is a very powerful mechanism
// to tackle many problems beyond basic memory safety. You will see some examples for this soon.
-//@ [index](main.html) | [previous](part03.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part04.rs) | [next](part05.html)
+//@ [index](main.html) | [previous](part03.html) | [raw source](workspace/src/part04.rs) | [next](part05.html)
//@ I hope this example clarifies why Rust has to rule out mutation in the presence of aliasing *in general*,
//@ not just for the specific case of a buffer being reallocated, and old pointers becoming hence invalid.
-//@ [index](main.html) | [previous](part04.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part05.rs) | [next](part06.html)
+//@ [index](main.html) | [previous](part04.html) | [raw source](workspace/src/part05.rs) | [next](part06.html)
//@ Most of the time, we don't have to explicitly add lifetimes to function types. This is thanks to *lifetime elision*,
//@ where Rust will automatically insert lifetimes we did not specify, following some [simple, well-documented rules](https://doc.rust-lang.org/stable/book/lifetimes.html#lifetime-elision).
-//@ [index](main.html) | [previous](part05.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part06.rs) | [next](part07.html)
+//@ [index](main.html) | [previous](part05.html) | [raw source](workspace/src/part06.rs) | [next](part07.html)
// of course, need a `Display` bound on `T`.) Then you should be able to use them with `println!` just like you do
// with numbers, and get rid of the inherent functions to print `SomethingOrNothing<i32>` and `SomethingOrNothing<f32>`.
-//@ [index](main.html) | [previous](part06.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part07.rs) | [next](part08.html)
+//@ [index](main.html) | [previous](part06.html) | [raw source](workspace/src/part07.rs) | [next](part08.html)
// **Exercise 08.6**: Write a subtraction function, and testcases for it. Decide for yourself how you want to handle negative results.
// For example, you may want to return an `Option`, to panic, or to return `0`.
-//@ [index](main.html) | [previous](part07.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part08.rs) | [next](part09.html)
+//@ [index](main.html) | [previous](part07.html) | [raw source](workspace/src/part08.rs) | [next](part09.html)
//@ then you will obtain ownership of the elements during the iteration - and destroy the vector in the process. We actually did that in
//@ `part01::vec_min`, but we did not care. You can write `for e in &v` or `for e in v.iter()` to avoid this.
-//@ [index](main.html) | [previous](part08.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part09.rs) | [next](part10.html)
+//@ [index](main.html) | [previous](part08.html) | [raw source](workspace/src/part09.rs) | [next](part10.html)
// product of those numbers that sit at odd positions? A function that checks whether a vector contains a certain number? Whether all numbers are
// smaller than some threshold? Be creative!
-//@ [index](main.html) | [previous](part09.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part10.rs) | [next](part11.html)
+//@ [index](main.html) | [previous](part09.html) | [raw source](workspace/src/part10.rs) | [next](part11.html)
// to work with an arbitrary type `T` that's passed to the callbacks. Since you need to call multiple callbacks with the
// same `t: T`, you will either have to restrict `T` to `Copy` types, or pass a reference.
-//@ [index](main.html) | [previous](part10.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part11.rs) | [next](part12.html)
+//@ [index](main.html) | [previous](part10.html) | [raw source](workspace/src/part11.rs) | [next](part12.html)
// **Exercise 12.1**: Write some piece of code using only the available, public interface of `CallbacksMut` such that a reentrant call to a closure
// is happening, and the program panics because the `RefCell` refuses to hand out a second mutable borrow of the closure's environment.
-//@ [index](main.html) | [previous](part11.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part12.rs) | [next](part13.html)
+//@ [index](main.html) | [previous](part11.html) | [raw source](workspace/src/part12.rs) | [next](part13.html)
//@ So if the environment of your closure contains an `Rc`, it won't be `Send`, preventing it from causing trouble. If however every
//@ captured variable *is* `Send`, then so is the entire environment, and you are good.
-//@ [index](main.html) | [previous](part12.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part13.rs) | [next](part14.html)
+//@ [index](main.html) | [previous](part12.html) | [raw source](workspace/src/part13.rs) | [next](part14.html)
// the pattern to regular-expression mode, and change `filter_lines` to honor this option. The documentation of regex is available from its crates.io site.
// (You won't be able to use the `regex!` macro if you are on the stable or beta channel of Rust. But it wouldn't help for our use-case anyway.)
-//@ [index](main.html) | [previous](part13.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part14.rs) | [next](part15.html)
+//@ [index](main.html) | [previous](part13.html) | [raw source](workspace/src/part14.rs) | [next](part15.html)
//@ [Rust RFC](https://github.com/rust-lang/rfcs/blob/master/text/0458-send-improvements.md), which contains a type `RcMut` that would be `Sync` and not `Send`.
//@ You may also be interested in [this blog post](https://huonw.github.io/blog/2015/02/some-notes-on-send-and-sync/) on the topic.
-//@ [index](main.html) | [previous](part14.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part15.rs) | [next](part16.html)
+//@ [index](main.html) | [previous](part14.html) | [raw source](workspace/src/part15.rs) | [next](part16.html)
//@ extensions here and there. The [index](main.html) contains some more links to additional resources you may find useful.
//@ With that, there's only one thing left to say: Happy Rust Hacking!
-//@ [index](main.html) | [previous](part15.html) | [raw source](https://www.ralfj.de/git/rust-101.git/blob_plain/HEAD:/workspace/src/part16.rs) | next
+//@ [index](main.html) | [previous](part15.html) | [raw source](workspace/src/part16.rs) | next
target/
-.*
+src/part*.rs
+++ /dev/null
-## Workspace
-
-This folder contains your Rust-101 workspace. I suggest you copy this
-folder somewhere else. Try `cargo build` in that new folder to check that compiling your workspace succeeds.
-(You can also execute it with `cargo run`, but you'll need to do some work before this will succeed.)
-See the [main tutorial](https://www.ralfj.de/projects/rust-101/main.html) for more details.
-
-Please do *not* edit this folder inside a `git clone` of Rust-101. The files in here are auto-generated.
-You should edit the files in `src/` in the repository root instead.
#![allow(dead_code, unused_imports, unused_variables, unused_mut, unreachable_code)]
+// To get started with the course, open the file `part00.rs` in this workspace as well
+// as its [fully explained version](https://www.ralfj.de/projects/rust-101/part00.html).
+// The code below is mostly boilerplate to dispatch into the various parts of the
+// tutorial.
+
// Only the files imported here will be compiled.
mod part00;
mod part01;
+++ /dev/null
-// Rust-101, Part 00: Algebraic datatypes
-// ======================================
-
-// As our first piece of Rust code, we want to write a function that computes the
-// minimum of a list.
-
-
-// An `enum` for "a number or nothing" could look as follows:
-enum NumberOrNothing {
- Number(i32),
- Nothing
-}
-
-// Observe how in Rust, the return type comes *after* the arguments.
-fn vec_min(vec: Vec<i32>) -> NumberOrNothing {
- let mut min = NumberOrNothing::Nothing;
-
- // Now we want to *iterate* over the list. Rust has some nice syntax for iterators:
- for el in vec {
- // So `el` is an element of the list. We need to update `min` accordingly, but how do we get the current
- // number in there? This is what pattern matching can do:
- match min {
- // In this case (*arm*) of the `match`, `min` is currently nothing, so let's just make it the number `el`.
- NumberOrNothing::Nothing => {
- unimplemented!()
- },
- // In this arm, `min` is currently the number `n`, so let's compute the new minimum and store it.
- NumberOrNothing::Number(n) => {
- unimplemented!()
- }
- }
- }
- // Finally, we return the result of the computation.
- return min;
-}
-
-// Now that we reduced the problem to computing the minimum of two integers, let's do that.
-fn min_i32(a: i32, b: i32) -> i32 {
- if a < b {
- unimplemented!()
- } else {
- unimplemented!()
- }
-}
-
-// Phew. We wrote our first Rust function! But all this `NumberOrNothing::` is getting kind of
-// ugly. Can't we do that nicer?
-
-// Indeed, we can: The following line tells Rust to take
-// the constructors of `NumberOrNothing` into the local namespace.
-// Try moving that above the function, and removing all the occurrences of `NumberOrNothing::`.
-use self::NumberOrNothing::{Number,Nothing};
-
-// To call this function, we now just need a list. Of course, ultimately we want to ask the user for
-// a list of numbers, but for now, let's just hard-code something.
-
-fn read_vec() -> Vec<i32> {
- unimplemented!()
-}
-
-// Of course, we would also like to actually see the result of the computation, so we need to print the result.
-
-fn print_number_or_nothing(n: NumberOrNothing) {
- unimplemented!()
-}
-
-// Putting it all together:
-pub fn main() {
- let vec = read_vec();
- let min = vec_min(vec);
- print_number_or_nothing(min);
-}
-
-// Finally, try `cargo run` on the console to run it.
-
-
+++ /dev/null
-// Rust-101, Part 01: Expressions, Inherent methods
-// ================================================
-
-// For Rust to compile this file, make sure to enable the corresponding line
-// in `main.rs` before going on.
-
-
-// ## Expression-based programming
-fn sqr(i: i32) -> i32 { i * i }
-
-// Conditionals are also just expressions. This is comparable to the ternary `? :` operator
-// from languages like C.
-fn abs(i: i32) -> i32 { if i >= 0 { i } else { -i } }
-
-enum NumberOrNothing {
- Number(i32),
- Nothing
-}
-use self::NumberOrNothing::{Number,Nothing};
-fn number_or_default(n: NumberOrNothing, default: i32) -> i32 {
- match n {
- Nothing => default,
- Number(n) => n,
- }
-}
-
-// It is even the case that blocks are expressions, evaluating to the last expression they contain.
-fn compute_stuff(x: i32) -> i32 {
- let y = { let z = x*x; z + 14 };
- y*y
-}
-
-// Let us now refactor `vec_min`.
-fn vec_min(v: Vec<i32>) -> NumberOrNothing {
- fn min_i32(a: i32, b: i32) -> i32 {
- unimplemented!()
- }
-
- let mut min = Nothing;
- for e in v {
- unimplemented!()
- }
- min
-}
-
-// Now that's already much shorter! Make sure you can go over the code above and actually understand
-// every step of what's going on.
-
-// ## Inherent implementations
-impl NumberOrNothing {
- fn print(self) {
- match self {
- Nothing => println!("The number is: <nothing>"),
- Number(n) => println!("The number is: {}", n),
- };
- }
-}
-
-// With our refactored functions and methods, `main` now looks as follows:
-fn read_vec() -> Vec<i32> {
- vec![18,5,7,2,9,27]
-}
-pub fn main() {
- let vec = read_vec();
- let min = vec_min(vec);
- unimplemented!()
-}
-// You will have to replace `part00` by `part01` in the `main` function in
-// `main.rs` to run this code.
-
-// **Exercise 01.1**: Write a function `vec_sum` that computes the sum of all values of a `Vec<i32>`.
-
-// **Exercise 01.2**: Write a function `vec_print` that takes a vector and prints all its elements.
-
+++ /dev/null
-// Rust-101, Part 02: Generic types, Traits
-// ========================================
-
-
-// ## Generic datatypes
-
-pub enum SomethingOrNothing<T> {
- Something(T),
- Nothing,
-}
-// Instead of writing out all the variants, we can also just import them all at once.
-pub use self::SomethingOrNothing::*;
-type NumberOrNothing = SomethingOrNothing<i32>;
-
-// ## Generic `impl`, Static functions
-// Inside an `impl`, `Self` refers to the type we are implementing things for. Here, it is
-// an alias for `SomethingOrNothing<T>`.
-impl<T> SomethingOrNothing<T> {
- fn new(o: Option<T>) -> Self {
- unimplemented!()
- }
-
- fn to_option(self) -> Option<T> {
- unimplemented!()
- }
-}
-// You can call static functions, and in particular constructors, as demonstrated in `call_constructor`.
-fn call_constructor(x: i32) -> SomethingOrNothing<i32> {
- SomethingOrNothing::new(Some(x))
-}
-
-// ## Traits
-
-pub trait Minimum : Copy {
- fn min(self, b: Self) -> Self;
-}
-
-pub fn vec_min<T: Minimum>(v: Vec<T>) -> SomethingOrNothing<T> {
- let mut min = Nothing;
- for e in v {
- min = Something(match min {
- Nothing => e,
- // Here, we can now call the `min` function of the trait.
- Something(n) => {
- unimplemented!()
- }
- });
- }
- min
-}
-
-// ## Trait implementations
-// To make `vec_min` usable with a `Vec<i32>`, we implement the `Minimum` trait for `i32`.
-impl Minimum for i32 {
- fn min(self, b: Self) -> Self {
- unimplemented!()
- }
-}
-
-// We again provide a `print` function.
-impl NumberOrNothing {
- pub fn print(self) {
- match self {
- Nothing => println!("The number is: <nothing>"),
- Something(n) => println!("The number is: {}", n),
- };
- }
-}
-
-// Now we are ready to run our new code. Remember to change `main.rs` appropriately.
-fn read_vec() -> Vec<i32> {
- vec![18,5,7,3,9,27]
-}
-pub fn main() {
- let vec = read_vec();
- let min = vec_min(vec);
- min.print();
-}
-
-
-// **Exercise 02.1**: Change your program such that it computes the minimum of a `Vec<f32>` (where `f32` is the type
-// of 32-bit floating-point numbers). You should not change `vec_min` in any way, obviously!
-
+++ /dev/null
-// Rust-101, Part 03: Input
-// ========================
-
-
-// I/O is provided by the module `std::io`, so we first have import that with `use`.
-// We also import the I/O *prelude*, which makes a bunch of commonly used I/O stuff
-// directly available.
-use std::io::prelude::*;
-use std::io;
-
-fn read_vec() -> Vec<i32> {
- let mut vec: Vec<i32> = Vec::<i32>::new();
- // The central handle to the standard input is made available by the function `io::stdin`.
- let stdin = io::stdin();
- println!("Enter a list of numbers, one per line. End with Ctrl-D (Linux) or Ctrl-Z (Windows).");
- for line in stdin.lock().lines() {
- // Rust's type for (dynamic, growable) strings is `String`. However, our variable `line`
- // here is not yet of that type: It has type `io::Result<String>`.
-
- // I chose the same name (`line`) for the new variable to ensure that I will never, accidentally,
- // access the "old" `line` again.
- let line = line.unwrap();
- // Now that we have our `String`, we want to make it an `i32`.
-
- match line.trim().parse::<i32>() {
- Ok(num) => {
- unimplemented!()
- },
- // We don't care about the particular error, so we ignore it with a `_`.
- Err(_) => {
- unimplemented!()
- },
- }
- }
-
- vec
-}
-
-
-// For the rest of the code, we just re-use part 02 by importing it with `use`.
-use part02::{SomethingOrNothing,Something,Nothing,vec_min};
-
-// If you update your `main.rs` to use part 03, `cargo run` should now ask you for some numbers,
-// and tell you the minimum. Neat, isn't it?
-pub fn main() {
- let vec = read_vec();
- unimplemented!()
-}
-
-// **Exercise 03.1**: Define a trait `Print` to write a generic version of `SomethingOrNothing::print`.
-// Implement that trait for `i32`, and change the code above to use it.
-// I will again provide a skeleton for this solution. It also shows how to attach bounds to generic
-// implementations (just compare it to the `impl` block from the previous exercise).
-// You can read this as "For all types `T` satisfying the `Print` trait, I provide an implementation
-// for `SomethingOrNothing<T>`".
-//
-// Notice that I called the function on `SomethingOrNothing` `print2` to disambiguate from the `print` defined previously.
-//
-// *Hint*: There is a macro `print!` for printing without appending a newline.
-pub trait Print {
- /* Add things here */
-}
-impl<T: Print> SomethingOrNothing<T> {
- fn print2(self) {
- unimplemented!()
- }
-}
-
-// **Exercise 03.2**: Building on exercise 02.2, implement all the things you need on `f32` to make your
-// program work with floating-point numbers.
-
+++ /dev/null
-// Rust-101, Part 04: Ownership, Borrowing, References
-// ===================================================
-
-/*
- void foo(std::vector<int> v) {
- int *first = &v[0];
- v.push_back(42);
- *first = 1337; // This is bad!
- }
-*/
-
-// ## Ownership
-fn work_on_vector(v: Vec<i32>) { /* do something */ }
-fn ownership_demo() {
- let v = vec![1,2,3,4];
- work_on_vector(v);
- /* println!("The first element is: {}", v[0]); */ /* BAD! */
-}
-
-// ## Borrowing a shared reference
-
-fn vec_min(v: &Vec<i32>) -> Option<i32> {
- use std::cmp;
-
- let mut min = None;
- // This time, we explicitly request an iterator for the vector `v`. The method `iter` just borrows the vector
- // it works on, and provides shared references to the elements.
- for e in v.iter() {
- // In the loop, `e` now has type `&i32`, so we have to dereference it to obtain an `i32`.
- min = Some(match min {
- None => *e,
- Some(n) => cmp::min(n, *e)
- });
- }
- min
-}
-
-// Now that `vec_min` does not acquire ownership of the vector anymore, we can call it multiple times on the same vector and also do things like
-fn shared_ref_demo() {
- let v = vec![5,4,3,2,1];
- let first = &v[0];
- vec_min(&v);
- vec_min(&v);
- println!("The first element is: {}", *first);
-}
-
-// ## Unique, mutable references
-
-fn vec_inc(v: &mut Vec<i32>) {
- for e in v.iter_mut() {
- *e += 1;
- }
-}
-// Here's an example of calling `vec_inc`.
-fn mutable_ref_demo() {
- let mut v = vec![5,4,3,2,1];
- /* let first = &v[0]; */
- vec_inc(&mut v);
- vec_inc(&mut v);
- /* println!("The first element is: {}", *first); */ /* BAD! */
-}
-
-// ## Summary
-// The ownership and borrowing system of Rust enforces the following three rules:
-//
-// * There is always exactly one owner of a piece of data
-// * If there is an active mutable reference, then nobody else can have active access to the data
-// * If there is an active shared reference, then every other active access to the data is also a shared reference
-//
-// As it turns out, combined with the abstraction facilities of Rust, this is a very powerful mechanism
-// to tackle many problems beyond basic memory safety. You will see some examples for this soon.
-
+++ /dev/null
-// Rust-101, Part 05: Clone
-// ========================
-
-// ## Big Numbers
-
-pub struct BigInt {
- pub data: Vec<u64>, // least significant digit first, no trailing zeros
-}
-
-// Now that we fixed the data representation, we can start implementing methods on it.
-impl BigInt {
- pub fn new(x: u64) -> Self {
- if x == 0 {
- unimplemented!()
- } else {
- unimplemented!()
- }
- }
-
- pub fn test_invariant(&self) -> bool {
- if self.data.len() == 0 {
- true
- } else {
- unimplemented!()
- }
- }
-
- // We can convert any vector of digits into a number, by removing trailing zeros. The `mut`
- // declaration for `v` here is just like the one in `let mut ...`: We completely own `v`, but Rust
- // still asks us to make our intention of modifying it explicit. This `mut` is *not* part of the
- // type of `from_vec` - the caller has to give up ownership of `v` anyway, so they don't care anymore
- // what you do to it.
- //
- // **Exercise 05.1**: Implement this function.
- //
- // *Hint*: You can use `pop` to remove the last element of a vector.
- pub fn from_vec(mut v: Vec<u64>) -> Self {
- unimplemented!()
- }
-}
-
-// ## Cloning
-fn clone_demo() {
- let v = vec![0,1 << 16];
- let b1 = BigInt::from_vec((&v).clone());
- let b2 = BigInt::from_vec(v);
-}
-
-impl Clone for BigInt {
- fn clone(&self) -> Self {
- unimplemented!()
- }
-}
-
-// We can also make the type `SomethingOrNothing<T>` implement `Clone`.
-use part02::{SomethingOrNothing,Something,Nothing};
-impl<T: Clone> Clone for SomethingOrNothing<T> {
- fn clone(&self) -> Self {
- unimplemented!()
- }
-}
-
-// **Exercise 05.2**: Write some more functions on `BigInt`. What about a function that returns the number of
-// digits? The number of non-zero digits? The smallest/largest digit? Of course, these should all take `self` as a shared reference (i.e., in borrowed form).
-
-// ## Mutation + aliasing considered harmful (part 2)
-enum Variant {
- Number(i32),
- Text(String),
-}
-fn work_on_variant(mut var: Variant, text: String) {
- let mut ptr: &mut i32;
- match var {
- Variant::Number(ref mut n) => ptr = n,
- Variant::Text(_) => return,
- }
- /* var = Variant::Text(text); */ /* BAD! */
- *ptr = 1337;
-}
-
+++ /dev/null
-// Rust-101, Part 06: Copy, Lifetimes
-// ==================================
-
-// We continue to work on our `BigInt`, so we start by importing what we already established.
-use part05::BigInt;
-
-// With `BigInt` being about numbers, we should be able to write a version of `vec_min`
-// that computes the minimum of a list of `BigInt`. First, we have to write `min` for `BigInt`.
-impl BigInt {
- fn min_try1(self, other: Self) -> Self {
- debug_assert!(self.test_invariant() && other.test_invariant());
- // Now our assumption of having no trailing zeros comes in handy:
- // If the lengths of the two numbers differ, we already know which is larger.
- if self.data.len() < other.data.len() {
- self
- } else if self.data.len() > other.data.len() {
- other
- } else {
- // **Exercise 06.1**: Fill in this code.
- unimplemented!()
- }
- }
-}
-
-// Now we can write `vec_min`.
-fn vec_min(v: &Vec<BigInt>) -> Option<BigInt> {
- let mut min: Option<BigInt> = None;
- // If `v` is a shared reference to a vector, then the default for iterating over it is to call `iter`, the iterator that borrows the elements.
- for e in v {
- let e = e.clone();
- unimplemented!()
- }
- min
-}
-
-// ## `Copy` types
-
-use part02::{SomethingOrNothing,Something,Nothing};
-impl<T: Copy> Copy for SomethingOrNothing<T> {}
-
-
-// ## Lifetimes
-
-fn head<T>(v: &Vec<T>) -> Option<&T> {
- if v.len() > 0 {
- unimplemented!()
- } else {
- None
- }
-}
-// Technically, we are returning a pointer to the first element. But doesn't that mean that callers have to be
-// careful? Imagine `head` would be a C++ function, and we would write the following code.
-/*
- int foo(std::vector<int> v) {
- int *first = head(v);
- v.push_back(42);
- return *first;
- }
-*/
-fn rust_foo(mut v: Vec<i32>) -> i32 {
- let first: Option<&i32> = head(&v);
- /* v.push(42); */
- *first.unwrap()
-}
-
-
+++ /dev/null
-// Rust-101, Part 07: Operator Overloading, Tests, Formatting
-// ==========================================================
-
-pub use part05::BigInt;
-
-// With our new knowledge of lifetimes, we are now able to write down the desired type of `min`:
-pub trait Minimum {
- fn min<'a>(&'a self, other: &'a Self) -> &'a Self;
-}
-
-pub fn vec_min<T: Minimum>(v: &Vec<T>) -> Option<&T> {
- let mut min: Option<&T> = None;
- for e in v {
- min = Some(match min {
- None => e,
- Some(n) => n.min(e)
- });
- }
- min
-}
-
-// **Exercise 07.1**: For our `vec_min` to be usable with `BigInt`, you will have to provide an implementation of
-// `Minimum`. You should be able to pretty much copy the code you wrote for exercise 06.1. You should *not*
-// make any copies of `BigInt`!
-impl Minimum for BigInt {
- fn min<'a>(&'a self, other: &'a Self) -> &'a Self {
- unimplemented!()
- }
-}
-
-// ## Operator Overloading
-
-impl PartialEq for BigInt {
- #[inline]
- fn eq(&self, other: &BigInt) -> bool {
- debug_assert!(self.test_invariant() && other.test_invariant());
- unimplemented!()
- }
-}
-
-
-// Now we can compare `BigInt`s. Rust treats `PartialEq` special in that it is wired to the operator `==`:
-fn compare_big_ints() {
- let b1 = BigInt::new(13);
- let b2 = BigInt::new(37);
- println!("b1 == b1: {} ; b1 == b2: {}; b1 != b2: {}", b1 == b1, b1 == b2, b1 != b2);
-}
-
-// ## Testing
-// With our equality test written, we are now ready to write our first testcase.
-// the `test` attribute. `assert!` is like `debug_assert!`, but does not get compiled away in a release build.
-#[test]
-fn test_min() {
- let b1 = BigInt::new(1);
- let b2 = BigInt::new(42);
- let b3 = BigInt::from_vec(vec![0, 1]);
-
- unimplemented!()
-}
-// Now run `cargo test` to execute the test. If you implemented `min` correctly, it should all work!
-
-// ## Formatting
-
-// All formating is handled by [`std::fmt`](https://doc.rust-lang.org/std/fmt/index.html). I won't explain
-// all the details, and refer you to the documentation instead.
-use std::fmt;
-
-impl fmt::Debug for BigInt {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.data.fmt(f)
- }
-}
-
-// Now we are ready to use `assert_eq!` to test `vec_min`.
-/*#[test]*/
-fn test_vec_min() {
- let b1 = BigInt::new(1);
- let b2 = BigInt::new(42);
- let b3 = BigInt::from_vec(vec![0, 1]);
-
- let v1 = vec![b2.clone(), b1.clone(), b3.clone()];
- let v2 = vec![b2.clone(), b3.clone()];
- unimplemented!()
-}
-
-// **Exercise 07.1**: Add some more testcases. In particular, make sure you test the behavior of
-// `vec_min` on an empty vector. Also add tests for `BigInt::from_vec` (in particular, removing
-// trailing zeros). Finally, break one of your functions in a subtle way and watch the test fail.
-//
-// **Exercise 07.2**: Go back to your good ol' `SomethingOrNothing`, and implement `Display` for it. (This will,
-// of course, need a `Display` bound on `T`.) Then you should be able to use them with `println!` just like you do
-// with numbers, and get rid of the inherent functions to print `SomethingOrNothing<i32>` and `SomethingOrNothing<f32>`.
-
+++ /dev/null
-// Rust-101, Part 08: Associated Types, Modules
-// ============================================
-
-use std::{cmp,ops};
-use part05::BigInt;
-
-
-// So, let us write a function to "add with carry", and give it the appropriate type. Notice Rust's native support for pairs.
-fn overflowing_add(a: u64, b: u64, carry: bool) -> (u64, bool) {
- let sum = a.wrapping_add(b);
- // If an overflow happened, then the sum will be smaller than *both* summands. Without an overflow, of course, it will be
- // at least as large as both of them. So, let's just pick one and check.
- if sum >= a {
- // The addition did not overflow. <br/>
- // **Exercise 08.1**: Write the code to handle adding the carry in this case.
- unimplemented!()
- } else {
- // Otherwise, the addition *did* overflow. It is impossible for the addition of the carry
- // to overflow again, as we are just adding 0 or 1.
- unimplemented!()
- }
-}
-
-// `overflow_add` is a sufficiently intricate function that a test case is justified.
-// This should also help you to check your solution of the exercise.
-/*#[test]*/
-fn test_overflowing_add() {
- assert_eq!(overflowing_add(10, 100, false), (110, false));
- assert_eq!(overflowing_add(10, 100, true), (111, false));
- assert_eq!(overflowing_add(1 << 63, 1 << 63, false), (0, true));
- assert_eq!(overflowing_add(1 << 63, 1 << 63, true), (1, true));
- assert_eq!(overflowing_add(1 << 63, (1 << 63) -1 , true), (0, true));
-}
-
-// ## Associated Types
-impl ops::Add<BigInt> for BigInt {
-
- // Here, we choose the result type to be again `BigInt`.
- type Output = BigInt;
-
- // Now we can write the actual function performing the addition.
- fn add(self, rhs: BigInt) -> Self::Output {
- // We know that the result will be *at least* as long as the longer of the two operands,
- // so we can create a vector with sufficient capacity to avoid expensive reallocations.
- let max_len = cmp::max(self.data.len(), rhs.data.len());
- let mut result_vec:Vec<u64> = Vec::with_capacity(max_len);
- let mut carry = false; /* the current carry bit */
- for i in 0..max_len {
- let lhs_val = if i < self.data.len() { self.data[i] } else { 0 };
- let rhs_val = if i < rhs.data.len() { rhs.data[i] } else { 0 };
- // Compute next digit and carry. Then, store the digit for the result, and the carry for later.
- unimplemented!()
- }
- // **Exercise 08.2**: Handle the final `carry`, and return the sum.
- unimplemented!()
- }
-}
-
-// ## Traits and reference types
-
-// Writing this out becomes a bit tedious, because trait implementations (unlike functions) require full explicit annotation
-// of lifetimes. Make sure you understand exactly what the following definition says. Notice that we can implement a trait for
-// a reference type!
-impl<'a, 'b> ops::Add<&'a BigInt> for &'b BigInt {
- type Output = BigInt;
- fn add(self, rhs: &'a BigInt) -> Self::Output {
- // **Exercise 08.3**: Implement this function.
- unimplemented!()
- }
-}
-
-// **Exercise 08.4**: Implement the two missing combinations of arguments for `Add`. You should not have to duplicate the implementation.
-
-// ## Modules
-
-// Rust calls a bunch of definitions that are grouped together a *module*. You can put the tests in a submodule as follows.
-#[cfg(test)]
-mod tests {
- use part05::BigInt;
-
- /*#[test]*/
- fn test_add() {
- let b1 = BigInt::new(1 << 32);
- let b2 = BigInt::from_vec(vec![0, 1]);
-
- assert_eq!(&b1 + &b2, BigInt::from_vec(vec![1 << 32, 1]));
- // **Exercise 08.5**: Add some more cases to this test.
- }
-}
-
-// **Exercise 08.6**: Write a subtraction function, and testcases for it. Decide for yourself how you want to handle negative results.
-// For example, you may want to return an `Option`, to panic, or to return `0`.
-
+++ /dev/null
-// Rust-101, Part 09: Iterators
-// ============================
-
-use part05::BigInt;
-
-
-pub struct Iter<'a> {
- num: &'a BigInt,
- idx: usize, // the index of the last number that was returned
-}
-
-// Now we are equipped to implement `Iterator` for `Iter`.
-impl<'a> Iterator for Iter<'a> {
- // We choose the type of things that we iterate over to be the type of digits, i.e., `u64`.
- type Item = u64;
-
- fn next(&mut self) -> Option<u64> {
- // First, check whether there's any more digits to return.
- if self.idx == 0 {
- // We already returned all the digits, nothing to do.
- unimplemented!()
- } else {
- // Otherwise: Decrement, and return next digit.
- unimplemented!()
- }
- }
-}
-
-// All we need now is a function that creates such an iterator for a given `BigInt`.
-impl BigInt {
- fn iter(&self) -> Iter {
- unimplemented!()
- }
-}
-
-// We are finally ready to iterate! Remember to edit `main.rs` to run this function.
-pub fn main() {
- let b = BigInt::new(1 << 63) + BigInt::new(1 << 16) + BigInt::new(1 << 63);
- for digit in b.iter() {
- println!("{}", digit);
- }
-}
-
-// Of course, we don't have to use `for` to apply the iterator. We can also explicitly call `next`.
-fn print_digits_v1(b: &BigInt) {
- let mut iter = b.iter();
- loop {
- // Each time we go through the loop, we analyze the next element presented by the iterator - until it stops.
- unimplemented!()
- }
-}
-
-fn print_digits_v2(b: &BigInt) {
- let mut iter = b.iter();
- while let Some(digit) = iter.next() {
- println!("{}", digit)
- }
-}
-
-// **Exercise 09.1**: Write a testcase for the iterator, making sure it yields the corrects numbers.
-//
-// **Exercise 09.2**: Write a function `iter_ldf` that iterators over the digits with the least-significant
-// digits coming first. Write a testcase for it.
-
-// ## Iterator invalidation and lifetimes
-
-fn iter_invalidation_demo() {
- let mut b = BigInt::new(1 << 63) + BigInt::new(1 << 16) + BigInt::new(1 << 63);
- for digit in b.iter() {
- println!("{}", digit);
- /*b = b + BigInt::new(1);*/ /* BAD! */
- }
-}
-
-// ## Iterator conversion trait
-
-impl<'a> IntoIterator for &'a BigInt {
- type Item = u64;
- type IntoIter = Iter<'a>;
- fn into_iter(self) -> Iter<'a> {
- self.iter()
- }
-}
-// With this in place, you can now replace `b.iter()` in `main` by `&b`. Go ahead and try it! <br/>
-
+++ /dev/null
-// Rust-101, Part 10: Closures
-// ===========================
-
-use std::fmt;
-use part05::BigInt;
-
-
-// So, let us define a trait that demands that the type provides some method `do_action` on digits.
-trait Action {
- fn do_action(&mut self, digit: u64);
-}
-
-// Now we can write a function that takes some `a` of a type `A` such that we can call `do_action` on `a`, passing it every digit.
-impl BigInt {
- fn act_v1<A: Action>(&self, mut a: A) {
- for digit in self {
- unimplemented!()
- }
- }
-}
-
-struct PrintWithString {
- prefix: String,
-}
-
-impl Action for PrintWithString {
- // Here we perform the actual printing of the prefix and the digit. We're not making use of our ability to
- // change `self` here, but we could replace the prefix if we wanted.
- fn do_action(&mut self, digit: u64) {
- unimplemented!()
- }
-}
-
-// Finally, this function takes a `BigInt` and a prefix, and prints the digits with the given prefix.
-fn print_with_prefix_v1(b: &BigInt, prefix: String) {
- let my_action = PrintWithString { prefix: prefix };
- b.act_v1(my_action);
-}
-
-// Here's a small main function, demonstrating the code above in action. Remember to edit `main.rs` to run it.
-pub fn main() {
- let bignum = BigInt::new(1 << 63) + BigInt::new(1 << 16) + BigInt::new(1 << 63);
- print_with_prefix_v1(&bignum, "Digit: ".to_string());
-}
-
-// ## Closures
-
-// This defines `act` very similar to above, but now we demand `A` to be the type of a closure that mutates its borrowed environment,
-// takes a digit, and returns nothing.
-impl BigInt {
- fn act<A: FnMut(u64)>(&self, mut a: A) {
- for digit in self {
- // We can call closures as if they were functions - but really, what's happening here is translated to essentially what we wrote above, in `act_v1`.
- unimplemented!()
- }
- }
-}
-
-// Now that we saw how to write a function that operates on closures, let's see how to write a closure.
-pub fn print_with_prefix(b: &BigInt, prefix: String) {
- b.act(|digit| println!("{}{}", prefix, digit) );
-}
-// You can change `main` to call this function, and you should notice - nothing, no difference in behavior.
-// But we wrote much less boilerplate code!
-
-// Remember that we decided to use the `FnMut` trait above? This means our closure could actually mutate its environment.
-// For example, we can use that to count the digits as they are printed.
-pub fn print_and_count(b: &BigInt) {
- let mut count: usize = 0;
- b.act(|digit| { println!("{}: {}", count, digit); count = count +1; } );
- println!("There are {} digits", count);
-}
-
-// ## Fun with iterators and closures
-
-// Let's say we want to write a function that increments every entry of a `Vec` by some number, then looks for numbers larger than some threshold, and prints them.
-fn inc_print_even(v: &Vec<i32>, offset: i32, threshold: i32) {
- for i in v.iter().map(|n| *n + offset).filter(|n| *n > threshold) {
- println!("{}", i);
- }
-}
-
-// Sometimes it is useful to know both the position of some element in a list, and its value. That's where the `enumerate` function helps.
-fn print_enumerated<T: fmt::Display>(v: &Vec<T>) {
- for (i, t) in v.iter().enumerate() {
- println!("Position {}: {}", i, t);
- }
-}
-
-// And as a final example, one can also collect all elements of an iterator, and put them, e.g., in a vector.
-fn filter_vec_by_divisor(v: &Vec<i32>, divisor: i32) -> Vec<i32> {
- unimplemented!()
-}
-
-// **Exercise 10.1**: Look up the [documentation of `Iterator`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html) to learn about more functions
-// that can act on iterators. Try using some of them. What about a function that sums the even numbers of an iterator? Or a function that computes the
-// product of those numbers that sit at odd positions? A function that checks whether a vector contains a certain number? Whether all numbers are
-// smaller than some threshold? Be creative!
-
+++ /dev/null
-// Rust-101, Part 11: Trait Objects, Box, Lifetime bounds
-// ======================================================
-
-
-// For now, we just decide that the callbacks have an argument of type `i32`.
-struct CallbacksV1<F: FnMut(i32)> {
- callbacks: Vec<F>,
-}
-
-/* struct CallbacksV2 {
- callbacks: Vec<FnMut(i32)>,
-} */
-
-pub struct Callbacks {
- callbacks: Vec<Box<FnMut(i32)>>,
-}
-
-impl Callbacks {
- // Now we can provide some functions. The constructor should be straight-forward.
- pub fn new() -> Self {
- unimplemented!()
- }
-
- // Registration simply stores the callback.
- pub fn register(&mut self, callback: Box<FnMut(i32)>) {
- self.callbacks.push(callback);
- }
-
- // We can also write a generic version of `register`, such that it will be instantiated with some concrete closure type `F`
- // and do the creation of the `Box` and the conversion from `F` to `FnMut(i32)` itself.
-
- pub fn register_generic<F: FnMut(i32)+'static>(&mut self, callback: F) {
- unimplemented!()
- }
-
- // And here we call all the stored callbacks.
- pub fn call(&mut self, val: i32) {
- // Since they are of type `FnMut`, we need to mutably iterate.
- for callback in self.callbacks.iter_mut() {
- unimplemented!()
- }
- }
-}
-
-// Now we are ready for the demo. Remember to edit `main.rs` to run it.
-pub fn main() {
- let mut c = Callbacks::new();
- c.register(Box::new(|val| println!("Callback 1: {}", val)));
- c.call(0);
-
- {
- let mut count: usize = 0;
- c.register_generic(move |val| {
- count = count+1;
- println!("Callback 2: {} ({}. time)", val, count);
- } );
- }
- c.call(1); c.call(2);
-}
-
-
-// **Exercise 11.1**: We made the arbitrary choice of using `i32` for the arguments. Generalize the data structures above
-// to work with an arbitrary type `T` that's passed to the callbacks. Since you need to call multiple callbacks with the
-// same `t: T`, you will either have to restrict `T` to `Copy` types, or pass a reference.
-
+++ /dev/null
-// Rust-101, Part 12: Rc, Interior Mutability, Cell, RefCell
-// =========================================================
-
-use std::rc::Rc;
-use std::cell::{Cell, RefCell};
-
-
-
-#[derive(Clone)]
-struct Callbacks {
- callbacks: Vec<Rc<Fn(i32)>>,
-}
-
-impl Callbacks {
- pub fn new() -> Self {
- Callbacks { callbacks: Vec::new() }
- }
-
- // Registration works just like last time, except that we are creating an `Rc` now.
- pub fn register<F: Fn(i32)+'static>(&mut self, callback: F) {
- unimplemented!()
- }
-
- pub fn call(&self, val: i32) {
- // We only need a shared iterator here. Since `Rc` is a smart pointer, we can directly call the callback.
- for callback in self.callbacks.iter() {
- unimplemented!()
- }
- }
-}
-
-// Time for a demo!
-fn demo(c: &mut Callbacks) {
- c.register(|val| println!("Callback 1: {}", val));
- c.call(0); c.clone().call(1);
-}
-
-pub fn main() {
- let mut c = Callbacks::new();
- demo(&mut c);
-}
-
-// ## Interior Mutability
-
-// So, let us put our counter in a `Cell`, and replicate the example from the previous part.
-fn demo_cell(c: &mut Callbacks) {
- {
- let count = Cell::new(0);
- // Again, we have to move ownership of the `count` into the environment closure.
- c.register(move |val| {
- // In here, all we have is a shared reference of our environment. But that's good enough for the `get` and `set` of the cell!
- let new_count = count.get()+1;
- count.set(new_count);
- println!("Callback 2: {} ({}. time)", val, new_count);
- } );
- }
-
- c.call(2); c.clone().call(3);
-}
-
-
-// ## `RefCell`
-
-// Our final version of `Callbacks` puts the closure environment into a `RefCell`.
-#[derive(Clone)]
-struct CallbacksMut {
- callbacks: Vec<Rc<RefCell<FnMut(i32)>>>,
-}
-
-impl CallbacksMut {
- pub fn new() -> Self {
- CallbacksMut { callbacks: Vec::new() }
- }
-
- pub fn register<F: FnMut(i32)+'static>(&mut self, callback: F) {
- unimplemented!()
- }
-
- pub fn call(&mut self, val: i32) {
- for callback in self.callbacks.iter() {
- // We have to *explicitly* borrow the contents of a `RefCell` by calling `borrow` or `borrow_mut`.
- let mut closure = callback.borrow_mut();
- // Unfortunately, Rust's auto-dereference of pointers is not clever enough here. We thus have to explicitly
- // dereference the smart pointer and obtain a mutable reference to the content.
- (&mut *closure)(val);
- }
- }
-}
-
-// Now we can repeat the demo from the previous part - but this time, our `CallbacksMut` type
-// can be cloned.
-fn demo_mut(c: &mut CallbacksMut) {
- c.register(|val| println!("Callback 1: {}", val));
- c.call(0);
-
- {
- let mut count: usize = 0;
- c.register(move |val| {
- count = count+1;
- println!("Callback 2: {} ({}. time)", val, count);
- } );
- }
- c.call(1); c.clone().call(2);
-}
-
-// **Exercise 12.1**: Write some piece of code using only the available, public interface of `CallbacksMut` such that a reentrant call to a closure
-// is happening, and the program panics because the `RefCell` refuses to hand out a second mutable borrow of the closure's environment.
-
+++ /dev/null
-// Rust-101, Part 13: Concurrency, Arc, Send
-// =========================================
-
-use std::io::prelude::*;
-use std::{io, fs, thread};
-use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
-use std::sync::Arc;
-
-
-// Before we come to the actual code, we define a data-structure `Options` to store all the information we need
-// to complete the job: Which files to work on, which pattern to look for, and how to output. <br/>
-#[derive(Clone,Copy)]
-pub enum OutputMode {
- Print,
- SortAndPrint,
- Count,
-}
-use self::OutputMode::*;
-
-pub struct Options {
- pub files: Vec<String>,
- pub pattern: String,
- pub output_mode: OutputMode,
-}
-
-
-// The first function reads the files, and sends every line over the `out_channel`.
-fn read_files(options: Arc<Options>, out_channel: SyncSender<String>) {
- for file in options.files.iter() {
- // First, we open the file, ignoring any errors.
- let file = fs::File::open(file).unwrap();
- // Then we obtain a `BufReader` for it, which provides the `lines` function.
- let file = io::BufReader::new(file);
- for line in file.lines() {
- let line = line.unwrap();
- // Now we send the line over the channel, ignoring the possibility of `send` failing.
- out_channel.send(line).unwrap();
- }
- }
- // When we drop the `out_channel`, it will be closed, which the other end can notice.
-}
-
-// The second function filters the lines it receives through `in_channel` with the pattern, and sends
-// matches via `out_channel`.
-fn filter_lines(options: Arc<Options>,
- in_channel: Receiver<String>,
- out_channel: SyncSender<String>) {
- // We can simply iterate over the channel, which will stop when the channel is closed.
- for line in in_channel.iter() {
- // `contains` works on lots of types of patterns, but in particular, we can use it to test whether
- // one string is contained in another. This is another example of Rust using traits as substitute for overloading.
- if line.contains(&options.pattern) {
- unimplemented!()
- }
- }
-}
-
-// The third function performs the output operations, receiving the relevant lines on its `in_channel`.
-fn output_lines(options: Arc<Options>, in_channel: Receiver<String>) {
- match options.output_mode {
- Print => {
- // Here, we just print every line we see.
- for line in in_channel.iter() {
- unimplemented!()
- }
- },
- Count => {
- // We are supposed to count the number of matching lines. There's a convenient iterator adapter that
- // we can use for this job.
- unimplemented!()
- },
- SortAndPrint => {
- // We are asked to sort the matching lines before printing. So let's collect them all in a local vector...
- let mut data: Vec<String> = in_channel.iter().collect();
- // ...and implement the actual sorting later.
- unimplemented!()
- }
- }
-}
-
-// With the operations of the three threads defined, we can now implement a function that performs grepping according
-// to some given options.
-pub fn run(options: Options) {
- // We move the `options` into an `Arc`, as that's what the thread workers expect.
- let options = Arc::new(options);
-
- // This sets up the channels. We use a `sync_channel` with buffer-size of 16 to avoid needlessly filling RAM.
- let (line_sender, line_receiver) = sync_channel(16);
- let (filtered_sender, filtered_receiver) = sync_channel(16);
-
- // Spawn the read thread: `thread::spawn` takes a closure that is run in a new thread.
- let options1 = options.clone();
- let handle1 = thread::spawn(move || read_files(options1, line_sender));
-
- // Same with the filter thread.
- let options2 = options.clone();
- let handle2 = thread::spawn(move || {
- filter_lines(options2, line_receiver, filtered_sender)
- });
-
- // And the output thread.
- let options3 = options.clone();
- let handle3 = thread::spawn(move || output_lines(options3, filtered_receiver));
-
- // Finally, wait until all three threads did their job.
- handle1.join().unwrap();
- handle2.join().unwrap();
- handle3.join().unwrap();
-}
-
-// Now we have all the pieces together for testing our rgrep with some hard-coded options.
-pub fn main() {
- let options = Options {
- files: vec!["src/part10.rs".to_string(),
- "src/part11.rs".to_string(),
- "src/part12.rs".to_string()],
- pattern: "let".to_string(),
- output_mode: Print
- };
- run(options);
-}
-
-// **Exercise 13.1**: Change rgrep such that it prints not only the matching lines, but also the name of the file
-// and the number of the line in the file. You will have to change the type of the channels from `String` to something
-// that records this extra information.
-
-
-
+++ /dev/null
-// Rust-101, Part 14: Slices, Arrays, External Dependencies
-// ========================================================
-
-
-// ## Slices
-
-pub fn sort<T: PartialOrd>(data: &mut [T]) {
- if data.len() < 2 { return; }
-
- // We decide that the element at 0 is our pivot, and then we move our cursors through the rest of the slice,
- // making sure that everything on the left is no larger than the pivot, and everything on the right is no smaller.
- let mut lpos = 1;
- let mut rpos = data.len();
- /* Invariant: pivot is data[0]; everything with index (0,lpos) is <= pivot;
- [rpos,len) is >= pivot; lpos < rpos */
- loop {
- // **Exercise 14.1**: Complete this Quicksort loop. You can use `swap` on slices to swap two elements. Write a
- // test function for `sort`.
- unimplemented!()
- }
-
- // Once our cursors met, we need to put the pivot in the right place.
- data.swap(0, lpos-1);
-
- // Finally, we split our slice to sort the two halves. The nice part about slices is that splitting them is cheap:
- let (part1, part2) = data.split_at_mut(lpos);
- unimplemented!()
-}
-
-// **Exercise 14.2**: Since `String` implements `PartialEq`, you can now change the function `output_lines` in the previous part
-// to call the sort function above. If you did exercise 13.1, you will have slightly more work. Make sure you sort by the matched line
-// only, not by filename or line number!
-
-// Now, we can sort, e.g., an vector of numbers.
-fn sort_nums(data: &mut Vec<i32>) {
- sort(&mut data[..]);
-}
-
-// ## Arrays
-fn sort_array() {
- let mut array_of_data: [f64; 5] = [1.0, 3.4, 12.7, -9.12, 0.1];
- sort(&mut array_of_data);
-}
-
-// ## External Dependencies
-
-
-// I disabled the following module (using a rather bad hack), because it only compiles if `docopt` is linked.
-// Remove the attribute of the `rgrep` module to enable compilation.
-#[cfg(feature = "disabled")]
-pub mod rgrep {
- // Now that `docopt` is linked, we can first add it to the namespace with `extern crate` and then import shorter names with `use`.
- // We also import some other pieces that we will need.
- extern crate docopt;
- use self::docopt::Docopt;
- use part13::{run, Options, OutputMode};
- use std::process;
-
- // The `USAGE` string documents how the program is to be called. It's written in a format that `docopt` can parse.
- static USAGE: &'static str = "
-Usage: rgrep [-c] [-s] <pattern> <file>...
-
-Options:
- -c, --count Count number of matching lines (rather than printing them).
- -s, --sort Sort the lines before printing.
-";
-
- // This function extracts the rgrep options from the command-line arguments.
- fn get_options() -> Options {
- // This parses `argv` and exit the program with an error message if it fails. The code is taken from the [`docopt` documentation](http://burntsushi.net/rustdoc/docopt/). <br/>
- let args = Docopt::new(USAGE).and_then(|d| d.parse()).unwrap_or_else(|e| e.exit());
- // Now we can get all the values out.
- let count = args.get_bool("-c");
- let sort = args.get_bool("-s");
- let pattern = args.get_str("<pattern>");
- let files = args.get_vec("<file>");
- if count && sort {
- println!("Setting both '-c' and '-s' at the same time does not make any sense.");
- process::exit(1);
- }
-
- // We need to make the strings owned to construct the `Options` instance.
- let mode = if count {
- OutputMode::Count
- } else if sort {
- OutputMode::SortAndPrint
- } else {
- OutputMode::Print
- };
- Options {
- files: files.iter().map(|file| file.to_string()).collect(),
- pattern: pattern.to_string(),
- output_mode: mode,
- }
- }
-
- // Finally, we can call the `run` function from the previous part on the options extracted using `get_options`. Edit `main.rs` to call this function.
- // You can now use `cargo run -- <pattern> <files>` to call your program, and see the argument parser and the threads we wrote previously in action!
- pub fn main() {
- unimplemented!()
- }
-}
-
-// **Exercise 14.3**: Wouldn't it be nice if rgrep supported regular expressions? There's already a crate that does all the parsing and matching on regular
-// expression, it's called [regex](https://crates.io/crates/regex). Add this crate to the dependencies of your workspace, add an option ("-r") to switch
-// the pattern to regular-expression mode, and change `filter_lines` to honor this option. The documentation of regex is available from its crates.io site.
-// (You won't be able to use the `regex!` macro if you are on the stable or beta channel of Rust. But it wouldn't help for our use-case anyway.)
-
+++ /dev/null
-// Rust-101, Part 15: Mutex, Interior Mutability (cont.), RwLock, Sync
-// ===================================================================
-
-use std::sync::{Arc, Mutex};
-use std::thread;
-use std::time::Duration;
-
-
-// The derived `Clone` implementation will clone the `Arc`, so all clones will actually talk about the same counter.
-#[derive(Clone)]
-struct ConcurrentCounter(Arc<Mutex<usize>>);
-
-impl ConcurrentCounter {
- // The constructor just wraps the constructors of `Arc` and `Mutex`.
- pub fn new(val: usize) -> Self {
- unimplemented!()
- }
-
- // The core operation is, of course, `increment`.
- pub fn increment(&self, by: usize) {
- // `lock` on a mutex returns a guard, very much like `RefCell`. The guard gives access to the data contained in the mutex.
- let mut counter = self.0.lock().unwrap();
- *counter = *counter + by;
- }
-
- // The function `get` returns the current value of the counter.
- pub fn get(&self) -> usize {
- unimplemented!()
- }
-}
-
-// Now our counter is ready for action.
-pub fn main() {
- let counter = ConcurrentCounter::new(0);
-
- // We clone the counter for the first thread, which increments it by 2 every 15ms.
- let counter1 = counter.clone();
- let handle1 = thread::spawn(move || {
- for _ in 0..10 {
- thread::sleep(Duration::from_millis(15));
- counter1.increment(2);
- }
- });
-
- // The second thread increments the counter by 3 every 20ms.
- let counter2 = counter.clone();
- let handle2 = thread::spawn(move || {
- for _ in 0..10 {
- thread::sleep(Duration::from_millis(20));
- counter2.increment(3);
- }
- });
-
- // Now we watch the threads working on the counter.
- for _ in 0..50 {
- thread::sleep(Duration::from_millis(5));
- println!("Current value: {}", counter.get());
- }
-
- // Finally, we wait for all the threads to finish to be sure we can catch the counter's final value.
- handle1.join().unwrap();
- handle2.join().unwrap();
- println!("Final value: {}", counter.get());
-}
-
-// **Exercise 15.1**: Add an operation `compare_and_inc(&self, test: usize, by: usize)` that increments the counter by
-// `by` *only if* the current value is `test`.
-//
-// **Exercise 15.2**: Rather than panicking in case the lock is poisoned, we can use `into_inner` on the error to recover
-// the data inside the lock. Change the code above to do that. Try using `unwrap_or_else` for this job.
-
-
-// **Exercise 15.3**: Change the code above to use `RwLock`, such that multiple calls to `get` can be executed at the same time.
-
-
+++ /dev/null
-// Rust-101, Part 16: Unsafe Rust, Drop
-// ====================================
-
-use std::ptr;
-use std::mem;
-use std::marker::PhantomData;
-
-
-// A node of the list consists of the data, and two node pointers for the predecessor and successor.
-struct Node<T> {
- next: NodePtr<T>,
- prev: NodePtr<T>,
- data: T,
-}
-// A node pointer is a *mutable raw pointer* to a node.
-type NodePtr<T> = *mut Node<T>;
-
-// The linked list itself stores pointers to the first and the last node. In addition, we tell Rust that this type
-// will own data of type `T`.
-pub struct LinkedList<T> {
- first: NodePtr<T>,
- last: NodePtr<T>,
- _marker: PhantomData<T>,
-}
-
-
-unsafe fn raw_into_box<T>(r: *mut T) -> Box<T> {
- mem::transmute(r)
-}
-fn box_into_raw<T>(b: Box<T>) -> *mut T {
- unsafe { mem::transmute(b) }
-}
-
-impl<T> LinkedList<T> {
- // A new linked list just contains null pointers. `PhantomData` is how we construct any `PhantomData<T>`.
- pub fn new() -> Self {
- LinkedList { first: ptr::null_mut(), last: ptr::null_mut(), _marker: PhantomData }
- }
-
- // This function adds a new node to the end of the list.
- pub fn push_back(&mut self, t: T) {
- // Create the new node, and make it a raw pointer.
- let new = Box::new( Node { data: t, next: ptr::null_mut(), prev: self.last } );
- let new = box_into_raw(new);
- // Update other pointers to this node.
- if self.last.is_null() {
- debug_assert!(self.first.is_null());
- // The list is currently empty, so we have to update the head pointer.
- unimplemented!()
- } else {
- debug_assert!(!self.first.is_null());
- // We have to update the `next` pointer of the tail node.
- unimplemented!()
- }
- // Make this the last node.
- self.last = new;
- }
-
- // **Exercise 16.1**: Add some more operations to `LinkedList`: `pop_back`, `push_front` and `pop_front`.
- // Add testcases for `push_back` and all of your functions. The `pop` functions should take `&mut self`
- // and return `Option<T>`.
-
- // Next, we are going to provide an iterator.
- pub fn iter_mut(&mut self) -> IterMut<T> {
- IterMut { next: self.first, _marker: PhantomData }
- }
-}
-
-
-pub struct IterMut<'a, T> where T: 'a {
- next: NodePtr<T>,
- _marker: PhantomData<&'a mut LinkedList<T>>,
-}
-
-impl<'a, T> Iterator for IterMut<'a, T> {
- type Item = &'a mut T;
-
- fn next(&mut self) -> Option<Self::Item> {
- // The actual iteration is straight-forward: Once we reached a null pointer, we are done.
- if self.next.is_null() {
- None
- } else {
- // Otherwise, we can convert the next pointer to a reference, get a reference to the data
- // and update the iterator.
- let next = unsafe { &mut *self.next };
- let ret = &mut next.data;
- unimplemented!()
- }
- }
-}
-
-
-// **Exercise 16.2**: Add a method `iter` and a type `Iter` providing iteration for shared references.
-// Add testcases for both kinds of iterators.
-
-// ## `Drop`
-
-impl<T> Drop for LinkedList<T> {
- // The destructor itself is a method which takes `self` in mutably borrowed form. It cannot own `self`, because then
- // the destructor of `self` would be called at the end of the function, resulting in endless recursion.
- fn drop(&mut self) {
- let mut cur_ptr = self.first;
- while !cur_ptr.is_null() {
- // In the destructor, we just iterate over the entire list, successively obtaining ownership
- // (`Box`) of every node. When the box is dropped, it will call the destructor on `data` if
- // necessary, and subsequently free the node on the heap.
- let cur = unsafe { raw_into_box(cur_ptr) };
- cur_ptr = cur.next;
- drop(cur);
- }
- }
-}
-
-// ## The End
-