}
}
- /// Construct a BigInt from a vector of 64-bit "digits", with the last significant digit being first
+ /// Construct a BigInt from a vector of 64-bit "digits", with the last significant digit being first. Solution to 05.1.
pub fn from_vec(mut v: Vec<u64>) -> Self {
- // remove trailing zeroes
+ // remove trailing zeros
while v.len() > 0 && v[v.len()-1] == 0 {
v.pop();
}
BigInt { data: v }
}
- /// Increments the number by 1. Solution to 05.1.
+ /// Increments the number by 1.
pub fn inc1(&mut self) {
let mut idx = 0;
// This loop adds "(1 << idx)". If there is no more carry, we leave.
}
impl Minimum for BigInt {
+ // This is essentially the solution to 06.1.
fn min<'a>(&'a self, other: &'a Self) -> &'a Self {
debug_assert!(self.test_invariant() && other.test_invariant());
if self.data.len() < other.data.len() {
// of the function), but that's a bit too much magic for my taste. We are being more explicit here:
// `parse::<i32>` is `parse` with its generic type set to `i32`.
match line.parse::<i32>() {
- // `parse` returns again a `Result`, and this time we use a `match` to handle errors (like, the user entering
- // something that is not a number).
- // This is a common pattern in Rust: Operations that could go wrong will return `Option` or `Result`.
- // The only way to get to the value we are interested in is through pattern matching (and through helper functions
- // like `unwrap()`). If we call a function that returns a `Result`, and throw the return value away,
- // the compiler will emit a warning. It is hence impossible for us to *forget* handling an error,
- // or to accidentally use a value that doesn't make any sense because there was an error producing it.
+ // `parse` returns again a `Result`, and this time we use a `match` to handle errors (like, the user entering
+ // something that is not a number).
+ // This is a common pattern in Rust: Operations that could go wrong will return `Option` or `Result`.
+ // The only way to get to the value we are interested in is through pattern matching (and through helper functions
+ // like `unwrap()`). If we call a function that returns a `Result`, and throw the return value away,
+ // the compiler will emit a warning. It is hence impossible for us to *forget* handling an error,
+ // or to accidentally use a value that doesn't make any sense because there was an error producing it.
Ok(num) => vec.push(num),
+ // We don't care about the particular error, so we ignore it with a `_`.
Err(_) => println!("What did I say about numbers?"),
}
}
// they do. However, the `v` in `mutable_borrow_demo` is not actually usable, it is not *active*: As long as there is an
// outstanding borrow, Rust will not allow you to do anything with `v`.
-// So, to summarize - the ownership and borrowing system of Rust enforces the following three rules:
+// ## 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 borrow, then nobody else can have active access to the data
// the memory of the machine being the only limit.
//
// We start by deciding how to represent such big numbers. One possibility here is
-// to use a vector "digits" of the big number. This is like "1337" being a vector of four digits (1, 3, 3, 7),
-// except that we will use `u64` as type of our digits. Now we just have to decide
-// the order in which we store numbers. I decided that we will store the least significant
+// to use a vector "digits" of the number. This is like "1337" being a vector of four digits (1, 3, 3, 7),
+// except that we will use `u64` as type of our digits, meaning we have 2^64 individual digits. Now we just
+// have to decide the order in which we store numbers. I decided that we will store the least significant
// digit first. This means that "1337" would actually become (7, 3, 3, 1).<br/>
// Finally, we declare that there must not be any trailing zeros (corresponding to
// useless leading zeros in our usual way of writing numbers). This is to ensure that
// 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 ...`, it says that we will locally
- // change the vector `v`. In this case, we need to make that annotation to be able to call `pop`
- // on `v`.
+ // change the vector `v`.
+ //
+ // **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 {
- while v.len() > 0 && v[v.len()-1] == 0 {
- v.pop();
- }
- BigInt { data: v }
+ unimplemented!()
}
}
-// **Exercise 05.1**: Write a function on `BigInt` that returns the number of digits. Write another one
-// that increments the number by 1.
-//
-// *Hint*: To take `self` as a mutable borrow, write `fn inc1(&mut self)`.
-
// ## Cloning
// If you have a close look at the type of `BigInt::from_vec`, you will notice that it
-// consumes the vector `v`. The caller hence loses access. There is however something
+// consumes the vector `v`. The caller hence loses access to its vector. There is however something
// we can do if we don't want that to happen: We can explicitly `clone` the vector,
// which means that a full (or *deep*) copy will be performed. Technically,
// `clone` takes a borrowed vector, and returns a fully owned one.
Text(String),
}
// Now consider the following piece of code. Like above, `n` will be a borrow of a part of `var`,
-// and since we wrote `ref mut`, they will be mutable borrows. In other words, right after the match, `ptr`
+// and since we wrote `ref mut`, the borrow will be mutable. In other words, right after the match, `ptr`
// points to the number that's stored in `var`, where `var` is a `Number`. Remember that `_` means
// "we don't care".
fn work_on_variant(mut var: Variant, text: String) {
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`. We start by writing `min` for
-// `BigInt`. Now our assumption of having no trailing zeros comes in handy!
+// 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 {
// Just to be sure, we first check that both operands actually satisfy our invariant. `debug_assert!` is a
// macro that checks that its argument (must be of type `bool`) is `true`, and panics otherwise. It gets
// removed in release builds, which you do with `cargo build --release`.
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
}
}
-// Now we can write `vec_min`. In order to make it type-check, we have to write it as follows.
+// Now we can write `vec_min`. In order to make it type-check, we have make a deep copy of e.
fn vec_min(v: &Vec<BigInt>) -> Option<BigInt> {
let mut min: Option<BigInt> = None;
for e in v {
+ let e = e.clone();
min = Some(match min {
- None => e.clone(),
- Some(n) => e.clone().min_try1(n)
+ None => e,
+ Some(n) => e.min_try1(n)
});
}
min
}
-// Now, what's happening here? Why do we have to write `clone()`, and why did we not
-// have to write that in our previous version?
+// Now, what's happening here? Why do we have to clone `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
// the Option<BigInt> that it returns is *owned*. We can't just return one of the elements of `v`,
// as that would mean that it is no longer in the vector! In our code, this comes up when we update
-// the intermediate variable `min`, which also has type `Option<BigInt>`. If you replace `e.clone()`
-// in the `None` arm with `*e`, Rust will complain "Cannot move out of borrowed content". That's because
+// the intermediate variable `min`, which also has type `Option<BigInt>`. If you replace get rid of the
+// `e.clone()`, Rust will complain "Cannot move out of borrowed content". That's because
// `e` is a `&BigInt`. Assigning `min = Some(*e)` works just like a function call: Ownership of the
// underlying data is transferred from where `e` borrows from to `min`. But that's not allowed, since
// we just borrowed `e`, so we cannot empty it! We can, however, call `clone()` on it. Then we own
// But before we go there, I should answer the second question I brought up above: Why did our old `vec_min` work?
// We stored the minimal `i32` locally without cloning, and Rust did not complain. That's because there isn't
// really much of an "ownership" when it comes to types like `i32` or `bool`: If you move the value from one
-// place to another, then both instance are "complete". We also say the value has been *duplicated*. This is in
+// place to another, then both instances are "complete". We also say the value has been *duplicated*. This is in
// stark contrast to types like `Vec<i32>`, where moving the value results in both the old and the new vector to
-// point to the same underlying buffer. We don't have two vectors, there's no duplication.
+// point to the same underlying buffer. We don't have two vectors, there's no proper duplication.
//
-// Rust calls types that can be freely duplicated `Copy` types. `Copy` is another trait, and it is implemented for
+// Rust calls types that can be easily duplicated `Copy` types. `Copy` is another trait, and it is implemented for
// types like `i32` and `bool`. Remember how we defined the trait `Minimum` by writing `trait Minimum : Copy { ...`?
// This tells Rust that every type that implements `Minimum` must also implement `Copy`, and that's why the compiler
// accepted our generic `vec_min` in part 02. `Copy` is the first *marker trait* that we encounter: It does not provide
// are `Copy`, and that's not the case for `BigInt`. However, we can make
// `SomethingOrNothing<T>` copy if `T` is `Copy`.
use part02::{SomethingOrNothing,Something,Nothing};
-impl<T: Copy> Copy for SomethingOrNothing<T>{}
+impl<T: Copy> Copy for SomethingOrNothing<T> {}
// Again, Rust can generate implementations of `Copy` automatically. If
// you add `#[derive(Copy,Clone)]` right before the definition of `SomethingOrNothing`,
// both `Copy` and `Clone` will automatically be implemented.
// ## An operational perspective
// Instead of looking at what happens "at the surface" (i.e., visible in Rust), one can also explain
-// ownership passing and how `Copy` and `Clone` fit by looking at what happens on the machine.<br/>
+// ownership passing and how `Copy` and `Clone` fit in by looking at what happens on the machine.<br/>
// When Rust code is executed, passing a value (like `i32` or `Vec<i32>`) to a function will always
// result in a shallow copy being performed: Rust just copies the bytes representing that value, and
// considers itself done. That's just like the default copy constructor in C++. Rust, however, will
// consider this a destructive operation: After copying the bytes elsewhere, the original value must
-// no longer be used. After all, the two could not share a pointer! If, however, you mark a type `Copy`,
+// no longer be used. After all, the two could now share a pointer! If, however, you mark a type `Copy`,
// then Rust will *not* consider a move destructive, and just like in C++, the old and new value
-// can happily coexist. Now, Rust does not allow to to overload the copy constructor. This means that
+// can happily coexist. Now, Rust does not allow you to overload the copy constructor. This means that
// passing a value around will always be a fast operation, no allocation or any other kind of heap access
// will happen. In the situations where you would write a copy constructor in C++ (and hence
// incur a hidden cost on every copy of this type), you'd have the type *not* implement `Copy`, but only
// To fix the performance problems of `vec_min`, we need to avoid using `clone()`. We'd like
// the return value to not be owned (remember that this was the source of our need for cloning), but *borrowed*.
-// This is demonstrated by the function `head` that borrows the first element of a vector if it is non-empty.
+// The function `head` demonstrates how that could work: It borrows the first element of a vector if it is non-empty.
// The type of the function says that it will either return nothing, or it will return a borrowed `T`.
// We can then borrow the first element of `v` and use it to construct the return value.
fn head<T>(v: &Vec<T>) -> Option<&T> {
None
}
}
-
-// Now, coming back to `head` - here, 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.
+// 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);
return *first;
}
*/
-// This is very much like our very first motivating example for ownership, at the beginning of part 04.
-// But this time, the bug is hidden behind the call to `head`. How does Rust solve this? If we translate
+// This is very much like our very first motivating example for ownership, at the beginning of part 04:
+// `push_back` could reallocate the buffer, making `first` an invalid pointer. Again, we have aliasing (of `first`
+// and `v`) and mutation. But this time, the bug is hidden behind the call to `head`. How does Rust solve this? If we translate
// the code above to Rust, it doesn't compile, so clearly we are good - but how and why?
// (Notice that have to explicitly assert using `unwrap` that `first` is not `None`, whereas the C++ code
// above would silently dereference a `NULL`-pointer. But that's another point.)
// To give the answer to this question, we have to talk about the *lifetime* of a borrow. The point is, saying that
// you borrowed your friend a `Vec<i32>`, or a book, is not good enough, unless you also agree on *how long*
-// your friend can borrow. After all, you need to know when you can rely on owning your data (or book) again.
+// your friend can borrow it. After all, you need to know when you can rely on owning your data (or book) again.
//
-// Every borrow in Rust has an associated lifetime. The full type of `head` reads as follows:
-// `fn<'a, T>(&'a Vec<T>) -> Option<&'a T>`. Here, `'a` is a *lifetime variable*, which represents how long the vector has
-// been borrowed. The function type expresses that argument and return value have *the same lifetime*.
+// Every borrow in Rust has an associated lifetime, written `&'a T` for a borrow of type `T` with lifetime `'a`. The full
+// type of `head` reads as follows: `fn<'a, T>(&'a Vec<T>) -> Option<&'a T>`. Here, `'a` is a *lifetime variable*, which
+// represents how long the vector has been borrowed. The function type expresses that argument and return value have *the same lifetime*.
//
// When analyzing the code of `rust_foo`, Rust has to assign a lifetime to `first`. It will choose the scope
// where `first` is valid, which is the entire rest of the function. Because `head` ties the lifetime of its
pub use part05::BigInt;
-// With our new knowledge on Lifetimes, we are now able to write down the desired type
+// With our new knowledge of lifetimes, we are now able to write down the desired type
// of `min`: We want the function to take two borrows *of the same lifetime*, and then
// return a borrow of that lifetime. If the two input lifetimes would be different, we
// would not know which lifetime to use for the result.
// a pointer in C(++), if you look at what happens during execution - but it's much safer to use.
// For our `vec_min` to be usable with `BigInt`, we need to provide an implementation of
-// `minimum`. You should be able to pretty much copy the code you wrote for exercise 06.1.
+// `Minimum`. You should be able to pretty much copy the code you wrote for exercise 06.1.
impl Minimum for BigInt {
fn min<'a>(&'a self, other: &'a Self) -> &'a Self {
unimplemented!()
// ## Operator Overloading
// How can we know that our `min` function actually does what we want it to do? One possibility
// here is to do *testing*. Rust comes with nice build-in support for both unit tests and integration
-// tests. However, before we go there, we need to have a way of checking whether the results are
+// tests. However, before we go there, we need to have a way of checking whether the results of function calls are
// correct. In other words, we need to define how to test equality of `BigInt`. Being able to
-// test equality is a property of a type, that - you guessed it - Rust expresses as a trait:
-// `PartialEq`. Once a type implements that trait, one can use the `==` operator on it.
+// test equality is a property of a type, that - you guessed it - Rust expresses as a trait: `PartialEq`.
// Doing this for `BigInt` is fairly easy, thanks to our requirement that there be no trailing zeros.
// The `inline` attribute tells Rust that we will typically want this function to be inlined.
// Since implementing `PartialEq` is a fairly mechanical business, you can let Rust automate this
// by adding the attribute `derive(PartialEq)` to the type definition. In case you wonder about
// the "partial", I suggest you check out the documentation of [`PartialEq`](http://doc.rust-lang.org/std/cmp/trait.PartialEq.html)
-// and [`Eq`](http://doc.rust-lang.org/std/cmp/trait.Eq.html). Again, `Eq` can be automatically derived.
+// and [`Eq`](http://doc.rust-lang.org/std/cmp/trait.Eq.html). `Eq` can be automatically derived as well.
-// Now we can compare `BigInt`s! Speaking in C++ terms, we just overloaded the `==` operator
+// Now we can compare `BigInt`s using `==`! Speaking in C++ terms, we just overloaded the `==` operator
// for `BigInt`. Rust does not have function overloading (i.e., it will not dispatch to different
// functions depending on the type of the argument). Instead, one typically finds (or defines) a
// trait that catches the core characteristic common to all the overloads, and writes a single
// function that's generic in the trait. For example, instead of overloading a function for all
-// the ways a string can be represented, one write a generic functions over [ToString](http://doc.rust-lang.org/std/string/trait.ToString.html).
+// the ways a string can be represented, one writes a generic functions over [ToString](http://doc.rust-lang.org/std/string/trait.ToString.html).
// Usually, there is a trait like this that fits the purpose - and if there is, this has the great
// advantage that any type *you* write, that can convert to a string, just has to implement
// that trait to be immediately usable with all the functions out there that generalize over `ToString`.
}
// ## Testing
-// With our equality test written, we are now ready to write out first testcase. It doesn't get much
+// With our equality test written, we are now ready to write our first testcase. It doesn't get much
// simpler: You just write a function (with no arguments or return value), and give it the `test` attribute.
// `assert!` is like `debug_assert!`, but does not get compiled away in a release build.
#[test]
// that users can understand, while `Debug` is meant to show the internal state of data and targeted at
// the programmer. The latter is what we want for `assert_eq!`, so let's get started.
-// Al formating is handled by [`std::fmt`](http://doc.rust-lang.org/std/fmt/index.html). I won't explain
+// All formating is handled by [`std::fmt`](http://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;
}
// `Debug` implementations can be automatically generated using the `derive(Debug)` attribute.
-// Now we are ready to use `assert_eq!` to test `vec_min`. While we are at it, let's also follow the usual
-// Rust style of putting tests into a *submodule*, to avoid polluting the namespace. The attribute `cfg(test)`
-// at the submodule means that it will only be compiled when building the tests.
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[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()];
- assert_eq!(vec_min(&v1), Some(&b1));
- assert_eq!(vec_min(&v2), Some(&b2));
- }
+// 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()];
+ assert_eq!(vec_min(&v1), Some(&b1));
+ assert_eq!(vec_min(&v2), Some(&b2));
}
// **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) and the functions you wrote for exercise 05.1. Finally, break one of your
-// functions in a subtle way and watch the test fail.
+// 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.