@echo "$< -> $@"
@# sed-fu: remove lines starting with "//@", and replace those ending in "/*@*/" by "unimplemented!()".
@# Also coalesce multiple adjacent such lines to one.
- @sed '/^\s*\/\/@/d;s|\(\s*\)\S.*/\*@\*/|\1unimplemented!()|' $< | sed -f dup-unimpl.sed > $@
+ @sed '/^\s*\/\/@/d;s|\(\s*\)\S.*/\*@@?\*/|\1unimplemented!()|' $< | sed -f dup-unimpl.sed > $@
workspace/src/main.rs:
# Don't touch this file
* Closures: Working on iterators
+
* Arrays/slices
* Trait objects
-* Drop
-* ?unsafe?
-* ?interior mutability?
* Arc, concurrency, channels: Some grep-like thing, "rgrep"
* Send, Sync
* External dependencies: regexp crate, add to rgrep
-*
\ No newline at end of file
+
+* Shared-memoty concurrency, interior mutability: Concurrent counter
+* Drop, unsafe: doubly-linked list
use std::cmp;
let mut min = None;
- for e in v {
+ // This time, we explicitly request an iterator for the vector `v`. The method `iter` borrows the vector
+ // it works on, and provides shared borrows of 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,
//@ As an example, consider a function which increments every element of a vector by 1.
//@ The type `&mut Vec<i32>` is the type of mutable borrows of `vec<i32>`. Because the borrow is
-//@ mutable, we can change `e` in the loop.
+//@ mutable, we can use a mutable iterator, providing a mutable borrow of the elements.
fn vec_inc(v: &mut Vec<i32>) {
- for e in v {
+ for e in v.iter_mut() {
*e += 1;
}
}
}
// Now we can write `vec_min`.
-//@ However, in order to make it type-check, we have to make a full (deep) copy of e by calling `clone`.
fn vec_min(v: &Vec<BigInt>) -> Option<BigInt> {
let mut min: Option<BigInt> = None;
+ // If `v` is a shared borrowed 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(); /*@*/
min = Some(match min { /*@*/
}
min
}
-//@ Now, what's happening here? Why do we have to clone `e`, and why did we not
+//@ Now, what's happening here? Why do we have to to make a full (deep) copy of `e`, and why did we not
//@ have to do that in our previous version?
//@
//@ The answer is already hidden in the type of `vec_min`: `v` is just borrowed, but
// **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!
+// make any copies of `BigInt`!
impl Minimum for BigInt {
fn min<'a>(&'a self, other: &'a Self) -> &'a Self {
unimplemented!()
if sum >= a {
// The addition did not overflow. <br/>
// **Exercise 08.1**: Write the code to handle adding the carry in this case.
- let sum_total = u64::wrapping_add(sum, if carry { 1 } else { 0 }); /*@@*/
- let had_overflow = sum_total < sum; /*@@*/
- (sum_total, had_overflow) /*@@*/
+ 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.
carry = new_carry; /*@*/
}
// **Exercise 08.2**: Handle the final `carry`, and return the sum.
- if carry { /*@@*/
- result_vec.push(1); /*@@*/
- } /*@@*/
- BigInt { data: result_vec } /*@@*/
+ unimplemented!()
}
}
//@ Rust would not bother compiling them when you just build your program for normal use. Other than that, tests work as usually.
#[cfg(test)]
mod tests {
- #[test]
+ use part05::BigInt;
+
+ /*#[test]*/
fn test_add() {
let b1 = BigInt::new(1 << 32);
let b2 = BigInt::from_vec(vec![0, 1]);
//@ which Rust successfully prevents.
// ## Iterator conversion trait
-//@ If you closely compare the `for` loop in `main` above, with the one in `vec_min`, you will notice that we were able to write
+//@ If you closely compare the `for` loop in `main` above, with the one in `part06::vec_min`, you will notice that we were able to write
//@ `for e in v` earlier, but now we have to call `iter`. Why is that? Well, the `for` sugar is not actually tied to `Iterator`.
-//@ Instead, if demands an implementation of [`IntoIterator`](http://doc.rust-lang.org/beta/std/iter/trait.IntoIterator.html).
+//@ Instead, it demands an implementation of [`IntoIterator`](http://doc.rust-lang.org/beta/std/iter/trait.IntoIterator.html).
//@ That's a trait of types that provide a *conversion* function into some kind of iterator. These conversion traits are a frequent
//@ pattern in Rust: Rather than demanding that something is an iterator, or a string, or whatever; one demands that something
//@ can be converted to an iterator/string/whatever. This provides convenience similar to overloading of functions: The function
//@ of the right type, the conversion function will not do anything and trivially be optimized away.
//@ If you have a look at the documentation of `IntoIterator`, you will notice that the function `into_iter` it provides actually
-//@ consumes its argument. So, we'd like to make `Self` a borrowed type, such that the number is not lost after the iteration.
+//@ consumes its argument. So we implement the trait for *borrowed* numbers, such that the number is not lost after the iteration.
impl<'a> IntoIterator for &'a BigInt {
type Item = u64;
type IntoIter = Iter<'a>;
}
}
// With this in place, you can now replace `b.iter()` in `main` by `&b`. Go ahead and try it! <br/>
-//@ Wait, `&b`? Why that? Well, we implemented `IntoIterator` for `&BigInt`, so we have to borrow `b`. If we wanted to be able to write
-//@ just `b`, we'd have to also implement `IntoIterator` for `BigInt` - which, as already mentioned, would mean that `b` is actually consumed
-//@ by the iteration, and gone. This can easily happen, for example, with a `Vec`: Both `Vec` and `&Vec` implement `IntoIterator`, so if
-//@ you do `for e in v`, and `v` has type `Vec`, then you will obtain ownership of the elements during the iteration - and destroy the vector
-//@ in the process. We actually did that in `vec_min`, but we did not care. You can write `for e in &v` or `for e in v.iter()` to avoid this.
+//@ Wait, `&b`? Why that? Well, we implemented `IntoIterator` for `&BigInt`. If we are in a place where `b` is already borrowed, we can
+//@ just do `for digit in b`. If however, we own `b`, we have to borrow it. Alternatively, we could implement `IntoIterator`
+//@ for `BigInt` - which, as already mentioned, would mean that `b` is actually consumed by the iteration, and gone. This can easily happen,
+//@ for example, with a `Vec`: Both `Vec` and `&Vec` (and `&mut Vec`) implement `IntoIterator`, so if you do `for e in v`, and `v` has type `Vec`,
+//@ 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) | [next](main.html)
use std::cmp;
let mut min = None;
- for e in v {
+ // This time, we explicitly request an iterator for the vector `v`. The method `iter` borrows the vector
+ // it works on, and provides shared borrows of 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,
// ## Mutable borrowing
fn vec_inc(v: &mut Vec<i32>) {
- for e in v {
+ for e in v.iter_mut() {
*e += 1;
}
}
// 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 borrowed vector, then the default for iterating over it is to call `iter`, the iterator that borrows the elements.
for e in v {
unimplemented!()
}
// **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!
+// make any copies of `BigInt`!
impl Minimum for BigInt {
fn min<'a>(&'a self, other: &'a Self) -> &'a Self {
unimplemented!()
if sum >= a {
// The addition did not overflow. <br/>
// **Exercise 08.1**: Write the code to handle adding the carry in this case.
- let sum_total = u64::wrapping_add(sum, if carry { 1 } else { 0 }); /*@@*/
- let had_overflow = sum_total < sum; /*@@*/
- (sum_total, had_overflow) /*@@*/
+ 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!()
}
// **Exercise 08.2**: Handle the final `carry`, and return the sum.
- if carry { /*@@*/
- result_vec.push(1); /*@@*/
- } /*@@*/
- BigInt { data: result_vec } /*@@*/
+ unimplemented!()
}
}
// 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 {
- #[test]
+ use part05::BigInt;
+
+ /*#[test]*/
fn test_add() {
let b1 = BigInt::new(1 << 32);
let b2 = BigInt::from_vec(vec![0, 1]);