From: Ralf Jung Date: Mon, 13 Jun 2016 18:18:26 +0000 (+0200) Subject: remove workspace from the repository, instead generate a zipfile to upload X-Git-Url: https://git.ralfj.de/rust-101.git/commitdiff_plain/ab7f9b241429bd675b437d2437799de75d2f409b remove workspace from the repository, instead generate a zipfile to upload --- diff --git a/Makefile b/Makefile index 1674606..c157dac 100644 --- a/Makefile +++ b/Makefile @@ -22,10 +22,10 @@ docs/%.html: .tmp/docs/%.rs # 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. @@ -34,6 +34,10 @@ workspace/src/%.rs: src/%.rs Makefile dup-unimpl.sed 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 diff --git a/README.md b/README.md index 7895b8b..acef159 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ # 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 diff --git a/docs/.gitignore b/docs/.gitignore index fe099b8..e48f428 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,3 +1,4 @@ fonts/ *.html +*.zip pycco.css diff --git a/src/main.rs b/src/main.rs index 69d9ca7..b4ee6ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,13 +42,13 @@ // [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 // -------------- diff --git a/src/part00.rs b/src/part00.rs index 00eb4d1..d126a17 100644 --- a/src/part00.rs +++ b/src/part00.rs @@ -113,4 +113,4 @@ pub fn main() { //@ 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) diff --git a/src/part01.rs b/src/part01.rs index fa06a80..56272b2 100644 --- a/src/part01.rs +++ b/src/part01.rs @@ -106,4 +106,4 @@ pub fn main() { // **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) diff --git a/src/part02.rs b/src/part02.rs index 44a20d4..6a01087 100644 --- a/src/part02.rs +++ b/src/part02.rs @@ -146,4 +146,4 @@ pub fn main() { // **Exercise 02.1**: Change your program such that it computes the minimum of a `Vec` (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) diff --git a/src/part03.rs b/src/part03.rs index 3a35aa7..2e818d1 100644 --- a/src/part03.rs +++ b/src/part03.rs @@ -116,4 +116,4 @@ impl SomethingOrNothing { // **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) diff --git a/src/part04.rs b/src/part04.rs index 31e8056..cb101fe 100644 --- a/src/part04.rs +++ b/src/part04.rs @@ -143,4 +143,4 @@ fn mutable_ref_demo() { // 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) diff --git a/src/part05.rs b/src/part05.rs index 8a2cd90..eaad980 100644 --- a/src/part05.rs +++ b/src/part05.rs @@ -147,4 +147,4 @@ fn work_on_variant(mut var: Variant, text: String) { //@ 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) diff --git a/src/part06.rs b/src/part06.rs index 9552eb5..4c0e6ce 100644 --- a/src/part06.rs +++ b/src/part06.rs @@ -148,4 +148,4 @@ fn rust_foo(mut v: Vec) -> i32 { //@ 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) diff --git a/src/part07.rs b/src/part07.rs index 2d88390..e395ba6 100644 --- a/src/part07.rs +++ b/src/part07.rs @@ -147,4 +147,4 @@ fn test_vec_min() { // 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` and `SomethingOrNothing`. -//@ [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) diff --git a/src/part08.rs b/src/part08.rs index 340de24..c18cbeb 100644 --- a/src/part08.rs +++ b/src/part08.rs @@ -152,4 +152,4 @@ mod tests { // **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) diff --git a/src/part09.rs b/src/part09.rs index ecb9ffa..5916258 100644 --- a/src/part09.rs +++ b/src/part09.rs @@ -146,4 +146,4 @@ impl<'a> IntoIterator for &'a BigInt { //@ 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) diff --git a/src/part10.rs b/src/part10.rs index b586cd5..6fa8f1e 100644 --- a/src/part10.rs +++ b/src/part10.rs @@ -141,4 +141,4 @@ fn filter_vec_by_divisor(v: &Vec, divisor: i32) -> Vec { // 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) diff --git a/src/part11.rs b/src/part11.rs index a5cafa1..9d931db 100644 --- a/src/part11.rs +++ b/src/part11.rs @@ -117,4 +117,4 @@ pub fn main() { // 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) diff --git a/src/part12.rs b/src/part12.rs index 7d03ca6..c2331ea 100644 --- a/src/part12.rs +++ b/src/part12.rs @@ -166,4 +166,4 @@ fn demo_mut(c: &mut CallbacksMut) { // **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) diff --git a/src/part13.rs b/src/part13.rs index 4fbc908..8edcc99 100644 --- a/src/part13.rs +++ b/src/part13.rs @@ -189,4 +189,4 @@ pub fn main() { //@ 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) diff --git a/src/part14.rs b/src/part14.rs index e16837c..5c00905 100644 --- a/src/part14.rs +++ b/src/part14.rs @@ -159,4 +159,4 @@ Options: // 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) diff --git a/src/part15.rs b/src/part15.rs index 6c99f4d..b1cab06 100644 --- a/src/part15.rs +++ b/src/part15.rs @@ -145,4 +145,4 @@ pub fn main() { //@ [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) diff --git a/src/part16.rs b/src/part16.rs index efa48a5..f0707aa 100644 --- a/src/part16.rs +++ b/src/part16.rs @@ -197,4 +197,4 @@ impl Drop for LinkedList { //@ 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 diff --git a/workspace/.gitignore b/workspace/.gitignore index 38ceaf9..7e94cdf 100644 --- a/workspace/.gitignore +++ b/workspace/.gitignore @@ -1,2 +1,2 @@ target/ -.* +src/part*.rs diff --git a/workspace/README.md b/workspace/README.md deleted file mode 100644 index ff4e1ee..0000000 --- a/workspace/README.md +++ /dev/null @@ -1,9 +0,0 @@ -## 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. diff --git a/workspace/src/main.rs b/workspace/src/main.rs index 77632d9..d804cfb 100644 --- a/workspace/src/main.rs +++ b/workspace/src/main.rs @@ -1,5 +1,10 @@ #![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; diff --git a/workspace/src/part00.rs b/workspace/src/part00.rs deleted file mode 100644 index 9144eae..0000000 --- a/workspace/src/part00.rs +++ /dev/null @@ -1,76 +0,0 @@ -// 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) -> 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 { - 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. - - diff --git a/workspace/src/part01.rs b/workspace/src/part01.rs deleted file mode 100644 index be2c2d2..0000000 --- a/workspace/src/part01.rs +++ /dev/null @@ -1,74 +0,0 @@ -// 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) -> 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: "), - Number(n) => println!("The number is: {}", n), - }; - } -} - -// With our refactored functions and methods, `main` now looks as follows: -fn read_vec() -> Vec { - 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`. - -// **Exercise 01.2**: Write a function `vec_print` that takes a vector and prints all its elements. - diff --git a/workspace/src/part02.rs b/workspace/src/part02.rs deleted file mode 100644 index 357c5a0..0000000 --- a/workspace/src/part02.rs +++ /dev/null @@ -1,83 +0,0 @@ -// Rust-101, Part 02: Generic types, Traits -// ======================================== - - -// ## Generic datatypes - -pub enum SomethingOrNothing { - 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; - -// ## Generic `impl`, Static functions -// Inside an `impl`, `Self` refers to the type we are implementing things for. Here, it is -// an alias for `SomethingOrNothing`. -impl SomethingOrNothing { - fn new(o: Option) -> Self { - unimplemented!() - } - - fn to_option(self) -> Option { - unimplemented!() - } -} -// You can call static functions, and in particular constructors, as demonstrated in `call_constructor`. -fn call_constructor(x: i32) -> SomethingOrNothing { - SomethingOrNothing::new(Some(x)) -} - -// ## Traits - -pub trait Minimum : Copy { - fn min(self, b: Self) -> Self; -} - -pub fn vec_min(v: Vec) -> SomethingOrNothing { - 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`, 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: "), - 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 { - 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` (where `f32` is the type -// of 32-bit floating-point numbers). You should not change `vec_min` in any way, obviously! - diff --git a/workspace/src/part03.rs b/workspace/src/part03.rs deleted file mode 100644 index 3840156..0000000 --- a/workspace/src/part03.rs +++ /dev/null @@ -1,71 +0,0 @@ -// 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 { - let mut vec: Vec = Vec::::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`. - - // 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::() { - 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`". -// -// 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 SomethingOrNothing { - 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. - diff --git a/workspace/src/part04.rs b/workspace/src/part04.rs deleted file mode 100644 index e0d522e..0000000 --- a/workspace/src/part04.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Rust-101, Part 04: Ownership, Borrowing, References -// =================================================== - -/* - void foo(std::vector v) { - int *first = &v[0]; - v.push_back(42); - *first = 1337; // This is bad! - } -*/ - -// ## Ownership -fn work_on_vector(v: Vec) { /* 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) -> Option { - 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) { - 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. - diff --git a/workspace/src/part05.rs b/workspace/src/part05.rs deleted file mode 100644 index 28c4030..0000000 --- a/workspace/src/part05.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Rust-101, Part 05: Clone -// ======================== - -// ## Big Numbers - -pub struct BigInt { - pub data: Vec, // 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) -> 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` implement `Clone`. -use part02::{SomethingOrNothing,Something,Nothing}; -impl Clone for SomethingOrNothing { - 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; -} - diff --git a/workspace/src/part06.rs b/workspace/src/part06.rs deleted file mode 100644 index 3bc7d08..0000000 --- a/workspace/src/part06.rs +++ /dev/null @@ -1,66 +0,0 @@ -// 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) -> Option { - let mut min: Option = 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 Copy for SomethingOrNothing {} - - -// ## Lifetimes - -fn head(v: &Vec) -> 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 v) { - int *first = head(v); - v.push_back(42); - return *first; - } -*/ -fn rust_foo(mut v: Vec) -> i32 { - let first: Option<&i32> = head(&v); - /* v.push(42); */ - *first.unwrap() -} - - diff --git a/workspace/src/part07.rs b/workspace/src/part07.rs deleted file mode 100644 index 916cb01..0000000 --- a/workspace/src/part07.rs +++ /dev/null @@ -1,93 +0,0 @@ -// 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(v: &Vec) -> 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` and `SomethingOrNothing`. - diff --git a/workspace/src/part08.rs b/workspace/src/part08.rs deleted file mode 100644 index 5ddcb33..0000000 --- a/workspace/src/part08.rs +++ /dev/null @@ -1,93 +0,0 @@ -// 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.
- // **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 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 = 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`. - diff --git a/workspace/src/part09.rs b/workspace/src/part09.rs deleted file mode 100644 index 61cc70b..0000000 --- a/workspace/src/part09.rs +++ /dev/null @@ -1,85 +0,0 @@ -// 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 { - // 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!
- diff --git a/workspace/src/part10.rs b/workspace/src/part10.rs deleted file mode 100644 index 8fc650f..0000000 --- a/workspace/src/part10.rs +++ /dev/null @@ -1,99 +0,0 @@ -// 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(&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(&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, 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(v: &Vec) { - 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, divisor: i32) -> Vec { - 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! - diff --git a/workspace/src/part11.rs b/workspace/src/part11.rs deleted file mode 100644 index a93ad07..0000000 --- a/workspace/src/part11.rs +++ /dev/null @@ -1,65 +0,0 @@ -// 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 { - callbacks: Vec, -} - -/* struct CallbacksV2 { - callbacks: Vec, -} */ - -pub struct Callbacks { - callbacks: Vec>, -} - -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) { - 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(&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. - diff --git a/workspace/src/part12.rs b/workspace/src/part12.rs deleted file mode 100644 index 1593e8a..0000000 --- a/workspace/src/part12.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Rust-101, Part 12: Rc, Interior Mutability, Cell, RefCell -// ========================================================= - -use std::rc::Rc; -use std::cell::{Cell, RefCell}; - - - -#[derive(Clone)] -struct Callbacks { - callbacks: Vec>, -} - -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(&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>>, -} - -impl CallbacksMut { - pub fn new() -> Self { - CallbacksMut { callbacks: Vec::new() } - } - - pub fn register(&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. - diff --git a/workspace/src/part13.rs b/workspace/src/part13.rs deleted file mode 100644 index ae12cd1..0000000 --- a/workspace/src/part13.rs +++ /dev/null @@ -1,128 +0,0 @@ -// 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.
-#[derive(Clone,Copy)] -pub enum OutputMode { - Print, - SortAndPrint, - Count, -} -use self::OutputMode::*; - -pub struct Options { - pub files: Vec, - 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, out_channel: SyncSender) { - 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, - in_channel: Receiver, - out_channel: SyncSender) { - // 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, in_channel: Receiver) { - 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 = 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. - - - diff --git a/workspace/src/part14.rs b/workspace/src/part14.rs deleted file mode 100644 index 5906acf..0000000 --- a/workspace/src/part14.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Rust-101, Part 14: Slices, Arrays, External Dependencies -// ======================================================== - - -// ## Slices - -pub fn sort(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) { - 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] ... - -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/).
- 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(""); - let files = args.get_vec(""); - 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 -- ` 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.) - diff --git a/workspace/src/part15.rs b/workspace/src/part15.rs deleted file mode 100644 index 148f648..0000000 --- a/workspace/src/part15.rs +++ /dev/null @@ -1,75 +0,0 @@ -// 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>); - -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. - - diff --git a/workspace/src/part16.rs b/workspace/src/part16.rs deleted file mode 100644 index fa5b184..0000000 --- a/workspace/src/part16.rs +++ /dev/null @@ -1,115 +0,0 @@ -// 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 { - next: NodePtr, - prev: NodePtr, - data: T, -} -// A node pointer is a *mutable raw pointer* to a node. -type NodePtr = *mut Node; - -// 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 { - first: NodePtr, - last: NodePtr, - _marker: PhantomData, -} - - -unsafe fn raw_into_box(r: *mut T) -> Box { - mem::transmute(r) -} -fn box_into_raw(b: Box) -> *mut T { - unsafe { mem::transmute(b) } -} - -impl LinkedList { - // A new linked list just contains null pointers. `PhantomData` is how we construct any `PhantomData`. - 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`. - - // Next, we are going to provide an iterator. - pub fn iter_mut(&mut self) -> IterMut { - IterMut { next: self.first, _marker: PhantomData } - } -} - - -pub struct IterMut<'a, T> where T: 'a { - next: NodePtr, - _marker: PhantomData<&'a mut LinkedList>, -} - -impl<'a, T> Iterator for IterMut<'a, T> { - type Item = &'a mut T; - - fn next(&mut self) -> Option { - // 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 Drop for LinkedList { - // 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 -