From aa233307c895868d6040dd686971006cdda56528 Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Sat, 25 Feb 2017 12:09:01 -0500 Subject: [PATCH 01/30] Pretend I'm @gankro: unsafe Rust it ain't the nomicon, but it'll do --- second-edition/src/ch19-01-unsafe-rust.md | 412 +++++++++++++++++++++- 1 file changed, 401 insertions(+), 11 deletions(-) diff --git a/second-edition/src/ch19-01-unsafe-rust.md b/second-edition/src/ch19-01-unsafe-rust.md index 6b4af7bf1..b0b3db04a 100644 --- a/second-edition/src/ch19-01-unsafe-rust.md +++ b/second-edition/src/ch19-01-unsafe-rust.md @@ -1,32 +1,422 @@ # Unsafe Rust -Things you may do in an unsafe block that you may not in safe rust +So far, we've been talking about code written in Rust. That's what you'd expect +from a book on Rust! However, Rust has a second language hiding out inside of +it: unsafe Rust. Unsafe Rust works just like regular Rust does, but it gives +you extra superpowers not available in safe Rust code. -- deref a raw pointer -- call an unsafe fn -- access or modify a static variable -- impl an unsafe trait +You may be wondering why this is. While Rust's safety guarantees are a +wonderful thing, by nature, static analysis is conservative. That is, when +trying to determine if something is okay or not, it's better to reject some +programs that are valid than it is to accept some programs that are invalid. +There are some times when your code might be okay, but Rust thinks it's not! In +these cases, you can use unsafe code to tell the compiler, "trust me, I know +what I'm doing." The downside is that you're on your own; if you get it wrong, +bad things can happen. -Go see other stuff +There's another reason that Rust needs to have unsafe code: the underyling +hardware of computers is not safe. If Rust didn't let you do unsafe things, +then there would be some things that you simply could not do. But Rust needs to +be able to let you do things like directly interact with your operating system, +or even write your own operating system! That's part of the goals of the +language. So we need some way to do these kinds of things. -Here's the syntax tho +## Unsafe Superpowers -You know unsafe blocks are the cause of any crashes +More specifically, there are four things that you can do with unsafe Rust that +you cannot do in safe Rust. We call these the "unsafe superpowers." Here they +are: -wrap all the unsafe, make it as small as possible, present a safe public API +1. Dereference a raw pointer. +2. Call an unsafe function. +3. Access or modify a static variable. +4. Implement an unsafe trait. + +We haven't seen most of these features yet because, well, they're only usable +by unsafe! That is, it's important to understand that unsafe doesn't "turn off +the borrow checker" or disable any of Rust's safety checks: if you use a +reference in unsafe code, it will still be checked. What it does do is give you +access to these new, unchecked features. You still get some degree of safety +inside of an unsafe block! + +Rust's strategy here is to make sure everything is safe, but allow you to do +extra unsafe things when you specifically annotate your code to allow unsafe +things. What kind of annotations? It looks like this: + +```rust +// only safe stuff here! +let x = 5; + +unsafe { + // here be dragons! +} +``` + +You can only use these features inside of these blocks. This means that you do +make a mistake and something goes wrong, you'll know that it has to be related +to one of the places that you opted into this unsafety. That makes these bugs +much easier to find. Because of this, it's important to contain your unsafe +code to as small of an area as possible. Once you use unsafe inside of a +module, any of the code in that module is supect. Keep them small and you'll +thank yourself later. + +One final note about unsafe blocks: while unsafe blocks let you do almost +anything, there are still rules. That is, `unsafe` does not mean "now I will do +anything," `unsafe` means "I have manually checked that I am following the +rules." If you break the rules, bad things can still happen! + +Let's talk about each of these four superpowers in turn. ## Raw Pointers +Way back in chapter four, we learned about references: + +```rust +let r = &5; +``` + +We also learned that references are always valid, and that the compiler makes +sure that this is so. Unsafe Rust has two new types that are similar to +references called "raw pointers." + +```rust +let mut num = 5; + +let r1 = &5 as *const i32; +let r2 = &mut 5 as *mut i32; +``` + +The `*const T` and `*mut T` types are raw pointers, in contrast with references +and mutable references, respectively. Unlike references, these pointers may or +may not be valid. We can even create raw pointers to arbitrary locations in +memory: + +```rust +// don't try this at home: +let address = 0x012345; +let r = address as *const i32; + +// bad things will happen if you try to use r +``` + +But wait, we said that you need to use `unsafe` with raw pointers, but there's +no `unsafe` block in the above examples. What gives? While you can _create_ +raw pointers in safe code, you can't _dereference_ raw pointers in safe code. +To use `*`, you need `unsafe`: + +```rust +let mut num = 5; + +let r1 = &5 as *const i32; +let r2 = &mut 5 as *mut i32; + +unsafe { + println!("r1 is: {}", *r1); + println!("r2 is: {}", *r2); +} +``` + +This is because creating a pointer can't do any harm; it's only when accessing +the value that it points at that you might end up dealing with something that's +invalid. + +Furthermore, in these examples, you may have noticed something: we created both +a `*const i32` and a `*mut i32` to the same memory location. With references, +this would be impossible, due to the mutability rules. With raw pointers, you +can do this. Be careful! + +With all of these dangers, why would we ever use raw pointers? One major +use-case is interfacing with C code; we'll talk about this more in the next +section. Another case is to build up safe abstractions that the borrow checker +doesn't understand. Before we show an example, let's talk about unsafe +functions; you'll often be using them with raw pointers. + ## Unsafe Functions +The second thing that requires an unsafe block is a call to an unsafe function. +Unsafe functions look exactly like regular functions, but with an extra +`unsafe` out front: + +```rust +unsafe fn dangerous() {} + +unsafe { + dangerous(); +} +``` + +If you try to call `dangerous` without the `unsafe` block, you'll get an error: + +```text +error[E0133]: call to unsafe function requires unsafe function or block + --> :4:5 + | +4 | dangerous(); + | ^^^^^^^^^^^ call to unsafe function +``` + +By inserting the `unsafe` block, you're asserting to Rust that you've read the +documentation for this function, you understand how to use it properly, and +you've verified that everything is correct. + +Raw pointers and unsafe functions often interact, becuase unsafe functions +often take raw pointers as arguments. Given that raw pointers aren't checked, a +very common constraint on unsafe functions is "make sure the raw pointers +you're passing to it are valid." + +As an example, let's check out some functionality from the standard library, +`split_at_mut`. This method is defined on mutable slices, and it takes one +slice and makes it into two, like this: + +```rust +let mut v = vec![1, 2, 3, 4, 5, 6]; + +let r = &mut v[..]; + +let (a, b) = r.split_at_mut(3); + +assert_eq!(a, &mut [1, 2, 3]); +assert_eq!(b, &mut [4, 5, 6]); +``` + +This function couldn't be written in safe Rust. If we tried, it might look like +this: + +```rust,ignore +fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { + // get the total length of the slice + let len = slice.len(); + + // make sure that our midpoint is in bounds + assert!(mid <= len); + + // return two slices, from the start to mid, and from mid to the end + (&mut slice[..mid], + &mut slice[(len - mid)..]) +} +``` + +If you try to compile this, you'll get an error: + +```text +error[E0499]: cannot borrow `*slice` as mutable more than once at a time + --> :6:11 + | +5 | (&mut slice[..mid], + | ----- first mutable borrow occurs here +6 | &mut slice[(len - mid)..]) + | ^^^^^ second mutable borrow occurs here +7 | } + | - first borrow ends here +``` + +Rust's borrow checker can't understand that we're borrowing different parts of +the slice; it only knows that we're borrowing from the same slice twice. Doing +this is fundamentally okay; our two `&mut [i32]`s aren't overlapping. But Rust +isn't smart enough to know this. When you know something is okay, but Rust +doesn't, it's time to reach for unsafe code. + +Here's how to use `unsafe` to make this work: + + +```rust,ignore +use std::slice; + +// in the standard library, this is generic over any T, but we'll use i32 here. +fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { + unsafe { + let len = slice.len(); + let ptr = slice.as_mut_ptr(); + + assert!(mid <= len); + + (slice::from_raw_parts_mut(ptr, mid), + slice::from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) + } +} +``` + +Remember how slices are a pointer to some data, and then the length of the +slice? You can get these bits with the `len` and `as_mut_ptr` methods. +`as_mut_ptr` returns a raw pointer, an `*mut i32` in this case. Then, +the `slice::from_raw_pts_mut` method does the reverse: it takes a raw pointer +and a length, and then conjures up a slice. Because slices are checked, they're +safe, but since `from_raw_parts_mut` takes a raw pointer, it just trusts that +this pointer is valid. For example, this code would _not_ work: + +```rust +use std::slice; + +let address = 0x012345; +let r = address as *mut i32; + +let slice = unsafe { + // noooooooooooo + slice::from_raw_parts_mut(r, 10000) +}; +``` + +Now you have a ten thousand long slice to a random place in memory. This won't +work. Don't try this at home. + +But above, since we got our raw pointer from an existing slice, we know this is +safe! So it's fine. We also have a second `unsafe` function hidden in there: +`offset`. The `offset` method on raw pointers takes a number, and then +increments the pointer in memory. We use this function to create the second +slice. + +That's the general idea of unsafe functions, but let's talk about two other +specific cases. + ### `transmute` -never ever. don't. stop. +The `transmute` function is an unsafe function, but it should really be known +as the most unsafe function, so unsafe that you shouldn't ever use it. What +does it do? It says "hey, compiler, you know this type? Treat the data as this +other type. Don't think about it, just trust me." So for example, + +```rust +let ptr = &0; + +let other_ptr: usize = unsafe { std::mem::transmute(ptr) }; +``` + +Here, we say "hey Rust! You know how you have a reference? Convert it into a +`usize`. Since a `usize` has the same number of bits as a reference, this works +just fine. + +However, there's almost always a better alternative to transmute. For example, +in this case, we could use `as` to first cast our reference to a raw pointer, +and then use it again to cast as a `usize`: + +```rust +let ptr = &0; + +let other_ptr = ptr as *const i32 as usize; +``` + +This is much safer. + +For more details, see the documentation for `transmute` in the standard +library. + +Or don't, because you shouldn't use `transmute`. Unless you absolutely, +absolutely, absolutely must. ### `extern fn` -You have to write unsafe code to FFI +Sometimes, your Rust code may need to interact with code written in another +language. To do this, Rust has a keyword, `extern`, that facilitates this: + +```rust,ignore +// This function is defined somewhere externally: +extern "C" { + fn some_function(); +} + +// This function can be exposed externally: +pub extern "C" fn call_from_c() { + // code goes here +} + +fn main() { + unsafe { some_function() }; +} +``` + +As you can see, `extern` can be used in two ways: to refer to a function +defined somewhere else, and to expose a Rust function to be used externally. +The block form is used for the former case, and putting it before the `fn` is +used for the latter case. + +If you're calling an external function, you need to use `unsafe`. The reason is +this: if you're calling into some other language, that language is not Rust, +and so does not follow Rust's safety guarantees. Since Rust can't check that +it's safe, you must. + +You'll also notice the `"C"` there; this defines which ABI, or "application +binary interface", your external function is. The ABI defines how to call the +function at the assembly level. The `"C"` ABI is the most common, and follows +the C programming language's ABI. ## `static` +We've gone this entire book without talking about "global variables." Many +programming languages support them, and so does Rust. However, global variables +can be problematic: if you have two threads, for example, accessing the same +mutable global variable, bad things can happen. + +We call global variables "static" in Rust, and they look like this: + +```rust +static HELLO_WORLD: &'static str = "Hello, world!"; + +fn main() { + println!("name is: {}", HELLO_WORLD); +} +``` + +You'll notice two things about `static`s: their names are in +`SCREAMING_SNAKE_CASE` by convention, and you _must_ declare the type, which is +`&'static str` in this case. Any references stored in a static will have the +`'static` lifetime. + +You can also have mutable statics, but those require `unsafe`: + +```rust +static mut COUNTER: u32 = 0; + +fn main() { + // mutation is unsafe... + unsafe { + COUNTER = COUNTER + 1; + } + + // ... but so is access + unsafe { + println!("COUNTER: {}", COUNTER); + } +} +``` + +Global mutable state is tricky! + ## Unsafe Traits + +Finally, the last feature of `unsafe` is related to traits. We can declare a +trait as `unsafe`: + +```rust +unsafe trait Foo { + // methods go here +} +``` + +And then they require the `unsafe` keyword to implement: + +```rust +# unsafe trait Foo { +# // methods go here +# } + +unsafe impl Foo for i32 { + // methods go here +} +``` + +Like general unsafe functions, an unsafe trait says "hey, there is some sort of +invariant here that the compiler cannot verify. By using `unsafe impl`, you are +promising that you uphold these invariants." + +As an example, remember the `Sync` and `Send` traits from Chapter 16? These +marker traits have no methods, and there's no way for the compiler to verify +that, if you try to implement these traits, that they actually have the `Sync` +and `Send` properties. As such, they're `unsafe` traits, and so you need +`unsafe` to implement them. + +## Summary + +That's the gist of unsafe! If you want an even more thorough coverage of unsafe +code, check out the Nomicon. + +Let's move on. Time to talk more about lifetimes! From 0cc76ff9c64e30b5616c81d29a602bd6cd3d0e3f Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 27 Feb 2017 14:38:20 -0500 Subject: [PATCH 02/30] rest of chapter 19 --- .../src/ch19-02-advanced-lifetimes.md | 494 +++++++++++++++++- second-edition/src/ch19-03-advanced-traits.md | 356 ++++++++++++- 2 files changed, 836 insertions(+), 14 deletions(-) diff --git a/second-edition/src/ch19-02-advanced-lifetimes.md b/second-edition/src/ch19-02-advanced-lifetimes.md index 5e6f57c62..ffe083897 100644 --- a/second-edition/src/ch19-02-advanced-lifetimes.md +++ b/second-edition/src/ch19-02-advanced-lifetimes.md @@ -1,11 +1,495 @@ # Advanced Lifetimes -### Lifetimes that depend on other lifetimes +Back in Chapter 10, we learned how you can help Rust understand your references +with the 'lifetime' syntax. As a quick recap, most of the time, Rust will let +you elide lifetimes, but every reference has one. If you need to be explicit, +they look like this: -'a: 'b stuff: subtyping +```rust +fn explicit_lifetime<'a>(a: &'a i32, b: &'a i32) -> &'a i32 { +# a +# } +``` -### Higher ranked trait bounds +There are three more features of lifetimes that we haven't learned yet, though: +*lifetime subtyping*, *trait object lifetimes*, and *higher ranked trait +bounds*. -for<'a> +## Lifetime subtyping -Needed for closures +Imagine that we want to write a parser. To do this, we'll have a structure +with the string that we're parsing, a 'context'. We'll write individual parsers +that parse this string, and return success or failure. The parsers will need to +borrow the context to do the parsing. We'd end up with something like the +following. We've left off the lifetime anntations for now; this code won't +compile: + +```rust,ignore +struct Context(&str); + +struct Parser { + context: &Context, +} + +impl Parser { + fn parse(&self) -> Result<(), &str> { + // do the parsing + } +} +``` + +For simplicity's sake, our `parse` function returns a `Result<(), &str>`, that +is, we don't do anything on success, and the failure is the part of our string +that didn't parse correctly. A real implementation would have more error +information than that, and would actually do something on success, but we're +since this isn't relevant to our example, we're leaving that stuff off. + +Okay, so, how do we fill in the lifetimes? The most straightforward thing to do +is to use the same lifetime everywhere: + +```rust,ignore +struct Context<'a>(&'a str); + +struct Parser<'a> { + context: &'a Context<'a>, +} +``` + +As is, this compiles. Let's implement our `parse` method now. Let's say that +we always produce an error, and the error happened after the first character. +Like this: + +```rust +struct Context<'a>(&'a str); + +struct Parser<'a> { + context: &'a Context<'a>, +} + +impl<'a> Parser<'a> { + fn parse(&self) -> Result<(), &str> { + // a real implementation would do a lot more, of course... + Err(&self.context.0[1..]) + } +} +``` + +So far, so good. Next, let's write a function that takes a context, and then +uses a `Parser` to parse that context. This won't quite work... + +```rust,ignore +struct Context<'a>(&'a str); + +struct Parser<'a> { + context: &'a Context<'a>, +} + +impl<'a> Parser<'a> { + fn parse(&self) -> Result<(), &str> { + // a real implementation would do a lot more, of course... + Err(&self.context.0[1..]) + } +} + +fn parse_context(context: Context) -> Result<(), &str> { + Parser { context: &context }.parse() +} +``` + +We get quite the error message: + +```text +error: borrowed value does not live long enough + --> :16:5 + | +16 | Parser { context: &context }.parse() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough +17 | } + | - temporary value only lives until here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:55... + --> :15:56 + | +15 | fn parse_context(context: Context) -> Result<(), &str> { + | ________________________________________________________^ starting here... +16 | | Parser { context: &context }.parse() +17 | | } + | |_^ ...ending here + +error: `context` does not live long enough + --> :16:24 + | +16 | Parser { context: &context }.parse() + | ^^^^^^^ does not live long enough +17 | } + | - borrowed value only lives until here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:55... + --> :15:56 + | +15 | fn parse_context(context: Context) -> Result<(), &str> { + | ________________________________________________________^ starting here... +16 | | Parser { context: &context }.parse() +17 | | } + | |_^ ...ending here +``` + +Let's break this error down: + +```text +error: borrowed value does not live long enough + --> :16:5 + | +16 | Parser { context: &context }.parse() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough +17 | } + | - temporary value only lives until here + | +``` + +Fundamentally, the issue is that our `Parser` is temporary, and it needs to +live for longer than that. But why? We use it to calculate the result, but +there's no other reason for it to stick around. + +For that, we need to look at the next part of the message: + +```text +note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:55... + --> :15:56 + | +15 | fn parse_context(context: Context) -> Result<(), &str> { + | ________________________________________________________^ starting here... +16 | | Parser { context: &context }.parse() +17 | | } + | |_^ ...ending here +``` + +Ah! So, Rust expects that it needs to live for the entire function, but it +doesn't; it only lives for this one line. Why? Let's keep looking at the +message. + +```text +error: `context` does not live long enough + --> :16:24 + | +16 | Parser { context: &context }.parse() + | ^^^^^^^ does not live long enough +17 | } + | - borrowed value only lives until here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:55... + --> :15:56 + | +15 | fn parse_context(context: Context) -> Result<(), &str> { + | ________________________________________________________^ starting here... +16 | | Parser { context: &context }.parse() +17 | | } + | |_^ ...ending here +``` + +This is the same thing, but for `context` rather than for `Parser`. Rust +expects them to live longer... let's look at their definitions again: + +```rust +struct Context<'a>(&'a str); + +struct Parser<'a> { + context: &'a Context<'a>, +} +``` + +Ah, right. We said `&'a Context<'a>`, that is, the `Context` has a lifetime +that's the same as the reference to it. That's fine, but... + +```rust,ignore + fn parse(&self) -> Result<(), &str> { +``` + +Remember the elision rules? This is the same as + +```rust,ignore + fn parse<'a>(&'a self) -> Result<(), &'a str> { +``` + +That is, the error part of `parse`'s return value is tied to the parser. That +makes sense, as it's a pointer to the `Context that it holds. So that's the +problem, in `parse_context`, we return this result from `parse`, which is tied +to the lifetime of the `Parser`. But the `Parser` won't live past the end of +the function; it's temporary. Hence the lifetime issue. + +However, this is safe: we know that the only reason that the result is tied to +the `Parser` is because it's referencing the `Parser`'s `Context`, so it's +_really_ the `Context` that we care about. We need a way to tell Rust that the +`Context` and the `Parser may have different lifetimes. + +We could try that like this, but it doesn't quite work: + +```rust,ignore +struct Context<'a>(&'a str); + +struct Parser<'a, 'b> { + context: &'a Context<'b>, +} + +impl<'a, 'b> Parser<'a, 'b> { + fn parse(&self) -> Result<(), &str> { + // a real implementation would do a lot more, of course... + Err(&self.context.0[1..]) + } +} + +fn parse_context(context: Context) -> Result<(), &str> { + Parser { context: &context }.parse() +} +``` + +Here's the error: + +```text +error[E0491]: in type `&'a main::Context<'b>`, reference has a longer lifetime than the data it references + --> :5:5 + | +5 | context: &'a Context<'b>, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the pointer is valid for the lifetime 'a as defined on the struct at 4:0 + --> :4:1 + | +4 | struct Parser<'a, 'b> { + | _^ starting here... +5 | | context: &'a Context<'b>, +6 | | } + | |_^ ...ending here +note: but the referenced data is only valid for the lifetime 'b as defined on the struct at 4:0 + --> :4:1 + | +4 | struct Parser<'a, 'b> { + | _^ starting here... +5 | | context: &'a Context<'b>, +6 | | } + | |_^ ...ending here +help: consider using an explicit lifetime parameter as shown: fn main() + --> :1:1 + | +1 | fn main() { + | ^ +``` + +Rust doesn't know of any relationship between `'b` and `'a`, so now that we've +said `&'a Context<'b>`, `'b` needs to _outlive_ `'a`, or else, we'd be pointing +to invalid state. + +This is the feature we're talking about in this section. That was a very +long-winded example, but like we said at the start of this chapter, the tools +here are fairly niche. :) We need to be able to say "hey Rust: `'b` will live +at least as long as `'a`." And we have some simple syntax for that: `'b: 'a`. + +If we add that to our definition for `Parser`... + +```rust +struct Context<'a>(&'a str); + +struct Parser<'a, 'b: 'a> { + context: &'a Context<'b>, +} +``` + +Now, the `Parser`'s `Context` and the reference to it have different +lifetimes, and we've ensured that it's longer than the reference to it. + +We also need to adjust the `impl` block to take both lifetimes... + +```rust,ignore +impl<'a, 'b> Parser<'a, 'b> { +``` + +... and then, the signature of `parse` needs to make use of `'b`, to show that +the result comes from the `Context`: + +```rust,ignore + fn parse(&self) -> Result<(), &'b str> { +``` + +After those minor changes, it will work! Here's the full code: + + +```rust +struct Context<'a>(&'a str); + +struct Parser<'a, 'b: 'a> { + context: &'a Context<'b>, +} + +impl<'a, 'b> Parser<'a, 'b> { + fn parse(&self) -> Result<(), &'b str> { + // a real implementation would do a lot more, of course... + Err(&self.context.0[1..]) + } +} + +fn parse_context<'a>(context: Context<'a>) -> Result<(), &'a str> { + Parser { context: &context }.parse() +} +``` + +As a recap: `'b: 'a` says that "the lifetime b will live at least as long as +the lifetime a." You don't often need this syntax, but it can come up in +situations like this one, where you need to refer to something you have a +reference to that also has lifetimes. + +## Lifetime bounds + +We've used traits to bound generic types before, but you can also use lifetimes +for those bounds. For example, let's say we wanted to make a wrapper over +references. Using no bounds at all gives an error: + +```rust,ignore +struct Ref(&T); +``` + +Like this: + +```text +error[E0309]: the parameter type `T` may not live long enough + --> :2:19 + | +2 | struct Ref<'a, T>(&'a T); + | ^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'a`... +note: ...so that the reference type `&'a T` does not outlive the data it points at + --> :2:19 + | +2 | struct Ref<'a, T>(&'a T); + | ^^^^^^ +``` + +Rust helpfully gave us good advice: + +> consider adding an explicit lifetime bound `T: 'a` so that the reference type +> `&'a T` does not outlive the data it points to. + +This works: + +```rust +struct Ref<'a, T: 'a>(&'a T); +``` + +The `T: 'a` syntax says "T can be any type, but if it contains any references, +it must live as long as `'a`." + +We could sort of do the reverse with `'static`: + +```rust +struct StaticRef(&'static T); +``` + +This says "If `T` contains any references, they must be `'static` ones. + +Types with no references inside count as `'static`, and since `'static` is +longer than any other lifetime, a type like `T: 'a` can be a type with no +references. + +## Lifetimes in trait objects + +In chapter 17, we learned about trait objects, like this: + +```rust +trait Foo { } + +impl Foo for i32 { } + +let obj = Box::new(5) as Box; +``` + +However, what if the type implementing our trait has a lifetime? + +```rust +trait Foo { } + +struct Bar<'a> { + x: &'a i32, +} + +impl<'a> Foo for Bar<'a> { } + +let num = 5; + +let obj = Box::new(Bar { x: &num }) as Box; +``` + +This code works. But how? We haven't said anything about the liftimes of the +object. + +Well, as it turns out, there are rules. For a trait object like `Box`, +we can add a lifetime bound as well, like `Box`, for example. Just as +with the other bounds, this means "Any implementor of `Foo` which has a +lifetime inside must be `'a`." But we didn't need to explicitly write this. +Here are the rules: + +* The default begins as 'static. +* If you have `&'a X` or `&'a mut X`, then the default is `'a`. +* If you have a single `T: 'a` clasues, then the default is `'a`. +* If you have multiple `T: 'a`-like clauses, then there is no default; you must + be explicit. + +If you need to be explicit, `Box` or `Box` is the way +to do it. + +## Higher ranked trait bounds + +Sometimes, you may write a function which accepts a closure, and that closure +takes a reference as an argument: + +```rust +fn call_with_ref(some_closure:F) -> i32 + where F: Fn(&i32) -> i32 { + + let value = 0; + + some_closure(&value) +} +``` + +This code compiles just fine, but what about the lifetime here? With the +elision rules, we don't actually *need* to write out the lifetime, but what if +we did? + +You might think that you'd write it something like this: + +```rust +fn call_with_ref<'a, F>(some_closure:F) -> i32 + where F: Fn(&'a i32) -> i32 { +# +# let value = 0; +# +# some_closure(&value) +# } +``` + +This will compile, but it's actually taking advantage of one last bit of syntax +sugar. Because our trait is generic, yet it also *contains* a generic lifetime, +we need a way to say that our generic is generic. In general, these kinds of +"generic of generic" issues are referred to with the words "higher", like +"higher kinded type." In this case, it's a "higher rank type." What that means +isn't important, but the implication is that Rust is doing something special +here for us. + +If we wanted to write it out entirely, we'd use this syntax, with `for<>`: + +```rust +fn call_with_ref(some_closure:F) -> i32 + where F: for<'a> Fn(&'a i32) -> i32 { +# +# let value = 0; +# +# some_closure(&value) +# } +``` + +This says "for any lifetime `'a`." Think of it as similar to how a generic +function says "for any type `T`." + +This comes up extremely rarely in Rust code. It's an explicit goal of one of +the members of the language design team that you should never need to write an +explicit `for<'a>`, but you can if you'd like to. diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 3e8cfdcaa..20643ef24 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -1,23 +1,361 @@ # Advanced Traits +We covered traits in Chapter 10, but like lifetimes, we didn't get to all the +details. Now that we know more Rust, we can get into the nitty-gritty. + ## Associated Types -More common than the other things, less common than the rest of the book +We've described most of the things in this chapter as being very rare. +Associated types are somewhere in the middle; they're more rare than the rest +of the book, but more common than many of the things in this chapter. -why this is a thing instead of a generic +Associated types look like this: -## The Thing Formerly Known as UFCS +```rust +trait Foo { + type Bar; -Only needed when implementing super generic code + fn foo(&self) -> Self::Bar; +} -Lots of things are syntax sugar for this +impl Foo for i32 { + type Bar = String; -Two traits that impl the same method - how to disambiguate + fn foo(&self) -> Self::Bar { + self.to_string() + } +} +``` + +The trait `Foo` has an associated type called `Bar`. We can then use +`Self::Bar` elsewhere in our trait definition to use that type. + +This _feels_ like more generics. For example, this seems similar to +the following code: + +```rust +trait Foo { + fn foo(&self) -> Bar; +} + +impl Foo for i32 { + fn foo(&self) -> String { + self.to_string() + } +} +``` + +But there's one big difference: with the second definition, we could also +implement `Foo for i32`, or anything else. In other words, with a trait +that has a generic parameter, we can implement that trait for a type multiple +times, changing the parameters each time. But with associated types, we can't; +we can only define it one time: it's not actually generic. + +There's another benefit to associated types: when using the trait, since there's +only one possible implementation, you end up with a lot less syntax. This is +easier with some code: + +```rust +// a generic graph +trait GGraph { + // methods would go here +} + +// an associated graph +trait AGraph { + type Node; + type Edge; + + // methods would go here +} +``` + +Let's say we wanted to compute the distance between two nodes in the graph. +With the generic graph, you'd have to write this: + +```rust,ignore +fn distance>(graph: &G, start: &N, end: &N) -> u32 { ... } +``` + +Even though `distance` doesn't need to know the types of the edges, we're +forced to declare an `E` parameter, because we need to to use `Graph`. But with +the associated type version: + +```rust,ignore +fn distance(graph: &G, start: &G::Node, end: &G::Node) -> u32 { ... } +``` + +This is much cleaner. + +## Fully qualified syntax + +Sometimes, methods can have the same names. Consider this code: + +```rust +trait Foo { + fn f(&self); +} + +trait Bar { + fn f(&self); +} + +struct Baz; + +impl Foo for Baz { + fn f(&self) { println!("Baz’s impl of Foo"); } +} + +impl Bar for Baz { + fn f(&self) { println!("Baz’s impl of Bar"); } +} + +let b = Baz; +``` + +If we were to try to call `b.f()`, we’d get an error: + +```text +error[E0034]: multiple applicable items in scope + --> :21:3 + | +21 | b.f(); + | ^ multiple `f` found + | +note: candidate #1 is defined in an impl of the trait `main::Foo` for the type `main::Baz` + --> :13:5 + | +13 | fn f(&self) { println!("Baz’s impl of Foo"); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: candidate #2 is defined in an impl of the trait `main::Bar` for the type `main::Baz` + --> :17:5 + | +17 | fn f(&self) { println!("Baz’s impl of Bar"); } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``` + +We need a way to disambiguate which method we need. We can do that like this: + +```rust +# trait Foo { +# fn f(&self); +# } +# trait Bar { +# fn f(&self); +# } +# struct Baz; +# impl Foo for Baz { +# fn f(&self) { println!("Baz’s impl of Foo"); } +# } +# impl Bar for Baz { +# fn f(&self) { println!("Baz’s impl of Bar"); } +# } +# let b = Baz; +::f(&b); +::f(&b); +``` + +In other words, we can turn this: + +```rust,ignore +foo.bar(args); +``` + +Into this: + +```rust,ignore +::bar(foo, args); +``` + +In a more generic sense, + +```rust,ignore +::method(receiver, args); +``` + +We only need the `Type as` part if it's ambiguous. And we only need the `<>` +part if we need the `Type as` part. So in some cases, you could write + +```rust,ignore +Trait::method(receiver, args); +``` + +This would have worked above: + +```rust +# trait Foo { +# fn f(&self); +# } +# trait Bar { +# fn f(&self); +# } +# struct Baz; +# impl Foo for Baz { +# fn f(&self) { println!("Baz’s impl of Foo"); } +# } +# impl Bar for Baz { +# fn f(&self) { println!("Baz’s impl of Bar"); } +# } +# let b = Baz; +Foo::f(&b); +Bar::f(&b); +``` + +Here's an example of where the longer form is needed. We have an inherent +method `foo` and a trait method `foo`: + + +```rust +trait Foo { + fn foo() -> i32; +} + +struct Bar; + +impl Bar { + fn foo() -> i32 { + 20 + } +} + +impl Foo for Bar { + fn foo() -> i32 { + 10 + } +} + +fn main() { + assert_eq!(10, ::foo()); + assert_eq!(20, Bar::foo()); +} +``` + +Using this syntax lets you call the trait method instead of the inherent one. + +## Super traits + +Sometimes, you may want a trait to be able to rely on another trait existing. +For example, let's say that you have a `Foo` trait and a `Bar` trait, but you +want `Bar`'s methods to be able to call `Foo`'s methods. Let's try it. (It +won't work just yet...) + +```rust,ignore +trait Foo { + fn foo(&self) { + println!("Foo"); + } +} + +trait Bar { + fn bar(&self) { + self.foo(); + } +} +``` + +We get this error: + +```text +error: no method named `foo` found for type `&Self` in the current scope + --> :10:14 + | +10 | self.foo(); + | ^^^ + | + = help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item `foo`, perhaps you need to implement it: + = help: candidate #1: `main::Foo` +``` + +In other words, we haven't said that anything that implements `Bar` also +implements `Foo`. We can do that with a `:`, like this: + +```rust +trait Foo { + fn foo(&self) { + println!("Foo"); + } +} + +trait Bar: Foo { + fn bar(&self) { + self.foo(); + } +} +``` + +This works fine. ## Coherence -Show examples of when you control traits and types or not +Finally, traits have a concept called 'coherence'. This governs exactly who is +allowed to implement a trait. In short: -Ex: Cannot impl Debug on someone else's type +> To implement a type for a trait, you must have defined either the type, the +> trait, or both. -Solution: newtype +Put another way: + +> You cannot implement a trait you didn't define for a type you didn't define. + +For example, defining the `Display` trait, which is defined in the standard +library, on a tuple of string slices, which is defined in the standard library, +won't work: + +```rust,ignore +use std::fmt; + +impl fmt::Display for (&'static str, &'static str) { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {})", self.0, self.1) + } +} +``` + +gives + +```text +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> :4:1 + | +4 | impl fmt::Display for (&'static str, &'static str) { + | _^ starting here... +5 | | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +6 | | write!(f, "({}, {})", self.0, self.1) +7 | | } +8 | | } + | |_^ ...ending here: impl doesn't use types inside crate + | + = note: the impl does not reference any types defined in this crate +``` + +Why do we have this rule? Allowing this would lead to ambiguity, confusion, and +broken code. Imagine that we have a crate `foo` that has a type `A` and a +trait `B`. If we could implement `B` for `A` in our code, it would work, but +what if someone else _also_ implemented `B` for `A` in their code? Furthermore, +what if a new release of `foo` comes out and implements `B` for `A` themselves? +These problems are not insurmountable, of course; we could determine some kind +of complex precedent rules to determine which `impl` 'wins' and works. + +## The newtype pattern + +There is a way to get around this, though. We call it the 'newtype pattern'. +You create a new type that's a thin wrapper around the type you want to +implement the trait for, and then implement the trait for the wrapper. This +*will* work: + +```rust +use std::fmt; + +struct Wrapper((&'static str, &'static str)); + +impl fmt::Display for Wrapper { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {})", (self.0).0, (self.0).1) + } +} +``` + +The downside is that since `Wrapper` is a new type, it has no methods; we'll +have to implement them all. If you want it to have every single method that the +inner type has, implementing `Deref` can help you there. Otherwise, you'll have +to implement the methods yourself. From c78da574433f9bcf82f20ed5114fe97df512116b Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 17:34:43 -0500 Subject: [PATCH 03/30] Spelling --- second-edition/dictionary.txt | 7 +++++++ second-edition/src/ch19-01-unsafe-rust.md | 7 +++---- second-edition/src/ch19-02-advanced-lifetimes.md | 16 ++++++++-------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index 06c4ecf4d..f07a85390 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -6,6 +6,7 @@ adaptor adaptors Addr aggregator +AGraph aliasability alignof Amir @@ -17,6 +18,7 @@ backtrace backtraces BACKTRACE Backtraces +Baz’s benchmarking bitand BitAnd @@ -104,6 +106,7 @@ FnMut FnOnce formatter FromIterator +GGraph GitHub gitignore grapheme @@ -147,6 +150,7 @@ iter iterator's JavaScript JoinHandle +kinded lang latin libc @@ -188,6 +192,7 @@ namespacing newfound NewsArticle newtype +nitty nocapture nomicon Nomicon @@ -260,6 +265,7 @@ spdx SpreadsheetCell sqrt stackoverflow +StaticRef stderr stdin Stdin @@ -314,6 +320,7 @@ uninstall unix unoptimized UnsafeCell +unsafety unsized unsynchronized username diff --git a/second-edition/src/ch19-01-unsafe-rust.md b/second-edition/src/ch19-01-unsafe-rust.md index b0b3db04a..ac9e0f983 100644 --- a/second-edition/src/ch19-01-unsafe-rust.md +++ b/second-edition/src/ch19-01-unsafe-rust.md @@ -14,7 +14,7 @@ these cases, you can use unsafe code to tell the compiler, "trust me, I know what I'm doing." The downside is that you're on your own; if you get it wrong, bad things can happen. -There's another reason that Rust needs to have unsafe code: the underyling +There's another reason that Rust needs to have unsafe code: the underlying hardware of computers is not safe. If Rust didn't let you do unsafe things, then there would be some things that you simply could not do. But Rust needs to be able to let you do things like directly interact with your operating system, @@ -57,7 +57,7 @@ make a mistake and something goes wrong, you'll know that it has to be related to one of the places that you opted into this unsafety. That makes these bugs much easier to find. Because of this, it's important to contain your unsafe code to as small of an area as possible. Once you use unsafe inside of a -module, any of the code in that module is supect. Keep them small and you'll +module, any of the code in that module is suspect. Keep them small and you'll thank yourself later. One final note about unsafe blocks: while unsafe blocks let you do almost @@ -159,7 +159,7 @@ By inserting the `unsafe` block, you're asserting to Rust that you've read the documentation for this function, you understand how to use it properly, and you've verified that everything is correct. -Raw pointers and unsafe functions often interact, becuase unsafe functions +Raw pointers and unsafe functions often interact, because unsafe functions often take raw pointers as arguments. Given that raw pointers aren't checked, a very common constraint on unsafe functions is "make sure the raw pointers you're passing to it are valid." @@ -251,7 +251,6 @@ let address = 0x012345; let r = address as *mut i32; let slice = unsafe { - // noooooooooooo slice::from_raw_parts_mut(r, 10000) }; ``` diff --git a/second-edition/src/ch19-02-advanced-lifetimes.md b/second-edition/src/ch19-02-advanced-lifetimes.md index ffe083897..b9d6fdfcb 100644 --- a/second-edition/src/ch19-02-advanced-lifetimes.md +++ b/second-edition/src/ch19-02-advanced-lifetimes.md @@ -21,7 +21,7 @@ Imagine that we want to write a parser. To do this, we'll have a structure with the string that we're parsing, a 'context'. We'll write individual parsers that parse this string, and return success or failure. The parsers will need to borrow the context to do the parsing. We'd end up with something like the -following. We've left off the lifetime anntations for now; this code won't +following. We've left off the lifetime annotations for now; this code won't compile: ```rust,ignore @@ -418,18 +418,18 @@ let num = 5; let obj = Box::new(Bar { x: &num }) as Box; ``` -This code works. But how? We haven't said anything about the liftimes of the +This code works. But how? We haven't said anything about the lifetimes of the object. Well, as it turns out, there are rules. For a trait object like `Box`, we can add a lifetime bound as well, like `Box`, for example. Just as -with the other bounds, this means "Any implementor of `Foo` which has a +with the other bounds, this means "Any implementer of `Foo` which has a lifetime inside must be `'a`." But we didn't need to explicitly write this. Here are the rules: * The default begins as 'static. * If you have `&'a X` or `&'a mut X`, then the default is `'a`. -* If you have a single `T: 'a` clasues, then the default is `'a`. +* If you have a single `T: 'a` clause, then the default is `'a`. * If you have multiple `T: 'a`-like clauses, then there is no default; you must be explicit. @@ -460,9 +460,9 @@ You might think that you'd write it something like this: ```rust fn call_with_ref<'a, F>(some_closure:F) -> i32 where F: Fn(&'a i32) -> i32 { -# +# # let value = 0; -# +# # some_closure(&value) # } ``` @@ -480,9 +480,9 @@ If we wanted to write it out entirely, we'd use this syntax, with `for<>`: ```rust fn call_with_ref(some_closure:F) -> i32 where F: for<'a> Fn(&'a i32) -> i32 { -# +# # let value = 0; -# +# # some_closure(&value) # } ``` From 8f1300496140beddf48e2bbaeed9560655449fac Mon Sep 17 00:00:00 2001 From: Steve Klabnik Date: Mon, 27 Feb 2017 21:00:21 -0500 Subject: [PATCH 04/30] fixes --- .../src/ch19-02-advanced-lifetimes.md | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/second-edition/src/ch19-02-advanced-lifetimes.md b/second-edition/src/ch19-02-advanced-lifetimes.md index b9d6fdfcb..34db347b0 100644 --- a/second-edition/src/ch19-02-advanced-lifetimes.md +++ b/second-edition/src/ch19-02-advanced-lifetimes.md @@ -457,7 +457,7 @@ we did? You might think that you'd write it something like this: -```rust +```rust,ignore fn call_with_ref<'a, F>(some_closure:F) -> i32 where F: Fn(&'a i32) -> i32 { # @@ -467,18 +467,17 @@ fn call_with_ref<'a, F>(some_closure:F) -> i32 # } ``` -This will compile, but it's actually taking advantage of one last bit of syntax -sugar. Because our trait is generic, yet it also *contains* a generic lifetime, -we need a way to say that our generic is generic. In general, these kinds of -"generic of generic" issues are referred to with the words "higher", like -"higher kinded type." In this case, it's a "higher rank type." What that means -isn't important, but the implication is that Rust is doing something special -here for us. +This will not compile. Because our trait is generic, yet it also *contains* a +generic lifetime, we need a way to say that our generic is generic. In general, +these kinds of "generic of generic" issues are referred to with the words +"higher", like "higher kinded type." In this case, it's a "higher rank type." +What that means isn't important, but the implication is that Rust is doing +something special here for us. If we wanted to write it out entirely, we'd use this syntax, with `for<>`: ```rust -fn call_with_ref(some_closure:F) -> i32 +fn call_with_ref(some_closure: F) -> i32 where F: for<'a> Fn(&'a i32) -> i32 { # # let value = 0; @@ -487,6 +486,12 @@ fn call_with_ref(some_closure:F) -> i32 # } ``` +failures: + Advanced_Lifetimes_19 + +test result: FAILED. 11 passed; 1 failed; 9 ignored; 0 measured + + This says "for any lifetime `'a`." Think of it as similar to how a generic function says "for any type `T`." From 69356b33471a1abaad159dd02854ab36aebe576d Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 27 Feb 2017 17:41:03 -0500 Subject: [PATCH 05/30] code style --- second-edition/src/ch19-02-advanced-lifetimes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/second-edition/src/ch19-02-advanced-lifetimes.md b/second-edition/src/ch19-02-advanced-lifetimes.md index 34db347b0..ffd9ec280 100644 --- a/second-edition/src/ch19-02-advanced-lifetimes.md +++ b/second-edition/src/ch19-02-advanced-lifetimes.md @@ -442,7 +442,7 @@ Sometimes, you may write a function which accepts a closure, and that closure takes a reference as an argument: ```rust -fn call_with_ref(some_closure:F) -> i32 +fn call_with_ref(some_closure: F) -> i32 where F: Fn(&i32) -> i32 { let value = 0; From f75c1b760256cfbcb94a6953f192507e6b2c4ccc Mon Sep 17 00:00:00 2001 From: steveklabnik Date: Mon, 10 Apr 2017 12:15:52 -0400 Subject: [PATCH 06/30] impl Add for OperatorOverloading --- second-edition/src/ch19-03-advanced-traits.md | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 20643ef24..629fa0d22 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -87,6 +87,114 @@ fn distance(graph: &G, start: &G::Node, end: &G::Node) -> u32 { ... } This is much cleaner. +## Operator overloading and default type parameters + +We can use traits in Rust to overload certain operators. Rust does not allow you to +create your own operators, or overload arbitrary operators: only the operations listed +in `std::ops` can be overloaded. Here's an example: + +```rust +use std::ops::Add; + +#[derive(Debug,PartialEq)] +struct Point { + x: i32, + y: i32, +} + +impl Add for Point { + type Output = Point; + + fn add(self, other: Point) -> Point { + Point { + x: self.x + other.x, + y: self.y + other.y, + } + } +} + +fn main() { + assert_eq!(Point { x: 1, y: 0 } + Point { x: 2, y: 3 }, + Point { x: 3, y: 3 }); +} +``` + +The `Add` trait lets us overload the `+` operator. We've implemented it for +a `Point` such that it adds the `x`s and `y`s together to make a new `Point`. +You'll notice that the `Add` trait has an `Output` associated type; this is +used to determine the result of the operation. + +Let's look at `Add` in a bit more detail. Here's its definition: + +```rust +trait Add { + type Output; + + fn add(self, rhs: RHS) -> Self::Output; +} +``` + +This should look familiar; it's a trait with one method and an associated type. But +there's one bit of syntax we haven't seen before: `RHS=Self`. What's up with that? + +This syntax is called 'default type parameters'. It allows you to say "If a parameter +isn't provided, use this default instead." So in other words, these two trait definitions +are very similar: + +```rust,ignore +trait Add { +trait Add { +``` + +The only difference is, with the first definition, we are required to parameterize +`Add` with a type for `RHS`, which is short for "right hand side." In the latter +form, we aren't required to, and if we do not, the type of `RHS` will be the type +of `Self`. + +Let's look at an example. Imagine we have two units, `Feet` and `Inches`. We can +implement `Add` like this: + +```rust +use std::ops::Add; + +struct Milimeters(u32); +struct Meters(u32); + +impl Add for Milimeters { + type Output = Milimeters; + + fn add(self, other: Milimeters) -> Milimeters { + Milimeters(self.0 + other.0) + } +} + +impl Add for Milimeters { + type Output = Milimeters; + + fn add(self, other: Meters) -> Milimeters { + Milimeters(self.0 + (other.0 * 1000)) + } +} +``` + +If we're adding `Milimeters` to other `Milimeters`, we don't need to parameterize +`Add`. If we want to add `Milimeters` to `Meters`, then we need to say `Add` +to set the value of the `RHS`. + +Default type parameters are used in two main ways: + +1. To extend a type without breaking existing code. +2. To allow customization in a way most users don't want. + +This is an example of the second purpose; most of the time, you're adding two +like types together. Using the default here makes it easier to do so without +the extra parameter. In other words, we've removed a little bit of boilerplate. + +What about the first case? Well, it's sort of the same thing, but in reverse: +because our existing users won't have written down a type parameter, if we want +to add a type parameter to an existing trait, giving it a default will let us +not break that code. + ## Fully qualified syntax Sometimes, methods can have the same names. Consider this code: From ac0b259c65f1855e6337ca462b6189f659f755ac Mon Sep 17 00:00:00 2001 From: steveklabnik Date: Mon, 10 Apr 2017 14:57:47 -0400 Subject: [PATCH 07/30] advanced types --- second-edition/src/SUMMARY.md | 2 + second-edition/src/ch19-04-advanced-types.md | 269 ++++++++++++++++++ ...ch19-05-advanced-functions-and-closures.md | 13 + 3 files changed, 284 insertions(+) create mode 100644 second-edition/src/ch19-04-advanced-types.md create mode 100644 second-edition/src/ch19-05-advanced-functions-and-closures.md diff --git a/second-edition/src/SUMMARY.md b/second-edition/src/SUMMARY.md index 87417b709..615fd783f 100644 --- a/second-edition/src/SUMMARY.md +++ b/second-edition/src/SUMMARY.md @@ -108,6 +108,8 @@ - [Unsafe Rust](ch19-01-unsafe-rust.md) - [Advanced Lifetimes](ch19-02-advanced-lifetimes.md) - [Advanced Traits](ch19-03-advanced-traits.md) + - [Advanced Types](ch19-04-advanced-types.md) + - [Advanced Functions & Closures](ch19-05-advanced-functions-and-closures.md) - [Un-named project](ch20-00-unnamed-project.md) diff --git a/second-edition/src/ch19-04-advanced-types.md b/second-edition/src/ch19-04-advanced-types.md new file mode 100644 index 000000000..a981dee32 --- /dev/null +++ b/second-edition/src/ch19-04-advanced-types.md @@ -0,0 +1,269 @@ +# Advanced Types + +There's a few aspects of Rust's type system we haven't gone over. Write a better +intro that isn't literally the same as every other section here :frown: + +## Type Aliases + +Rust provides the ability to declare a 'type alias' with the `type` keyword: + +```rust +type Foo = i32; +``` + +This means that `Foo` is a _synonym_ for `i32`; it's not its own, new type. Which +means you can do this: + +```rust +type Foo = i32; + +let x: i32 = 5; +let y: Foo = 5; + +println!("x + y = {}", x + y); +``` + +Since `Foo` is an alias for `i32`, they're the same type, and we can add them together. +If you want a distinct type for `Foo`, you'd use the newtype pattern from Chapter XX. + +The main use-case for type synonyms is to reduce repitition. For example, you may have +a type like this: + +```rust,ignore +Box +``` + +Typing this out all over the place can be tiresome and error-prone: + +```rust,ignore +let f: Box = |x| x + 1; + +fn takes_long_type(f: Box) { + // ... +} + +fn returns_long_type() -> Box { + // ... +} +``` + +An alias makes this more manageable: + +```rust,ignore +type Thunk = Box; + +let f: Thunk = |x| x + 1; + +fn takes_long_type(f: Thunk) { + // ... +} + +fn returns_long_type() -> Thunk { + // ... +} +``` + +Much easier. A related case is with the `Result` type. Consider the `std::io` +module in the standard library. I/O operations often return a `Result`, as they +may fail to work. So, there's a struct, `std::io::Error`, that represents all of these +different possible errors. Many of the functions in `std::io` will be returning a +`Result` where the `E` is an `std::io::Error`. For example, the `Write` trait: + +```rust,ignore +use std::io::Error; + +pub trait Write { + fn write(&mut self, buf: &[u8]) -> Result; + fn flush(&mut self) -> Result<(), Error>; + + fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { ... } + fn write_fmt(&mut self, fmt: Arguments) -> Result<(), Error> { ... } +} +``` + +We're writing `Result<..., Error>` a lot. As such, `std::io` has this +declaration: + +```rust,ignore +type Result = Result; +``` + +Because this is in the `std::io` module, it's now `std::io::Result`; that is, +a `Result` with the `E` filled in as `std::io::Error`. This helps in two +ways: first, the `Write` trait ends up looking like this: + +```rust,ignore +pub trait Write { + fn write(&mut self, buf: &[u8]) -> Result; + fn flush(&mut self) -> Result<()>; + + fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... } + fn write_fmt(&mut self, fmt: Arguments) -> Result<()> { ... } +} +``` + +This is easier to write *and* gives us a consistent interface across all +of `std::io`. But because it's an alias, it is just another `Result`, +which means we can use any methods that work on `Result` with it, +and special syntax like `?`. + +## The 'never' type, `!` + +Rust has a special type named `!`. In type theory lingo, it's called the 'bottom type', +but we prefer the name 'never'. The name describes what it does: + +```rust,ignore +fn bar() -> ! { +``` + +This is read as "the function `bar` returns never." And in this case, that's what +it means! You cannot create values of the type `!`, and so `bar` can never possibly +return. How could it, if it can't create a value to return? + +What use is a type you can never create values for? If you think all the way back +to Chapter 2, we had some code that looked like this: + +```rust,ignore +let guess: u32 = match guess.trim().parse() { + Ok(num) => num, + Err(_) => continue, +}; +``` + +At the time, we skipped over some details. For example, you've learned that +`match` arms must have the same value. This doesn't work: + +```rust,ignore +let guess = match guess.trim().parse() { + Ok(_) => 5, + Err(_) => "hello", +} +``` + +What would the type of `guess` be here? It'd have to be both an integer and a string, +and that doesn't work. So why does `continue`? + +As you may have guessed, `continue` has a value of `!`. That is, when Rust goes to +compute the type of `guess`, it looks at both of the match arms. The former has a +value of `u32`, and the latter has a value of `!`. Since `!` can never have a value, +Rust is okay with this, and decides that the type of `guess` is `u32`. The fancy way +of saying this is that "never unifies with all other types". This works becuase +`continue` doesn't actually return a value; it instead moves control back to the top +of the loop. In the `Err` case, we never actually assign a value to `guess`. So +this is fine. + +Another example of the never type is `panic!`. Remember the `unwrap` function that +we call on `Option` values to produce a value or panic? Here's its definition: + +```rust,ignore +impl Option { + pub fn unwrap(self) -> T { + match self { + Some(val) => val, + None => panic!("called `Option::unwrap()` on a `None` value"), + } + } +} +``` + +Here, the same thing happens: We know that `val` has the type `T`, and `panic!` has +the type `!`. So the result of the overall `match` expression is `T`. This works +because `panic!` doesn't produce a value; it panics. In the `None` case, we won't be +returning a value from `unwrap`, and so it all works out. + +One final expression that has the type `!` is a `loop`: + +```rust,ignore +print!("forever "); + +loop { + print!("and ever "); +} +``` + +Here, the loop never ends, and so the value of the expression is `!`. This +wouldn't be true if we included a `break`, however, as the loop would terminate. + +## Dynamically Sized Types & `Sized` + +Because Rust needs to know things like memory layout, there's a particular corner +of its type system that can be confusing, and that's the concept of 'dynamically +sized types.' Sometimes referred to as 'DSTs' or 'unsized types', these types let +us talk about things that we only know the size of at runtime. + +That's extremely abstract, so let's dig into the details of a dynamically sized +type that we've been using this whole book: `str`. That's right, not `&str`, but +`str`, on its own. `str` is a DST; we can't know how long the string is until +runtime. Since we can't know that, we can't create a variable of type `str`; +nor can we take an argument of type `str`. Consider this code, which does not +work: + +```rust,ignore +let s1: str = "Hello there!"; +let s2: str = "How's it going?"; +``` + +These two `str`s would need to have the exact same memory layout, but they have +different lengths: `s1` needs 12 bytes of storage, and `s2` needs 15. This is +why it's not possible to create a variable holding a dynamically sized type. + +So what to do? Well, you already know the answer in this case: `s1` and `s2` +aren't just `str`s, but `&str`s, and more specifically, `&'static str`s, though +the static bit isn't particularly relevant here. If you think back to Chapter 4, +we said this about `&str`: + +> ... it’s a reference to an internal position in the String and the number of +> elements that it refers to. + +So while a `&T` is a single value, storing the memory address of where the `T` +is located, a `&str` is _two_ values: the address of the `str`, and how long +it is. As such, a `&str` has a size we can know at compile time: it's two +`usizes` in length. That is, we always know the size of a `&str`, no matter +how long the string it refers to is. This is the general way in which dynamically +sized types are used in Rust; they have an extra bit of metadata that stores +the dynamic information. This leads us to the golden rule of dynamically sized +types: + +You must always put values of dynamically sized types behind a pointer of some +kind. + +While we've talked a lot about `&str`, we can combine `str` with all kinds of +pointers: `Box`, for example, or `Rc`. In fact, you've already seen +this before, but with a different dynamically sized type: `Trait`. That is, +the name of a trait, without any sort of qualifications. In Chapter 17, +we only talked about `Box` as a trait object, but given that +just `Trait` on its own is a dynamically sized type, `Rc` or +`&Trait` work too. + + + +### The Sized trait + +To work with DSTs, Rust has a trait that determines if a type's size is known +at compile time or not: `Sized`. This trait is automatically implemented for +everything the compiler knows the size of at compile time. In addition, Rust +sneaks in a bound on `Sized` to every generic function. That is, + +```rust,ignore +fn generic(t: T) { +``` + +is actually + +```rust,ignore +fn generic(t: T) { +``` + +That is, by default, everything can only work on types that are sized at compile +time. There is, however, special syntax you can use to relax this restriction: + +```rust,ignore +fn generic(t: &T) { +``` + +There's two differences here: `?Sized` is the opposite of `Sized`, that is, this +reads as '`T` may or may not be `Sized`. This syntax is only available for `Sized`, +and not other traits. + +Secondly, you'll note we switched to `&T`; because the argument may not be `Sized`, +we need to use it behind some kind of pointer, in this case, a reference. \ No newline at end of file diff --git a/second-edition/src/ch19-05-advanced-functions-and-closures.md b/second-edition/src/ch19-05-advanced-functions-and-closures.md new file mode 100644 index 000000000..e3995e78a --- /dev/null +++ b/second-edition/src/ch19-05-advanced-functions-and-closures.md @@ -0,0 +1,13 @@ +# Advanced Functions & Closures + +We've talked a lot about functions in this book, and a little bit about a +related feature, closures. There's a few bits we haven't covered yet, so let's +go over those now. + +## Function pointers + +## Diverging functions + +## Move closures + +## Returning closures From b350106553f7b674a16ad05370bd5ccd38a8a930 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 10 Apr 2017 11:19:30 -0400 Subject: [PATCH 08/30] Editing through unsafe rust --- .../src/ch19-00-advanced-features.md | 22 +- second-edition/src/ch19-01-unsafe-rust.md | 540 ++++++++++-------- 2 files changed, 313 insertions(+), 249 deletions(-) diff --git a/second-edition/src/ch19-00-advanced-features.md b/second-edition/src/ch19-00-advanced-features.md index 9e6aa1e28..687135004 100644 --- a/second-edition/src/ch19-00-advanced-features.md +++ b/second-edition/src/ch19-00-advanced-features.md @@ -1,15 +1,17 @@ # Advanced Features We've come a long way! By now, we've learned 99% of the things you'll need to -know when writing Rust. We'll wrap the book up by doing one more project, but -before we get to that, let's talk about a few things that you may run into that -last 1% of the time. Feel free to skip this chapter and come back to it once -you run into these things in the wild; the tools we'll learn to use here are -useful in very specific situations. We don't want to leave them out, but you -won't find yourself reaching for them often. +know when writing Rust. Before we do one more project in Chapter 20, let's talk +about a few things that you may run into that last 1% of the time. Feel free to +skip this chapter and come back to it once you run into these things in the +wild; the features we'll learn to use here are useful in very specific +situations. We don't want to leave these features out, but you won't find +yourself reaching for them often. -Here's a quick summary: +In this chapter, we're going to cover: -* Unsafe Rust: for when you need to tell Rust "just trust me, promise!" -* Advanced Lifetimes: Additional lifetime syntax for complex situations. -* Advanced Traits: Associated Types, coherence, and disambiguation. +* Unsafe Rust: for when you need to opt out of some of Rust's guarantees and + tell the compiler that you will be responsible for upholding the guarantees + instead +* Advanced Lifetimes: Additional lifetime syntax for complex situations +* Advanced Traits: Associated Types, coherence, and disambiguation diff --git a/second-edition/src/ch19-01-unsafe-rust.md b/second-edition/src/ch19-01-unsafe-rust.md index ac9e0f983..c715fd296 100644 --- a/second-edition/src/ch19-01-unsafe-rust.md +++ b/second-edition/src/ch19-01-unsafe-rust.md @@ -1,114 +1,119 @@ -# Unsafe Rust +## Unsafe Rust -So far, we've been talking about code written in Rust. That's what you'd expect -from a book on Rust! However, Rust has a second language hiding out inside of -it: unsafe Rust. Unsafe Rust works just like regular Rust does, but it gives -you extra superpowers not available in safe Rust code. +In all of the previous chapters in this book, we've been discussing code +written in Rust that has memory safety guarantees enforced at compile time. +However, Rust has a second language hiding out inside of it, unsafe Rust, which +does not enforce these memory safety guarantees. Unsafe Rust works just like +regular Rust does, but it gives you extra superpowers not available in safe +Rust code. -You may be wondering why this is. While Rust's safety guarantees are a -wonderful thing, by nature, static analysis is conservative. That is, when -trying to determine if something is okay or not, it's better to reject some -programs that are valid than it is to accept some programs that are invalid. -There are some times when your code might be okay, but Rust thinks it's not! In -these cases, you can use unsafe code to tell the compiler, "trust me, I know -what I'm doing." The downside is that you're on your own; if you get it wrong, -bad things can happen. +Unsafe Rust exists because, by nature, static analysis is conservative. When +trying to determine if code upholds some guarantees or not, it's better to +reject some programs that are valid than it is to accept some programs that are +invalid. There are some times when your code might be okay, but Rust thinks +it's not! In these cases, you can use unsafe code to tell the compiler, "trust +me, I know what I'm doing." The downside is that you're on your own; if you get +unsafe code wrong, problems due to memory unsafety like null pointer +dereferencing can occur. There's another reason that Rust needs to have unsafe code: the underlying -hardware of computers is not safe. If Rust didn't let you do unsafe things, -then there would be some things that you simply could not do. But Rust needs to -be able to let you do things like directly interact with your operating system, -or even write your own operating system! That's part of the goals of the -language. So we need some way to do these kinds of things. +hardware of computers is inherently not safe. If Rust didn't let you do unsafe +operations, there would be some tasks that you simply could not do. But Rust +needs to be able to let you do low-level systems programming like directly +interacting with your operating system, or even writing your own operating +system! That's part of the goals of the language. We need some way to do these +kinds of things. -## Unsafe Superpowers +### Unsafe Superpowers -More specifically, there are four things that you can do with unsafe Rust that -you cannot do in safe Rust. We call these the "unsafe superpowers." Here they -are: +We switch into unsafe Rust by using the `unsafe` keyword and starting a new +block that holds the unsafe code. There are four actions that you can take in +unsafe Rust that you can't in safe Rust. We call these the "unsafe +superpowers." We haven't seen most of these features yet since they're only +usable with `unsafe`! -1. Dereference a raw pointer. -2. Call an unsafe function. -3. Access or modify a static variable. -4. Implement an unsafe trait. +1. Dereferencing a raw pointer +2. Calling an unsafe function +3. Accessing or modifying a mutable static variable +4. Implementing an unsafe trait -We haven't seen most of these features yet because, well, they're only usable -by unsafe! That is, it's important to understand that unsafe doesn't "turn off -the borrow checker" or disable any of Rust's safety checks: if you use a -reference in unsafe code, it will still be checked. What it does do is give you -access to these new, unchecked features. You still get some degree of safety -inside of an unsafe block! +It's important to understand that `unsafe` doesn't turn off the borrow checker +or disable any other of Rust's safety checks: if you use a reference in unsafe +code, it will still be checked. The only thing the `unsafe` keyword does is +give you access to these four features that aren't checked by the compiler for +memory safety. You still get some degree of safety inside of an unsafe block! +Furthermore, `unsafe` does not mean the code inside the block is dangerous or +definitely will have memory safety problems: the intent is that you as the +programmer will ensure that the code inside an `unsafe` block will have valid +memory, since you've turned off the compiler checks. -Rust's strategy here is to make sure everything is safe, but allow you to do -extra unsafe things when you specifically annotate your code to allow unsafe -things. What kind of annotations? It looks like this: +People are fallible, however, and mistakes will happen. By requiring these four +unsafe operations to be inside blocks annotated with `unsafe`, if you make a +mistake and get an error related to memory safety, you'll know that it has to +be related to one of the places that you opted into this unsafety. That makes +the cause of memory safety bugs much easier to find, since we know Rust is +checking all of the other code for us. To get this benefit of only having a few +places to investigate memory safety bugs, it's important to contain your unsafe +code to as small of an area as possible. Once you use `unsafe` inside of a +module, any of the code in that module is suspect: keep `unsafe` blocks small +and you'll thank yourself later. -```rust -// only safe stuff here! -let x = 5; +In order to isolate unsafe code as much as possible, it's a good idea to +enclose unsafe code within a safe abstration and provide a safe API. Parts of +the standard library are implemented as safe abstractions over unsafe code that +has been audited. This prevents uses of `unsafe` from leaking out into all the +places that you or your users might want to make use of the functionality +implemented with `unsafe` code, since using a safe abstraction is safe. -unsafe { - // here be dragons! -} -``` +Let's talk about each of the four unsafe superpowers in turn, and along the way +we'll look at some abstractions that provide a safe interface to unsafe code. -You can only use these features inside of these blocks. This means that you do -make a mistake and something goes wrong, you'll know that it has to be related -to one of the places that you opted into this unsafety. That makes these bugs -much easier to find. Because of this, it's important to contain your unsafe -code to as small of an area as possible. Once you use unsafe inside of a -module, any of the code in that module is suspect. Keep them small and you'll -thank yourself later. +### Dereferencing a Raw Pointer -One final note about unsafe blocks: while unsafe blocks let you do almost -anything, there are still rules. That is, `unsafe` does not mean "now I will do -anything," `unsafe` means "I have manually checked that I am following the -rules." If you break the rules, bad things can still happen! - -Let's talk about each of these four superpowers in turn. - -## Raw Pointers - -Way back in chapter four, we learned about references: - -```rust -let r = &5; -``` - -We also learned that references are always valid, and that the compiler makes -sure that this is so. Unsafe Rust has two new types that are similar to -references called "raw pointers." +Way back in Chapter 4, we first learned about references. We also learned that +the compiler ensures that references are always valid. Unsafe Rust has two new +types similar to references called *raw pointers*. Just like references, we can +have an immutable raw pointer and a mutable raw pointer. Listing 19-1 shows how +to create raw pointers from references: ```rust let mut num = 5; -let r1 = &5 as *const i32; -let r2 = &mut 5 as *mut i32; +let r1 = &num as *const i32; +let r2 = &mut num as *mut i32; ``` -The `*const T` and `*mut T` types are raw pointers, in contrast with references -and mutable references, respectively. Unlike references, these pointers may or -may not be valid. We can even create raw pointers to arbitrary locations in -memory: +Listing 19-1: Creating raw pointers from references + +The `*const T` type is an immutable raw pointer, and `*mut T` is a mutable raw +pointer. We've created raw pointers by using `as` to cast an immutable and a +mutable reference into their corresponding raw pointer types. Unlike +references, these pointers may or may not be valid. + +Listing 19-2 shows how to create a raw pointer to an arbitrary location in +memory. Trying to use arbitrary memory is undefined: there may be data at that +address, there may not be any data at that address, or your program might +segfault. There's not usually a good reason to be writing code like this, but +it is possible: ```rust -// don't try this at home: let address = 0x012345; let r = address as *const i32; - -// bad things will happen if you try to use r ``` -But wait, we said that you need to use `unsafe` with raw pointers, but there's -no `unsafe` block in the above examples. What gives? While you can _create_ -raw pointers in safe code, you can't _dereference_ raw pointers in safe code. -To use `*`, you need `unsafe`: +Listing 19-2: Creating a raw pointer to an arbitrary +memory address + +Note there's no `unsafe` block in either Listing 19-1 or 19-2. You can *create* +raw pointers in safe code, but you can't *dereference* raw pointers and read +the data being pointed to. Using the dereference operator, `*`, on a raw +pointer requires an `unsafe` block, as shown in Listing 19-3: ```rust let mut num = 5; -let r1 = &5 as *const i32; -let r2 = &mut 5 as *mut i32; +let r1 = &num as *const i32; +let r2 = &mut num as *mut i32; unsafe { println!("r1 is: {}", *r1); @@ -116,26 +121,33 @@ unsafe { } ``` -This is because creating a pointer can't do any harm; it's only when accessing -the value that it points at that you might end up dealing with something that's -invalid. +Listing 19-3: Dereferencing raw pointers within an +`unsafe` block -Furthermore, in these examples, you may have noticed something: we created both -a `*const i32` and a `*mut i32` to the same memory location. With references, -this would be impossible, due to the mutability rules. With raw pointers, you -can do this. Be careful! +Creating a pointer can't do any harm; it's only when accessing the value that +it points at that you might end up dealing with an invalid value. -With all of these dangers, why would we ever use raw pointers? One major -use-case is interfacing with C code; we'll talk about this more in the next -section. Another case is to build up safe abstractions that the borrow checker -doesn't understand. Before we show an example, let's talk about unsafe -functions; you'll often be using them with raw pointers. +Note also that in Listing 19-1 and 19-3 we created a `*const i32` and a `*mut +i32` that both pointed to the same memory location, that of `num`. If we had +tried to create an immutable and a mutable reference to `num` instead of raw +pointers, this would not have compiled due to the rule that says we can't have +a mutable reference at the same time as any immutable references. With raw +pointers, we are able to create a mutable pointer and an immutable pointer to +the same location, and change data through the mutable pointer while the +immutable pointer expects the data not to change, potentially creating a data +race. Be careful! -## Unsafe Functions +With all of these dangers, why would we ever use raw pointers? One major use +case is interfacing with C code, as we'll see in the next section on unsafe +functions. Another case is to build up safe abstractions that the borrow +checker doesn't understand. Let's introduce unsafe functions then look at an +example of a safe abstraction that uses unsafe code. -The second thing that requires an unsafe block is a call to an unsafe function. -Unsafe functions look exactly like regular functions, but with an extra -`unsafe` out front: +### Calling an Unsafe Function + +The second operation that requires an unsafe block is calling an unsafe +function. Unsafe functions look exactly like regular functions, but they have +an extra `unsafe` out front: ```rust unsafe fn dangerous() {} @@ -145,7 +157,7 @@ unsafe { } ``` -If you try to call `dangerous` without the `unsafe` block, you'll get an error: +If we try to call `dangerous` without the `unsafe` block, we'll get an error: ```text error[E0133]: call to unsafe function requires unsafe function or block @@ -155,18 +167,17 @@ error[E0133]: call to unsafe function requires unsafe function or block | ^^^^^^^^^^^ call to unsafe function ``` -By inserting the `unsafe` block, you're asserting to Rust that you've read the -documentation for this function, you understand how to use it properly, and -you've verified that everything is correct. +By inserting the `unsafe` block around our call to `dangerous`, we're asserting +to Rust that we've read the documentation for this function, we understand how +to use it properly, and we've verified that everything is correct. -Raw pointers and unsafe functions often interact, because unsafe functions -often take raw pointers as arguments. Given that raw pointers aren't checked, a -very common constraint on unsafe functions is "make sure the raw pointers -you're passing to it are valid." +#### Creating a Safe Abstraction Over Unsafe Code As an example, let's check out some functionality from the standard library, -`split_at_mut`. This method is defined on mutable slices, and it takes one -slice and makes it into two, like this: +`split_at_mut`, and explore how we might implement it ourselves. This safe +method is defined on mutable slices, and it takes one slice and makes it into +two by splitting the slice at the index given as an argument, as demonstrated +in Listing 19-4: ```rust let mut v = vec![1, 2, 3, 4, 5, 6]; @@ -179,24 +190,38 @@ assert_eq!(a, &mut [1, 2, 3]); assert_eq!(b, &mut [4, 5, 6]); ``` -This function couldn't be written in safe Rust. If we tried, it might look like -this: +Listing 19-4: Using the safe `split_at_mut` +function + +This function can't be implemented using only safe Rust. An attempt might look +like Listing 19-5. For simplicity, we're implementing `split_at_mut` as a +function rather than a method, and only for slices of `i32` values rather than +for a generic type `T`: ```rust,ignore fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { - // get the total length of the slice let len = slice.len(); - // make sure that our midpoint is in bounds assert!(mid <= len); - // return two slices, from the start to mid, and from mid to the end (&mut slice[..mid], &mut slice[(len - mid)..]) } ``` -If you try to compile this, you'll get an error: +Listing 19-5: An attempted implementation of +`split_at_mut` using only safe Rust + +This function first gets the total length of the slice, then asserts that the +index given as a parameter is within the slice by checking that the parameter +is less than or equal to the length. The assertion means that if we pass an +index that's greater than the length of the slice to split at, the function +will panic before it attempts to use that index. + +Then we return two mutable slices in a tuple: one from the start of the initial +slice to the `mid` index, and another from `mid` to the end of the slice. + +If we try to compile this, we'll get an error: ```text error[E0499]: cannot borrow `*slice` as mutable more than once at a time @@ -211,38 +236,68 @@ error[E0499]: cannot borrow `*slice` as mutable more than once at a time ``` Rust's borrow checker can't understand that we're borrowing different parts of -the slice; it only knows that we're borrowing from the same slice twice. Doing -this is fundamentally okay; our two `&mut [i32]`s aren't overlapping. But Rust -isn't smart enough to know this. When you know something is okay, but Rust -doesn't, it's time to reach for unsafe code. +the slice; it only knows that we're borrowing from the same slice twice. +Borrowing diffreent parts of a slice is fundamentally okay; our two `&mut +[i32]`s aren't overlapping. However, Rust isn't smart enough to know this. When +we know something is okay, but Rust doesn't, it's time to reach for unsafe code. -Here's how to use `unsafe` to make this work: +Listing 19-6 shows how to use an `unsafe` block, a raw pointer, and some calls +to unsafe functions to make the implementation of `split_at_mut` work: - -```rust,ignore +```rust use std::slice; -// in the standard library, this is generic over any T, but we'll use i32 here. fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { + let len = slice.len(); + let ptr = slice.as_mut_ptr(); + + assert!(mid <= len); + unsafe { - let len = slice.len(); - let ptr = slice.as_mut_ptr(); - - assert!(mid <= len); - (slice::from_raw_parts_mut(ptr, mid), slice::from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) } } ``` -Remember how slices are a pointer to some data, and then the length of the -slice? You can get these bits with the `len` and `as_mut_ptr` methods. -`as_mut_ptr` returns a raw pointer, an `*mut i32` in this case. Then, -the `slice::from_raw_pts_mut` method does the reverse: it takes a raw pointer -and a length, and then conjures up a slice. Because slices are checked, they're -safe, but since `from_raw_parts_mut` takes a raw pointer, it just trusts that -this pointer is valid. For example, this code would _not_ work: +Listing 19-6: Using unsafe code in the implementation of +the `split_at_mut` function + +Recall from Chapter 4 that slices are a pointer to some data and the length of +the slice. We've often used the `len` method to get the length of a slice; we +can use the `as_mut_ptr` method to get access to the raw pointer of a slice. In +this case, since we have a mutable slice to `i32` values, `as_mut_ptr` returns +a raw pointer with the type `*mut i32`, which we've stored in the variable +`ptr`. + +The assertion that the `mid` index is within the slice stays the same. Then, +the `slice::from_raw_pts_mut` function does the reverse from the `as_mut_ptr` +and `len` methods: it takes a raw pointer and a length and creates a slice. We +call `slice::from_raw_pts_mut` to create a slice that starts from `ptr` and is +`mid` items long. Then we call the `offset` method on `ptr` with `mid` as an +argument to get a raw pointer that starts at `mid`, and we create a slice using +that pointer and the remaining number of items after `mid` as the length. + +Because slices are checked, they're safe to use once we've created them. The +function `slice::from_raw_parts_mut` is an unsafe function because it takes a +raw pointer and trusts that this pointer is valid. The `offset` method on raw +pointers is also unsafe, since it trusts that the location some offset after a +raw pointer is also a valid pointer. We've put an `unsafe` block around our +calls to `slice::from_raw_parts_mut` and `offset` to be allowed to call them, +and we can tell by looking at the code and by adding the assertion that `mid` +must be less than or equal to `len` that all the raw pointers used within the +`unsafe` block will be valid pointers to data within the slice. This is an +acceptable and appropriate use of `unsafe`. + +Note that the resulting `split_at_mut` function is safe: we didn't have to add +the `unsafe` keyword in front of it, and we can call this function from safe +Rust. We've created a safe abstraction to the unsafe code by writing an +implementation of the function that uses `unsafe` code in a safe way by only +creating valid pointers from the data this function has access to. + +In contrast, the use of `slice::from_raw_parts_mut` in Listing 19-7 would *not* +be appropriate. This code takes an arbitrary memory location and creates a +slice ten thousand items long: ```rust use std::slice; @@ -255,97 +310,79 @@ let slice = unsafe { }; ``` -Now you have a ten thousand long slice to a random place in memory. This won't -work. Don't try this at home. +Listing 19-7: Creating a slice from an arbitrary memory +location -But above, since we got our raw pointer from an existing slice, we know this is -safe! So it's fine. We also have a second `unsafe` function hidden in there: -`offset`. The `offset` method on raw pointers takes a number, and then -increments the pointer in memory. We use this function to create the second -slice. +We don't own the memory at this arbitrary location, and there's no guarantee +that the slice this code creates contains valid `i32` values. Attempting to use +`slice` as if it was a valid slice would be undefined behavior. -That's the general idea of unsafe functions, but let's talk about two other -specific cases. - -### `transmute` - -The `transmute` function is an unsafe function, but it should really be known -as the most unsafe function, so unsafe that you shouldn't ever use it. What -does it do? It says "hey, compiler, you know this type? Treat the data as this -other type. Don't think about it, just trust me." So for example, - -```rust -let ptr = &0; - -let other_ptr: usize = unsafe { std::mem::transmute(ptr) }; -``` - -Here, we say "hey Rust! You know how you have a reference? Convert it into a -`usize`. Since a `usize` has the same number of bits as a reference, this works -just fine. - -However, there's almost always a better alternative to transmute. For example, -in this case, we could use `as` to first cast our reference to a raw pointer, -and then use it again to cast as a `usize`: - -```rust -let ptr = &0; - -let other_ptr = ptr as *const i32 as usize; -``` - -This is much safer. - -For more details, see the documentation for `transmute` in the standard -library. - -Or don't, because you shouldn't use `transmute`. Unless you absolutely, -absolutely, absolutely must. - -### `extern fn` +#### `extern` Functions for Calling External Code are Unsafe Sometimes, your Rust code may need to interact with code written in another -language. To do this, Rust has a keyword, `extern`, that facilitates this: +language. To do this, Rust has a keyword, `extern`, that facilitates creating +and using a *Foreign Function Interface* (FFI). Listing 19-8 demonstrates how +to set up an integration with a function named `some_function` defined in an +external library written in a language other tha Rust. Functions declared +within `extern` blocks are always unsafe to call from Rust code: + +Filename: src/main.rs ```rust,ignore -// This function is defined somewhere externally: extern "C" { fn some_function(); } -// This function can be exposed externally: -pub extern "C" fn call_from_c() { - // code goes here -} - fn main() { unsafe { some_function() }; } ``` -As you can see, `extern` can be used in two ways: to refer to a function -defined somewhere else, and to expose a Rust function to be used externally. -The block form is used for the former case, and putting it before the `fn` is -used for the latter case. +Listing 19-8: Declaring and calling an `extern` function +defined in another language -If you're calling an external function, you need to use `unsafe`. The reason is -this: if you're calling into some other language, that language is not Rust, -and so does not follow Rust's safety guarantees. Since Rust can't check that -it's safe, you must. +Within the `extern "C"` block, we list the names and signatures of functions +defined in a library written in another language that we want to be able to +call.`"C"` defines which *application binary interface* (ABI) the external +function uses. The ABI defines how to call the function at the assembly level. +The `"C"` ABI is the most common, and follows the C programming language's ABI. -You'll also notice the `"C"` there; this defines which ABI, or "application -binary interface", your external function is. The ABI defines how to call the -function at the assembly level. The `"C"` ABI is the most common, and follows -the C programming language's ABI. +Calling an external function is always unsafe. If we're calling into some other +language, that language does not enforce Rust's safety guarantees. Since Rust +can't check that the external code is safe, we are responsible for checking the +safety of the external code and indicating we have done so by using an `unsafe` +block to call external functions. -## `static` + -We've gone this entire book without talking about "global variables." Many +##### Calling Rust Functions from Other Languages + +The `extern` keyword is also used for creating an interface that allows other +languages to call Rust functions. Instead of an `extern` block, we can add the +`extern` keyword and specifying the ABI to use just before the `fn` keyword. +The `call_from_c` function in this example would be accessible from C code: + +```rust +pub extern "C" fn call_from_c() { + println!("Just called a Rust function from C!"); +} +``` + +This usage of `extern` does not require `unsafe` + + + +### Accessing or Modifying a Mutable Static Variable + +We've gone this entire book without talking about *global variables*. Many programming languages support them, and so does Rust. However, global variables -can be problematic: if you have two threads, for example, accessing the same -mutable global variable, bad things can happen. +can be problematic: for example, if you have two threads accessing the same +mutable global variable, a data race can happen. -We call global variables "static" in Rust, and they look like this: +Global variables are called *static* in Rust. Listing 19-9 shows an example +declaration and use of a static variable with a string slice as a value: + +Filename: src/main.rs ```rust static HELLO_WORLD: &'static str = "Hello, world!"; @@ -355,67 +392,92 @@ fn main() { } ``` -You'll notice two things about `static`s: their names are in -`SCREAMING_SNAKE_CASE` by convention, and you _must_ declare the type, which is -`&'static str` in this case. Any references stored in a static will have the -`'static` lifetime. +Listing 19-9: Defining and using an immutable static +variable -You can also have mutable statics, but those require `unsafe`: +`static` variables are similar to constants: their names are also in +`SCREAMING_SNAKE_CASE` by convention, and we *must* annotate the variable's +type, which is `&'static str` in this case. Only references with the `'static` +lifetime may be stored in a static variable. Accessing immutable static +variables is safe. Values in a static variable have a fixed address in memory, +and using the value will always access the same data. Constants, on the other +hand, duplicate their data whenever they are used. + +Another way in which static variables are different from constants is that +static variables can be mutable. Both accessing and modifying mutable static +variables is unsafe. Listing 19-10 shows how to declare, access, and modify a +mutable static variable named `COUNTER`: + +Filename: src/main.rs ```rust static mut COUNTER: u32 = 0; -fn main() { - // mutation is unsafe... +fn add_to_count(inc: u32) { unsafe { - COUNTER = COUNTER + 1; + COUNTER += inc; } +} + +fn main() { + add_to_count(3); - // ... but so is access unsafe { println!("COUNTER: {}", COUNTER); } } ``` -Global mutable state is tricky! +Listing 19-10: Reading from or writing to a mutable +static variable is unsafe -## Unsafe Traits +Just like with regular variables, we specify that a static variable should be +mutable using the `mut` keyword. Any time that we read or write from `COUNTER` +has to be within an `unsafe` block. This code compiles and prints `COUNTER: 3` +as we would expect since it's single threaded, but having multiple threads +accessing `COUNTER` would likely result in data races. -Finally, the last feature of `unsafe` is related to traits. We can declare a -trait as `unsafe`: +Mutable data that is globally accessible is difficult to manage and ensure that +there are no data races, which is why Rust considers mutable static variables +to be unsafe. If possible, prefer using the concurrency techniques and +threadsafe smart pointers we discussed in Chapter 16 to have the compiler check +that data accessed from different threads is done safely. + +### Implementing an Unsafe Trait + +Finally, the last action we're only allowed to take within an `unsafe` block is +implementing an unsafe trait. We can declare that a trait is `unsafe` by adding +the `unsafe` keyword before `trait`, and then implementing the trait must be +marked as `unsafe` too, as shown in Listing 19-11: ```rust unsafe trait Foo { // methods go here } -``` - -And then they require the `unsafe` keyword to implement: - -```rust -# unsafe trait Foo { -# // methods go here -# } unsafe impl Foo for i32 { - // methods go here + // method implementations go here } ``` -Like general unsafe functions, an unsafe trait says "hey, there is some sort of -invariant here that the compiler cannot verify. By using `unsafe impl`, you are -promising that you uphold these invariants." +Listing 19-11: Defining and implementing an unsafe +trait -As an example, remember the `Sync` and `Send` traits from Chapter 16? These -marker traits have no methods, and there's no way for the compiler to verify -that, if you try to implement these traits, that they actually have the `Sync` -and `Send` properties. As such, they're `unsafe` traits, and so you need -`unsafe` to implement them. +Like unsafe functions, methods in an unsafe trait have some invariant that the +compiler cannot verify. By using `unsafe impl`, we're promising that we'll +uphold these invariants. -## Summary +As an example, recall the `Sync` and `Send` marker traits from Chapter 16, and +that the compiler implements these automatically if our types are composed +entirely of `Send` and `Sync` types. If we implement a type that contains +something that's not `Send` or `Sync` such as raw pointers, and we want to mark +our type as `Send` or `Sync`, that requires using `unsafe`. Rust can't verify +that our type upholds the guarantees that a type can be safely sent across +threads or accessed from multiple threads, so we need to do those checks +ourselves and indicate as such with `unsafe`. -That's the gist of unsafe! If you want an even more thorough coverage of unsafe -code, check out the Nomicon. - -Let's move on. Time to talk more about lifetimes! +Using `unsafe` to take one of these four actions isn't wrong or frowned upon, +but it is trickier to get `unsafe` code correct since the compiler isn't able +to help uphold memory safety. When you have a reason to use `unsafe` code, +however, it's possible to do so, and having the explicit `unsafe` annotation +makes it easier to track down the source of problems if they occur. From ff186016e8cf2ed5e948263ed6cd444fafb17aaf Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 10 Apr 2017 16:17:18 -0400 Subject: [PATCH 09/30] Spelling --- second-edition/dictionary.txt | 1 + second-edition/src/ch19-03-advanced-traits.md | 24 +++++++++---------- second-edition/src/ch19-04-advanced-types.md | 18 +++++++------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index f07a85390..b86dff4c9 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -82,6 +82,7 @@ doccargo doccratesio doesn DraftPost +DSTs ebooks Edsger else's diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 629fa0d22..13dea789c 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -157,28 +157,28 @@ implement `Add` like this: ```rust use std::ops::Add; -struct Milimeters(u32); +struct Millimeters(u32); struct Meters(u32); -impl Add for Milimeters { - type Output = Milimeters; +impl Add for Millimeters { + type Output = Millimeters; - fn add(self, other: Milimeters) -> Milimeters { - Milimeters(self.0 + other.0) + fn add(self, other: Millimeters) -> Millimeters { + Millimeters(self.0 + other.0) } } -impl Add for Milimeters { - type Output = Milimeters; +impl Add for Millimeters { + type Output = Millimeters; - fn add(self, other: Meters) -> Milimeters { - Milimeters(self.0 + (other.0 * 1000)) + fn add(self, other: Meters) -> Millimeters { + Millimeters(self.0 + (other.0 * 1000)) } } ``` -If we're adding `Milimeters` to other `Milimeters`, we don't need to parameterize -`Add`. If we want to add `Milimeters` to `Meters`, then we need to say `Add` +If we're adding `Millimeters` to other `Millimeters`, we don't need to parameterize +`Add`. If we want to add `Millimeters` to `Meters`, then we need to say `Add` to set the value of the `RHS`. Default type parameters are used in two main ways: @@ -442,7 +442,7 @@ trait `B`. If we could implement `B` for `A` in our code, it would work, but what if someone else _also_ implemented `B` for `A` in their code? Furthermore, what if a new release of `foo` comes out and implements `B` for `A` themselves? These problems are not insurmountable, of course; we could determine some kind -of complex precedent rules to determine which `impl` 'wins' and works. +of complex precedent rules to determine which `impl` 'wins' and works. ## The newtype pattern diff --git a/second-edition/src/ch19-04-advanced-types.md b/second-edition/src/ch19-04-advanced-types.md index a981dee32..fdb600c64 100644 --- a/second-edition/src/ch19-04-advanced-types.md +++ b/second-edition/src/ch19-04-advanced-types.md @@ -26,7 +26,7 @@ println!("x + y = {}", x + y); Since `Foo` is an alias for `i32`, they're the same type, and we can add them together. If you want a distinct type for `Foo`, you'd use the newtype pattern from Chapter XX. -The main use-case for type synonyms is to reduce repitition. For example, you may have +The main use-case for type synonyms is to reduce repetition. For example, you may have a type like this: ```rust,ignore @@ -147,7 +147,7 @@ As you may have guessed, `continue` has a value of `!`. That is, when Rust goes compute the type of `guess`, it looks at both of the match arms. The former has a value of `u32`, and the latter has a value of `!`. Since `!` can never have a value, Rust is okay with this, and decides that the type of `guess` is `u32`. The fancy way -of saying this is that "never unifies with all other types". This works becuase +of saying this is that "never unifies with all other types". This works because `continue` doesn't actually return a value; it instead moves control back to the top of the loop. In the `Err` case, we never actually assign a value to `guess`. So this is fine. @@ -216,13 +216,13 @@ we said this about `&str`: > elements that it refers to. So while a `&T` is a single value, storing the memory address of where the `T` -is located, a `&str` is _two_ values: the address of the `str`, and how long -it is. As such, a `&str` has a size we can know at compile time: it's two -`usizes` in length. That is, we always know the size of a `&str`, no matter -how long the string it refers to is. This is the general way in which dynamically -sized types are used in Rust; they have an extra bit of metadata that stores -the dynamic information. This leads us to the golden rule of dynamically sized -types: +is located, a `&str` is _two_ values: the address of the `str`, and how long it +is. As such, a `&str` has a size we can know at compile time: it's two times +the size of a `usize` in length. That is, we always know the size of a `&str`, +no matter how long the string it refers to is. This is the general way in which +dynamically sized types are used in Rust; they have an extra bit of metadata +that stores the dynamic information. This leads us to the golden rule of +dynamically sized types: You must always put values of dynamically sized types behind a pointer of some kind. From 514b4058cf44f5b77c83c8034cfed5bbb92eb915 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 10 Apr 2017 17:23:14 -0400 Subject: [PATCH 10/30] More spelling :( --- second-edition/src/ch19-01-unsafe-rust.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/second-edition/src/ch19-01-unsafe-rust.md b/second-edition/src/ch19-01-unsafe-rust.md index c715fd296..61e059b5f 100644 --- a/second-edition/src/ch19-01-unsafe-rust.md +++ b/second-edition/src/ch19-01-unsafe-rust.md @@ -59,7 +59,7 @@ module, any of the code in that module is suspect: keep `unsafe` blocks small and you'll thank yourself later. In order to isolate unsafe code as much as possible, it's a good idea to -enclose unsafe code within a safe abstration and provide a safe API. Parts of +enclose unsafe code within a safe abstraction and provide a safe API. Parts of the standard library are implemented as safe abstractions over unsafe code that has been audited. This prevents uses of `unsafe` from leaking out into all the places that you or your users might want to make use of the functionality @@ -237,7 +237,7 @@ error[E0499]: cannot borrow `*slice` as mutable more than once at a time Rust's borrow checker can't understand that we're borrowing different parts of the slice; it only knows that we're borrowing from the same slice twice. -Borrowing diffreent parts of a slice is fundamentally okay; our two `&mut +Borrowing different parts of a slice is fundamentally okay; our two `&mut [i32]`s aren't overlapping. However, Rust isn't smart enough to know this. When we know something is okay, but Rust doesn't, it's time to reach for unsafe code. From 9a04e864406daa090cbebcddc32de52d7355d90e Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Mon, 10 Apr 2017 21:38:24 -0400 Subject: [PATCH 11/30] Edits to the lifetime subtyping section --- .../src/ch19-02-advanced-lifetimes.md | 365 ++++++++---------- 1 file changed, 152 insertions(+), 213 deletions(-) diff --git a/second-edition/src/ch19-02-advanced-lifetimes.md b/second-edition/src/ch19-02-advanced-lifetimes.md index ffd9ec280..69b1df6ed 100644 --- a/second-edition/src/ch19-02-advanced-lifetimes.md +++ b/second-edition/src/ch19-02-advanced-lifetimes.md @@ -1,28 +1,20 @@ -# Advanced Lifetimes +## Advanced Lifetimes -Back in Chapter 10, we learned how you can help Rust understand your references -with the 'lifetime' syntax. As a quick recap, most of the time, Rust will let -you elide lifetimes, but every reference has one. If you need to be explicit, -they look like this: +Back in Chapter 10, we learned how to annotate references with lifetime +parameters to help Rust understand how the lifetimes of different references +relate. We saw how most of the time, Rust will let you elide lifetimes, but +every reference has a lifetime. There are three advanced features of lifetimes +that we haven't covered though: *lifetime subtyping*, *trait object lifetimes*, +and *higher ranked trait bounds*. -```rust -fn explicit_lifetime<'a>(a: &'a i32, b: &'a i32) -> &'a i32 { -# a -# } -``` +### Lifetime subtyping -There are three more features of lifetimes that we haven't learned yet, though: -*lifetime subtyping*, *trait object lifetimes*, and *higher ranked trait -bounds*. - -## Lifetime subtyping - -Imagine that we want to write a parser. To do this, we'll have a structure -with the string that we're parsing, a 'context'. We'll write individual parsers -that parse this string, and return success or failure. The parsers will need to -borrow the context to do the parsing. We'd end up with something like the -following. We've left off the lifetime annotations for now; this code won't -compile: +Imagine that we want to write a parser. To do this, we'll have a structure that +holds a reference to the string that we're parsing, and we'll call that struct +`Context`. We'll write a parser that will parse this string and return success +or failure. The parser will need to borrow the context to do the parsing. +Implementing this would look like the code in Listing 19-12, which won't +compile because we've left off the lifetime annotations for now: ```rust,ignore struct Context(&str); @@ -33,31 +25,29 @@ struct Parser { impl Parser { fn parse(&self) -> Result<(), &str> { - // do the parsing + Err(&self.context.0[1..]) } } ``` -For simplicity's sake, our `parse` function returns a `Result<(), &str>`, that -is, we don't do anything on success, and the failure is the part of our string -that didn't parse correctly. A real implementation would have more error -information than that, and would actually do something on success, but we're -since this isn't relevant to our example, we're leaving that stuff off. +Listing 19-12: Defining a `Context` struct that holds a +string slice, a `Parser` struct that holds a reference to a `Context` instance, +and a `parse` method that always returns an error referencing the string +slice -Okay, so, how do we fill in the lifetimes? The most straightforward thing to do -is to use the same lifetime everywhere: +For simplicity's sake, our `parse` function returns a `Result<(), &str>`. That +is, we don't do anything on success, and on failure we return the part of the +string slice that didn't parse correctly. A real implementation would have more +error information than that, and would actually return something created when +parsing succeeds, but we're leaving those parts of the implementation off since +they aren't relevant to the lifetimes part of this example. We're also defining +`parse` to always produce an error after the first byte. Note that this may +panic if the first byte is not on a valid character boundary; again, we're +simplifying the example in order to concentrate on the lifetimes involved. -```rust,ignore -struct Context<'a>(&'a str); - -struct Parser<'a> { - context: &'a Context<'a>, -} -``` - -As is, this compiles. Let's implement our `parse` method now. Let's say that -we always produce an error, and the error happened after the first character. -Like this: +So how do we fill in the lifetime parameters for the string slice in `Context` +and the reference to the `Context` in `Parser`? The most straightforward thing +to do is to use the same lifetime everywhere, as shown in Listing 19-13: ```rust struct Context<'a>(&'a str); @@ -68,35 +58,29 @@ struct Parser<'a> { impl<'a> Parser<'a> { fn parse(&self) -> Result<(), &str> { - // a real implementation would do a lot more, of course... Err(&self.context.0[1..]) } } ``` -So far, so good. Next, let's write a function that takes a context, and then -uses a `Parser` to parse that context. This won't quite work... +Listing 19-13: Annotating all references in `Context` and +`Parser` with the same lifetime parameter + +This compiles fine. Next, in Listing 19-14, let's write a function that takes +an instance of `Context`, uses a `Parser` to parse that context, and returns +what `parse` returns. This won't quite work: ```rust,ignore -struct Context<'a>(&'a str); - -struct Parser<'a> { - context: &'a Context<'a>, -} - -impl<'a> Parser<'a> { - fn parse(&self) -> Result<(), &str> { - // a real implementation would do a lot more, of course... - Err(&self.context.0[1..]) - } -} - fn parse_context(context: Context) -> Result<(), &str> { Parser { context: &context }.parse() } ``` -We get quite the error message: +Listing 19-14: An attempt to add a `parse_context` +function that takes a `Context` and uses a `Parser` + +We get two quite verbose errors when we try to compile the code with the +addition of the `parse_context` function: ```text error: borrowed value does not live long enough @@ -107,7 +91,8 @@ error: borrowed value does not live long enough 17 | } | - temporary value only lives until here | -note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:55... +note: borrowed value must be valid for the anonymous lifetime #1 defined on the +body at 15:55... --> :15:56 | 15 | fn parse_context(context: Context) -> Result<(), &str> { @@ -124,7 +109,8 @@ error: `context` does not live long enough 17 | } | - borrowed value only lives until here | -note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:55... +note: borrowed value must be valid for the anonymous lifetime #1 defined on the +body at 15:55... --> :15:56 | 15 | fn parse_context(context: Context) -> Result<(), &str> { @@ -134,106 +120,82 @@ note: borrowed value must be valid for the anonymous lifetime #1 defined on the | |_^ ...ending here ``` -Let's break this error down: +These errors are saying that both the `Parser` instance we're creating and the +`context` parameter live from the line that the `Parser` is created until the +end of the `parse_context` function, but they both need to live for the entire +lifetime of the function. -```text -error: borrowed value does not live long enough - --> :16:5 - | -16 | Parser { context: &context }.parse() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough -17 | } - | - temporary value only lives until here - | -``` +In other words, `Parser` and `context` need to *outlive* the entire function +and be valid before the function starts as well as after it ends in order for +all the references in this code to always be valid. Both the `Parser` we're +creating and the `context` parameter go out of scope at the end of the +function, though (since `parse_context` takes ownership of `context`). -Fundamentally, the issue is that our `Parser` is temporary, and it needs to -live for longer than that. But why? We use it to calculate the result, but -there's no other reason for it to stick around. - -For that, we need to look at the next part of the message: - -```text -note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:55... - --> :15:56 - | -15 | fn parse_context(context: Context) -> Result<(), &str> { - | ________________________________________________________^ starting here... -16 | | Parser { context: &context }.parse() -17 | | } - | |_^ ...ending here -``` - -Ah! So, Rust expects that it needs to live for the entire function, but it -doesn't; it only lives for this one line. Why? Let's keep looking at the -message. - -```text -error: `context` does not live long enough - --> :16:24 - | -16 | Parser { context: &context }.parse() - | ^^^^^^^ does not live long enough -17 | } - | - borrowed value only lives until here - | -note: borrowed value must be valid for the anonymous lifetime #1 defined on the body at 15:55... - --> :15:56 - | -15 | fn parse_context(context: Context) -> Result<(), &str> { - | ________________________________________________________^ starting here... -16 | | Parser { context: &context }.parse() -17 | | } - | |_^ ...ending here -``` - -This is the same thing, but for `context` rather than for `Parser`. Rust -expects them to live longer... let's look at their definitions again: - -```rust -struct Context<'a>(&'a str); - -struct Parser<'a> { - context: &'a Context<'a>, -} -``` - -Ah, right. We said `&'a Context<'a>`, that is, the `Context` has a lifetime -that's the same as the reference to it. That's fine, but... +Let's look at the definitions in Listing 19-13 again, especially the signature +of the `parse` method: ```rust,ignore fn parse(&self) -> Result<(), &str> { ``` -Remember the elision rules? This is the same as +Remember the elision rules? If we annotate the lifetimes of the references, the +signature would be: ```rust,ignore fn parse<'a>(&'a self) -> Result<(), &'a str> { ``` -That is, the error part of `parse`'s return value is tied to the parser. That -makes sense, as it's a pointer to the `Context that it holds. So that's the -problem, in `parse_context`, we return this result from `parse`, which is tied -to the lifetime of the `Parser`. But the `Parser` won't live past the end of -the function; it's temporary. Hence the lifetime issue. +That is, the error part of the return value of `parse` has a lifetime that is +tied to the `Parser` instance's lifetime (that of `&self` in the `parse` method +signature). That makes sense, as the returned string slice references the +string slice in the `Context` instance that the `Parser` holds, and we've +specified in the definition of the `Parser` struct that the lifetime of the +reference to `Context` that `Parser` holds and the lifetime of the string slice +that `Context` holds should be the same. -However, this is safe: we know that the only reason that the result is tied to -the `Parser` is because it's referencing the `Parser`'s `Context`, so it's -_really_ the `Context` that we care about. We need a way to tell Rust that the -`Context` and the `Parser may have different lifetimes. +The problem is that the `parse_context` function returns the value returned +from `parse`, so the lifetime of the return value of `parse_context` is tied to +the lifetime of the `Parser` as well. But the `Parser` instance created in the +`parse_context` function won't live past the end of the function (it's +temporary), and the `context` will go out of scope at the end of the function +(`parse_context` takes ownership of it). -We could try that like this, but it doesn't quite work: +We're not allowed to return a reference to a value that goes out of scope at +the end of the function. Rust thinks that's what we're trying to do because we +annotated all the lifetimes with the same lifetime parameter. That told Rust +the lifetime of the string slice that `Context` holds is the same as that of +the lifetime of the reference to `Context` that `Parser` holds. + +The `parse_context` function can't see that within the `parse` function, the +string slice returned will outlive both `Context` and `Parser`, and that the +reference `parse_context` returns refers to the string slice, not to `Context` +or `Parser`. + +By knowing what the implementation of `parse` does, we know that the only +reason that the return value of `parse` is tied to the `Parser` is because it's +referencing the `Parser`'s `Context`, which is referencing the string slice, so +it's really the lifetime of the string slice that `parse_context` needs to care +about. We need a way to tell Rust that the string slice in `Context` and the +reference to the `Context` in `Parser` have different lifetimes and that the +return value of `parse_context` is tied to the lifetime of the string slice in +`Context`. + +We could try only giving `Parser` and `Context` different lifetime parameters +as shown in Listing 19-15. We've chosen the lifetime parameter names `'s` and +`'c` here to be clearer about which lifetime goes with the string slice in +`Context` and which goes with the reference to `Context` in `Parser`. Note that +this won't completely fix the problem, but it's a start and we'll look at why +this isn't sufficient when we try to compile. ```rust,ignore -struct Context<'a>(&'a str); +struct Context<'s>(&'s str); -struct Parser<'a, 'b> { - context: &'a Context<'b>, +struct Parser<'c, 's> { + context: &'c Context<'s>, } -impl<'a, 'b> Parser<'a, 'b> { - fn parse(&self) -> Result<(), &str> { - // a real implementation would do a lot more, of course... +impl<'c, 's> Parser<'c, 's> { + fn parse(&self) -> Result<(), &'s str> { Err(&self.context.0[1..]) } } @@ -243,99 +205,76 @@ fn parse_context(context: Context) -> Result<(), &str> { } ``` -Here's the error: +Listing 19-15: Specifying different lifetime parameters +for the references to the string slice and to `Context` + +We've annotated the lifetimes of the references in all the same places that we +annotated them in Listing 19-13, but used different parameters depending on +whether the reference goes with the string slice or with `Context`. We've also +added an annotation to the string slice part of the return value of `parse` to +indicate that it goes with the lifetime of the string slice in `Context`. + +Here's the error we get now: ```text -error[E0491]: in type `&'a main::Context<'b>`, reference has a longer lifetime than the data it references - --> :5:5 +error[E0491]: in type `&'c Context<'s>`, reference has a longer lifetime than the data it references + --> src/main.rs:4:5 | -5 | context: &'a Context<'b>, +4 | context: &'c Context<'s>, | ^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the pointer is valid for the lifetime 'a as defined on the struct at 4:0 - --> :4:1 +note: the pointer is valid for the lifetime 'c as defined on the struct at 3:0 + --> src/main.rs:3:1 | -4 | struct Parser<'a, 'b> { +3 | struct Parser<'c, 's> { | _^ starting here... -5 | | context: &'a Context<'b>, -6 | | } +4 | | context: &'c Context<'s>, +5 | | } | |_^ ...ending here -note: but the referenced data is only valid for the lifetime 'b as defined on the struct at 4:0 - --> :4:1 +note: but the referenced data is only valid for the lifetime 's as defined on the struct at 3:0 + --> src/main.rs:3:1 | -4 | struct Parser<'a, 'b> { +3 | struct Parser<'c, 's> { | _^ starting here... -5 | | context: &'a Context<'b>, -6 | | } +4 | | context: &'c Context<'s>, +5 | | } | |_^ ...ending here -help: consider using an explicit lifetime parameter as shown: fn main() - --> :1:1 - | -1 | fn main() { - | ^ ``` -Rust doesn't know of any relationship between `'b` and `'a`, so now that we've -said `&'a Context<'b>`, `'b` needs to _outlive_ `'a`, or else, we'd be pointing -to invalid state. +Rust doesn't know of any relationship between `'c` and `'s`. In order to be +valid, the referenced data in `Context` with lifetime `'s` needs to be +constrained to guarantee that it lives longer than the reference to `Context` +that has lifetime `'c`. If `'s` is not longer than `'c`, then the reference to +`Context` might not be valid. -This is the feature we're talking about in this section. That was a very -long-winded example, but like we said at the start of this chapter, the tools -here are fairly niche. :) We need to be able to say "hey Rust: `'b` will live -at least as long as `'a`." And we have some simple syntax for that: `'b: 'a`. +Which gets us to the point of this section: Rust has a feature called *lifetime +subtyping*, which is a way to specify that one lifetime parameter lives at +least as long as another one. In the angle brackets where we declare lifetime +parameters, we can declare a lifetime `'a` as usual, and declare a lifetime +`'b` that lives at least as long as `'a` by declaring `'b` with the syntax `'b: +'a`. -If we add that to our definition for `Parser`... +In our definition of `Parser`, in order to say that `'s` (the lifetime of the +string slice) is guaranteed to live at least as long as `'c` (the lifetime of +the reference to `Context`), we change the lifetime declarations to look like +this: ```rust -struct Context<'a>(&'a str); - -struct Parser<'a, 'b: 'a> { - context: &'a Context<'b>, +# struct Context<'a>(&'a str); +# +struct Parser<'c, 's: 'c> { + context: &'c Context<'s>, } ``` -Now, the `Parser`'s `Context` and the reference to it have different -lifetimes, and we've ensured that it's longer than the reference to it. +Now, the reference to `Context` in the `Parser` and the reference to the string +slice in the `Context` have different lifetimes, and we've ensured that the +lifetime of the string slice is longer than the reference to the `Context`. -We also need to adjust the `impl` block to take both lifetimes... - -```rust,ignore -impl<'a, 'b> Parser<'a, 'b> { -``` - -... and then, the signature of `parse` needs to make use of `'b`, to show that -the result comes from the `Context`: - -```rust,ignore - fn parse(&self) -> Result<(), &'b str> { -``` - -After those minor changes, it will work! Here's the full code: - - -```rust -struct Context<'a>(&'a str); - -struct Parser<'a, 'b: 'a> { - context: &'a Context<'b>, -} - -impl<'a, 'b> Parser<'a, 'b> { - fn parse(&self) -> Result<(), &'b str> { - // a real implementation would do a lot more, of course... - Err(&self.context.0[1..]) - } -} - -fn parse_context<'a>(context: Context<'a>) -> Result<(), &'a str> { - Parser { context: &context }.parse() -} -``` - -As a recap: `'b: 'a` says that "the lifetime b will live at least as long as -the lifetime a." You don't often need this syntax, but it can come up in -situations like this one, where you need to refer to something you have a -reference to that also has lifetimes. +That was a very long-winded example, but as we mentioned at the start of this +chapter, these features are pretty niche. You won't often need this syntax, but +it can come up in situations like this one, where you need to refer to +something you have a reference to. ## Lifetime bounds From 1bc81b0d7cfc8d948ee50ea3cd050d8b9f111318 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 11 Apr 2017 10:34:18 -0400 Subject: [PATCH 12/30] =?UTF-8?q?Address=20some=20of=20ariel's=20comments?= =?UTF-8?q?=20=E2=9D=A4=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- second-edition/src/ch19-01-unsafe-rust.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/second-edition/src/ch19-01-unsafe-rust.md b/second-edition/src/ch19-01-unsafe-rust.md index 61e059b5f..1501e147a 100644 --- a/second-edition/src/ch19-01-unsafe-rust.md +++ b/second-edition/src/ch19-01-unsafe-rust.md @@ -271,9 +271,9 @@ a raw pointer with the type `*mut i32`, which we've stored in the variable `ptr`. The assertion that the `mid` index is within the slice stays the same. Then, -the `slice::from_raw_pts_mut` function does the reverse from the `as_mut_ptr` +the `slice::from_raw_parts_mut` function does the reverse from the `as_mut_ptr` and `len` methods: it takes a raw pointer and a length and creates a slice. We -call `slice::from_raw_pts_mut` to create a slice that starts from `ptr` and is +call `slice::from_raw_parts_mut` to create a slice that starts from `ptr` and is `mid` items long. Then we call the `offset` method on `ptr` with `mid` as an argument to get a raw pointer that starts at `mid`, and we create a slice using that pointer and the remaining number of items after `mid` as the length. @@ -295,9 +295,9 @@ Rust. We've created a safe abstraction to the unsafe code by writing an implementation of the function that uses `unsafe` code in a safe way by only creating valid pointers from the data this function has access to. -In contrast, the use of `slice::from_raw_parts_mut` in Listing 19-7 would *not* -be appropriate. This code takes an arbitrary memory location and creates a -slice ten thousand items long: +In contrast, the use of `slice::from_raw_parts_mut` in Listing 19-7 would +result in undefined behavior. This code takes an arbitrary memory location and +creates a slice ten thousand items long: ```rust use std::slice; @@ -359,10 +359,13 @@ block to call external functions. The `extern` keyword is also used for creating an interface that allows other languages to call Rust functions. Instead of an `extern` block, we can add the -`extern` keyword and specifying the ABI to use just before the `fn` keyword. -The `call_from_c` function in this example would be accessible from C code: +`extern` keyword and specifying the ABI to use just before the `fn` keyword. We +also add the `#[no_mangle]` annotation to tell the Rust compiler not to mangle +the name of this function. The `call_from_c` function in this example would be +accessible from C code: ```rust +#[no_mangle] pub extern "C" fn call_from_c() { println!("Just called a Rust function from C!"); } From 8fa70999f7871d08a82a4d9f9e718a92bd497c85 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 11 Apr 2017 11:04:21 -0400 Subject: [PATCH 13/30] Edits to the rest of advanced lifetimes, some TODOs --- .../src/ch19-02-advanced-lifetimes.md | 112 +++++++++++------- 1 file changed, 67 insertions(+), 45 deletions(-) diff --git a/second-edition/src/ch19-02-advanced-lifetimes.md b/second-edition/src/ch19-02-advanced-lifetimes.md index 69b1df6ed..1a667e426 100644 --- a/second-edition/src/ch19-02-advanced-lifetimes.md +++ b/second-edition/src/ch19-02-advanced-lifetimes.md @@ -7,7 +7,7 @@ every reference has a lifetime. There are three advanced features of lifetimes that we haven't covered though: *lifetime subtyping*, *trait object lifetimes*, and *higher ranked trait bounds*. -### Lifetime subtyping +### Lifetime Subtyping Imagine that we want to write a parser. To do this, we'll have a structure that holds a reference to the string that we're parsing, and we'll call that struct @@ -276,17 +276,23 @@ chapter, these features are pretty niche. You won't often need this syntax, but it can come up in situations like this one, where you need to refer to something you have a reference to. -## Lifetime bounds +### Lifetime Bounds -We've used traits to bound generic types before, but you can also use lifetimes -for those bounds. For example, let's say we wanted to make a wrapper over -references. Using no bounds at all gives an error: +In Chapter 10, we discussed how to use trait bounds on generic types. We can +also add lifetime parameters as constraints on generic types. For example, +let's say we wanted to make a wrapper over references to any type in order +to... TODO. The struct definition without lifetime parameters would look like +Listing 19-16: ```rust,ignore struct Ref(&T); ``` -Like this: +Listing 19-16: Defining a struct to wrap a reference to a +generic type; without lifetime parameters to start + +However, using no lifetime bounds at all gives an error because Rust doesn't +know how long the generic type `T` will live: ```text error[E0309]: the parameter type `T` may not live long enough @@ -303,45 +309,58 @@ note: ...so that the reference type `&'a T` does not outlive the data it points | ^^^^^^ ``` -Rust helpfully gave us good advice: +This is the same error that we'd get if we filled in `T` with a concrete type, +like `struct Ref(&i32)`; all references in struct definitions need a lifetime +parameter. However, because we have a generic type parameter, we can't add a +lifetime parameter in the same way. Defining `Ref` as `struct Ref<'a>(&'a T)` +will result in an error because Rust can't determine that `T` lives long +enough. Since `T` can be any type, `T` could itself be a reference or it could +be a type that holds one or more references, each of which have their own +lifetimes. -> consider adding an explicit lifetime bound `T: 'a` so that the reference type -> `&'a T` does not outlive the data it points to. +Rust helpfully gave us good advice on how to specify the lifetime parameter in +this case: -This works: +```text +consider adding an explicit lifetime bound `T: 'a` so that the reference type +`&'a T` does not outlive the data it points to. +``` + +The code in Listing 19-17 works because `T: 'a` syntax specifies that `T` can +be any type, but if it contains any references, `T` must live as long as `'a`: ```rust struct Ref<'a, T: 'a>(&'a T); ``` -The `T: 'a` syntax says "T can be any type, but if it contains any references, -it must live as long as `'a`." +Listing 19-17: Adding lifetime bounds on `T` to specify +that any references in `T` live at least as long as `'a` -We could sort of do the reverse with `'static`: +We could choose to solve this in a different way as shown in Listing 19-18 by +bounding `T` on `'static`. This means if `T` contains any references, they must +have the `'static` lifetime: ```rust struct StaticRef(&'static T); ``` -This says "If `T` contains any references, they must be `'static` ones. +Listing 19-18: Adding a `'static` lifetime bound to `T` +to constrain `T` to types that have only `'static` references or no +references -Types with no references inside count as `'static`, and since `'static` is -longer than any other lifetime, a type like `T: 'a` can be a type with no -references. +Types with no references inside count as `'static`. Because `'static` is longer +than any other lifetime, a type like `T: 'a` can only be a type with no +references. TODO CONFUSED -## Lifetimes in trait objects +### Lifetimes in Trait Objects -In chapter 17, we learned about trait objects, like this: - -```rust -trait Foo { } - -impl Foo for i32 { } - -let obj = Box::new(5) as Box; -``` - -However, what if the type implementing our trait has a lifetime? +In Chapter 17, we learned about trait objects that consist of putting a trait +behind a reference in order to use dynamic dispatch. However, we didn't discuss +what happens if the type implementing the trait used in the trait object has a +lifetime. Consider Listing 19-19, where we have a trait `Foo` and a struct +`Bar` that holds a reference (and thus has a lifetime parameter) that +implements trait `Foo`, and we want to use an instance of `Bar` as the trait +object `Box`: ```rust trait Foo { } @@ -357,28 +376,31 @@ let num = 5; let obj = Box::new(Bar { x: &num }) as Box; ``` -This code works. But how? We haven't said anything about the lifetimes of the -object. +Listing 19-19: Using a type that has a lifetime parameter +with a trait object -Well, as it turns out, there are rules. For a trait object like `Box`, -we can add a lifetime bound as well, like `Box`, for example. Just as -with the other bounds, this means "Any implementer of `Foo` which has a -lifetime inside must be `'a`." But we didn't need to explicitly write this. -Here are the rules: +This code compiles without any errors, even though we haven't said anything +about the lifetimes involved in `obj`. This works because there are rules +having to do with lifetimes and trait objects: -* The default begins as 'static. -* If you have `&'a X` or `&'a mut X`, then the default is `'a`. -* If you have a single `T: 'a` clause, then the default is `'a`. -* If you have multiple `T: 'a`-like clauses, then there is no default; you must +* The default lifetime of a trait object is `'static`. +* If we have `&'a X` or `&'a mut X`, then the default is `'a`. +* If we have a single `T: 'a` clause, then the default is `'a`. +* If we have multiple `T: 'a`-like clauses, then there is no default; we must be explicit. -If you need to be explicit, `Box` or `Box` is the way -to do it. +When we must be explicit, we can add a lifetime bound on a trait object like +`Box` with the syntax `Box` or `Box`, depending +on what's needed. Just as with the other bounds, this means that any +implementer of the `Foo` trait that has any references inside must have the +lifetime specified in the trait object bounds as those references. -## Higher ranked trait bounds +### Higher Ranked Trait Bounds -Sometimes, you may write a function which accepts a closure, and that closure -takes a reference as an argument: +TODO: cut this section or work up an example that requires lifetime annotation + +When writing a function that accepts a closure, the closure might take a +reference as a parameter, like so: ```rust fn call_with_ref(some_closure: F) -> i32 From cac7ba957a4826c312cd1f0c6ab82ce0d2c9a5b9 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 11 Apr 2017 12:06:36 -0400 Subject: [PATCH 14/30] Tweak unsafe slice wording --- second-edition/src/ch19-01-unsafe-rust.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/second-edition/src/ch19-01-unsafe-rust.md b/second-edition/src/ch19-01-unsafe-rust.md index 1501e147a..9d69a4c97 100644 --- a/second-edition/src/ch19-01-unsafe-rust.md +++ b/second-edition/src/ch19-01-unsafe-rust.md @@ -296,8 +296,8 @@ implementation of the function that uses `unsafe` code in a safe way by only creating valid pointers from the data this function has access to. In contrast, the use of `slice::from_raw_parts_mut` in Listing 19-7 would -result in undefined behavior. This code takes an arbitrary memory location and -creates a slice ten thousand items long: +likely crash when the slice is used. This code takes an arbitrary memory +location and creates a slice ten thousand items long: ```rust use std::slice; From c8a327eca49d0e0ac698455a08020605cb8acc6c Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 11 Apr 2017 14:02:56 -0400 Subject: [PATCH 15/30] Edit associated types --- second-edition/src/ch13-02-iterators.md | 2 +- second-edition/src/ch19-03-advanced-traits.md | 206 +++++++++++++----- 2 files changed, 155 insertions(+), 53 deletions(-) diff --git a/second-edition/src/ch13-02-iterators.md b/second-edition/src/ch13-02-iterators.md index 1aa816ddc..a38844500 100644 --- a/second-edition/src/ch13-02-iterators.md +++ b/second-edition/src/ch13-02-iterators.md @@ -88,7 +88,7 @@ trait Iterator { There's some new syntax that we haven't covered here yet: `type Item` and `Self::Item` are defining an *associated type* with this trait, and we'll talk -about associated types in depth in Chapter XX. For now, all you need to know is +about associated types in depth in Chapter 19. For now, all you need to know is that this code says the `Iterator` trait requires that you also define an `Item` type, and this `Item` type is used in the return type of the `next` method. In other words, the `Item` type will be the type of element that's diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 13dea789c..c819ae2fe 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -1,67 +1,89 @@ -# Advanced Traits +## Advanced Traits We covered traits in Chapter 10, but like lifetimes, we didn't get to all the details. Now that we know more Rust, we can get into the nitty-gritty. -## Associated Types +### Associated Types + +*Associated types* are a way of associating a type placeholder with a trait +such that the trait method definitions can use these placeholder types in their +signatures. The implementer of a trait will specify the concrete type to be +used in this type's place for the particular implementation. We've described most of the things in this chapter as being very rare. Associated types are somewhere in the middle; they're more rare than the rest of the book, but more common than many of the things in this chapter. -Associated types look like this: +An example of a trait with an associated type is the `Iterator` trait provided +by the standard library. It has an associated type named `Item` that stands in +for the type of the values that we're iterating over. We mentioned in Chapter +13 that the definition of the `Iterator` trait is as shown in Listing 19-20: ```rust -trait Foo { - type Bar; - - fn foo(&self) -> Self::Bar; -} - -impl Foo for i32 { - type Bar = String; - - fn foo(&self) -> Self::Bar { - self.to_string() - } +pub trait Iterator { + type Item; + fn next(&mut self) -> Option; } ``` -The trait `Foo` has an associated type called `Bar`. We can then use -`Self::Bar` elsewhere in our trait definition to use that type. +Listing 19-20: The definition of the `Iterator` trait +that has an associated type `Item` -This _feels_ like more generics. For example, this seems similar to -the following code: +This says that the `Iterator` trait has an associated type named `Item`. `Item` +is a placeholder type, and the return value of the `next` method will return +values of type `Option`. Implementers of this trait will specify +the concrete type for `Item`, and the `next` method will return an `Option` +containing a value of whatever type the implementer has specified. + +#### Associated Types Versus Generics + +When we implemented the `Iterator` trait on the `Counter` struct in Listing +13-6, we specified that the `Item` type was `u32`: + +```rust,ignore +impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option { +``` + +This feels similar to generics. So why isn't the `Iterator` trait defined as +shown in Listing 19-21? ```rust -trait Foo { - fn foo(&self) -> Bar; -} - -impl Foo for i32 { - fn foo(&self) -> String { - self.to_string() - } +pub trait Iterator { + fn next(&mut self) -> Option; } ``` -But there's one big difference: with the second definition, we could also -implement `Foo for i32`, or anything else. In other words, with a trait -that has a generic parameter, we can implement that trait for a type multiple -times, changing the parameters each time. But with associated types, we can't; -we can only define it one time: it's not actually generic. +Listing 19-21: A hypothetical definition of the +`Iterator` trait using generics -There's another benefit to associated types: when using the trait, since there's -only one possible implementation, you end up with a lot less syntax. This is -easier with some code: +The difference is that with the definition in Listing 19-21, we could also +implement `Iterator for Counter`, or any other type as well, so that +we'd have multiple implementations of `Iterator` for `Counter`. In other words, +when a trait has a generic parameter, we can implement that trait for a type +multiple times, changing the generic type parameters' concrete types each time. +Then when we use the `next` method on `Counter`, we'd have to provide type +annotations to indicate which implementation of `Iterator` we wanted to use. + +With associated types, we can't implement a trait on a type multiple times. +Using the actual definition of `Iterator` from Listing 19-20, we can only +choose once what the type of `Item` will be, since there can only be one `impl +Iterator for Counter`. We don't have to specify that we want an iterator of +`u32` values everywhere that we call `next` on `Counter`. + +The benefit of not having to specify generic type parameters when a trait uses +associated types shows up in another way as well. Consider the two traits +defined in Listing 19-22. Both are defining a trait having to do with a graph +structure that contains nodes of some type and edges of some type. `GGraph` is +defined using generics, and `AGraph` is defined using associated types: ```rust -// a generic graph trait GGraph { // methods would go here } -// an associated graph trait AGraph { type Node; type Edge; @@ -70,24 +92,104 @@ trait AGraph { } ``` -Let's say we wanted to compute the distance between two nodes in the graph. -With the generic graph, you'd have to write this: +Listing 19-22: Two graph trait definitions, `GGraph` +using generics and `AGraph` using associated types for `Node` and `Edge` -```rust,ignore -fn distance>(graph: &G, start: &N, end: &N) -> u32 { ... } +Let's say we wanted to implement a function that computes the distance between +two nodes in any types that implement the graph trait. With the `GGraph` trait +defined using generics, our `distance` function signature would have to look +like Listing 19-23: + +```rust +# trait GGraph {} +# +fn distance>(graph: &G, start: &N, end: &N) -> u32 { +# 0 +} ``` -Even though `distance` doesn't need to know the types of the edges, we're -forced to declare an `E` parameter, because we need to to use `Graph`. But with -the associated type version: +Listing 19-23: The signature of a `distance` function +that uses the trait `GGraph` and has to specify all the generic +parameters -```rust,ignore -fn distance(graph: &G, start: &G::Node, end: &G::Node) -> u32 { ... } +Our function would need to specify the generic type parameters `N`, `E`, and +`G`, where `G` is bound by the trait `GGraph` that has type `N` as its `Node` +type and type `E` as its `Edge` type. Even though `distance` doesn't need to +know the types of the edges, we're forced to declare an `E` parameter, because +we need to to use the `GGraph` trait and that requires specifying the type for +`Edge`. + +Contrast with the definition of `distance` in Listing 19-24 that uses the +`AGraph` trait from Listing 19-22 with associated types: + +```rust +# trait AGraph { +# type Node; +# type Edge; +# } +# +fn distance(graph: &G, start: &G::Node, end: &G::Node) -> u32 { +# 0 +} ``` -This is much cleaner. +Listing 19-24: The signature of a `distance` function +that uses the trait `AGraph` and the associated type `Node` -## Operator overloading and default type parameters +This is much cleaner. We only need to have one generic type parameter, `G`, +with the trait bound `AGraph`. Since `distance` doesn't use the `Edge` type at +all, it doesn't need to be specified anywhere. To use the `Node` type +associated with `AGraph`, we can specify `G::Node`. + +#### Trait Objects with Associated Types + +You may have been wondering why we didn't use a trait object in the `distance` +functions in Listing 19-23 and Listing 19-24. The signature for the `distance` +function using the generic `GGraph` trait does get a bit more concise using a +trait object: + +```rust +# trait GGraph {} +# +fn distance(graph: &GGraph, start: &N, end: &N) -> u32 { +# 0 +} +``` + +This might be a more fair comparison to Listing 19-24. Specifying the `Edge` +type is still required, though, which means Listing 19-24 is still preferable +since we don't have to specify something we don't use. + +It's not possible to change Listing 19-24 to use a trait object for the graph, +since then there would be no way to refer to the `AGraph` trait's associated +type. + +It is possible in general to use trait objects of traits that have associated +types, though; Listing 19-25 shows a function named `traverse` that doesn't +need to use the trait's associated types in other arguments. We do, however, +have to specify the concrete types for the associated types in this case. Here, +we've chosen to accept types that implement the `AGraph` trait with the +concrete type of `usize` as their `Node` type and a tuple of two `usize` values +for their `Edge` type: + +```rust +# trait AGraph { +# type Node; +# type Edge; +# } +# +fn traverse(graph: &AGraph) {} +``` + +While trait objects mean that we don't need to know the concrete type of the +`graph` parameter at compile time, we do need to constrain the use of the +`AGraph` trait in the `traverse` function by the concrete types of the +associated types. If we didn’t provide this constraint, Rust wouldn't be able +to figure out which `impl` to match this trait object to. + +TODO: I basically copied the last sentence from the old book but i dont really understand it /Carol + +### Operator overloading and default type parameters We can use traits in Rust to overload certain operators. Rust does not allow you to create your own operators, or overload arbitrary operators: only the operations listed @@ -195,7 +297,7 @@ because our existing users won't have written down a type parameter, if we want to add a type parameter to an existing trait, giving it a default will let us not break that code. -## Fully qualified syntax +### Fully qualified syntax Sometimes, methods can have the same names. Consider this code: @@ -340,7 +442,7 @@ fn main() { Using this syntax lets you call the trait method instead of the inherent one. -## Super traits +### Super traits Sometimes, you may want a trait to be able to rely on another trait existing. For example, let's say that you have a `Foo` trait and a `Bar` trait, but you @@ -393,7 +495,7 @@ trait Bar: Foo { This works fine. -## Coherence +### Coherence Finally, traits have a concept called 'coherence'. This governs exactly who is allowed to implement a trait. In short: @@ -444,7 +546,7 @@ what if a new release of `foo` comes out and implements `B` for `A` themselves? These problems are not insurmountable, of course; we could determine some kind of complex precedent rules to determine which `impl` 'wins' and works. -## The newtype pattern +### The newtype pattern There is a way to get around this, though. We call it the 'newtype pattern'. You create a new type that's a thin wrapper around the type you want to From b01d6bd84ebb7437584be007cecd26a81b750a48 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 11 Apr 2017 14:15:20 -0400 Subject: [PATCH 16/30] Cut HRTB --- .../src/ch19-02-advanced-lifetimes.md | 65 +------------------ 1 file changed, 1 insertion(+), 64 deletions(-) diff --git a/second-edition/src/ch19-02-advanced-lifetimes.md b/second-edition/src/ch19-02-advanced-lifetimes.md index 1a667e426..a47bece8b 100644 --- a/second-edition/src/ch19-02-advanced-lifetimes.md +++ b/second-edition/src/ch19-02-advanced-lifetimes.md @@ -395,67 +395,4 @@ on what's needed. Just as with the other bounds, this means that any implementer of the `Foo` trait that has any references inside must have the lifetime specified in the trait object bounds as those references. -### Higher Ranked Trait Bounds - -TODO: cut this section or work up an example that requires lifetime annotation - -When writing a function that accepts a closure, the closure might take a -reference as a parameter, like so: - -```rust -fn call_with_ref(some_closure: F) -> i32 - where F: Fn(&i32) -> i32 { - - let value = 0; - - some_closure(&value) -} -``` - -This code compiles just fine, but what about the lifetime here? With the -elision rules, we don't actually *need* to write out the lifetime, but what if -we did? - -You might think that you'd write it something like this: - -```rust,ignore -fn call_with_ref<'a, F>(some_closure:F) -> i32 - where F: Fn(&'a i32) -> i32 { -# -# let value = 0; -# -# some_closure(&value) -# } -``` - -This will not compile. Because our trait is generic, yet it also *contains* a -generic lifetime, we need a way to say that our generic is generic. In general, -these kinds of "generic of generic" issues are referred to with the words -"higher", like "higher kinded type." In this case, it's a "higher rank type." -What that means isn't important, but the implication is that Rust is doing -something special here for us. - -If we wanted to write it out entirely, we'd use this syntax, with `for<>`: - -```rust -fn call_with_ref(some_closure: F) -> i32 - where F: for<'a> Fn(&'a i32) -> i32 { -# -# let value = 0; -# -# some_closure(&value) -# } -``` - -failures: - Advanced_Lifetimes_19 - -test result: FAILED. 11 passed; 1 failed; 9 ignored; 0 measured - - -This says "for any lifetime `'a`." Think of it as similar to how a generic -function says "for any type `T`." - -This comes up extremely rarely in Rust code. It's an explicit goal of one of -the members of the language design team that you should never need to write an -explicit `for<'a>`, but you can if you'd like to. +Next, let's take a look at some other advanced features dealing with traits! From 102cf1c3b65ff2eacecc4a803d7691f9e75daa77 Mon Sep 17 00:00:00 2001 From: steveklabnik Date: Tue, 11 Apr 2017 15:54:40 -0400 Subject: [PATCH 17/30] draft of 19.5 --- ...ch19-05-advanced-functions-and-closures.md | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/second-edition/src/ch19-05-advanced-functions-and-closures.md b/second-edition/src/ch19-05-advanced-functions-and-closures.md index e3995e78a..a0a3f1ad2 100644 --- a/second-edition/src/ch19-05-advanced-functions-and-closures.md +++ b/second-edition/src/ch19-05-advanced-functions-and-closures.md @@ -6,8 +6,108 @@ go over those now. ## Function pointers +We've talked about how to pass closures to functions, but you can pass regular +functions to functions too! Functions have the type `fn()`, with a lower case 'f'. +Don't confuse it with the `Fn()` closure trait! The syntax is similar: + +```rust +fn add_one(x: i32) -> i32 { + x + 1 +} + +fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) + f(arg) +} + +fn main() { + let answer = do_twice(add_one, 5); + + println!("The answer is: {}", answer); +} +``` + +This prints `The answer is: 12`. This `f(i32) -> i32` syntax is called +a 'function pointer', and unlike closures, you don't use it as a trait, +you use it directly, as you can see in the signature of `do_twice`. + +### Point-free style + +Function pointers implement all three of the closure traits: `Fn`, `FnMut`, and +`FnOnce`. So you can always pass a pointer to a function that expects a closure: + +```rust +// fold takes a FnMut closure... but we can use this function too! +fn add(acc: i32, x: &i32) -> i32 { + acc + *x +} + +let v = vec![1, 2, 3]; + +let six = v.iter().fold(0, |acc, &x| acc + x); +let six = v.iter().fold(0, add); +``` + +This is sometimes called 'point-free style', for fairly obscure reasons +that don't matter. This can work for anything where the types line up. +For example: + +```rust +let v = vec![1, 2, 3]; + +let strings: Vec = v.iter().map(|s| s.to_string()).collect(); + +// to_string is provided by the ToString trait +let strings: Vec = v.iter().map(ToString::to_string).collect(); +``` + +Some people prefer this style, some people prefer the closure. They end up +with the same code, so use whichever feels more clear to you. + ## Diverging functions -## Move closures +In the previous section, we talked about the never type, `!`. Functions +that return never are called "diverging functions": + +```rust +fn never_returns() -> ! { + panic!("oh no!"); +} +``` + +For more details, see the previous section. ## Returning closures + +As we discussed before, closures are represented by traits: `Fn`, `FnMut`, and `FnOnce`. +This means that returning them is a little tricky; you can't do it directly. This will +give a compiler error: + +```rust,ignore +fn returns_closure() -> Fn(i32) -> i32 { + |x| x + 1 +} +``` + +It looks like this: + +```text +error[E0277]: the trait bound `std::ops::Fn(i32) -> i32 + 'static: std::marker::Sized` is not satisfied + --> :2:25 + | +2 | fn returns_closure() -> Fn(i32) -> i32 { + | ^^^^^^^^^^^^^^ the trait `std::marker::Sized` is not implemented for `std::ops::Fn(i32) -> i32 + 'static` + | + = note: `std::ops::Fn(i32) -> i32 + 'static` does not have a constant size known at compile-time + = note: the return type of a function must have a statically known size +``` + +What to do? With most things that implement traits, we could return them by naming +the type, but we can't do that with closures. Instead, we need to use a trait object: + +```rust +fn returns_closure() -> Box i32> { + Box::new(|x| x + 1) +} +``` + +For more about trait objects, see Chapter 18. \ No newline at end of file From 8a8c791140f894f02dcb2f925527083d609b3fbc Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Tue, 11 Apr 2017 16:43:21 -0400 Subject: [PATCH 18/30] Edits to definitely-not-UFCS --- second-edition/src/ch19-03-advanced-traits.md | 234 ++++++++---------- 1 file changed, 107 insertions(+), 127 deletions(-) diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index c819ae2fe..9ed31edee 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -189,11 +189,19 @@ to figure out which `impl` to match this trait object to. TODO: I basically copied the last sentence from the old book but i dont really understand it /Carol -### Operator overloading and default type parameters +### Operator Overloading and Default Type Parameters -We can use traits in Rust to overload certain operators. Rust does not allow you to -create your own operators, or overload arbitrary operators: only the operations listed -in `std::ops` can be overloaded. Here's an example: +The `` syntax is used in another way as well: to +specify the default type for a generic type. A great example of a situation +where this is useful is operator overloading. + +Rust does not allow you to create your own operators or overload arbitrary +operators, but the operations listed in `std::ops` can be overloaded by +implementing the traits associated with the operator. For example, Listing +19-25 shows how to overload the `+` operator by implementing the `Add` trait on +a `Point` struct so that we can add two `Point` instances together: + +Filename: src/main.rs ```rust use std::ops::Add; @@ -221,12 +229,15 @@ fn main() { } ``` -The `Add` trait lets us overload the `+` operator. We've implemented it for -a `Point` such that it adds the `x`s and `y`s together to make a new `Point`. -You'll notice that the `Add` trait has an `Output` associated type; this is -used to determine the result of the operation. +Listing 19-25: Implementing the `Add` trait to overload +the `+` operator for `Point` instances -Let's look at `Add` in a bit more detail. Here's its definition: +We've implemented the `add` method to add the `x` values of two `Point` +instances together and the `y` values of two `Point` instances together to +create a new `Point`. The `Add` trait has an `Output` associated type that's +used to determine the type returned from `add`. result of the operation. + +Let's look at the `Add` trait in a bit more detail. Here's its definition: ```rust trait Add { @@ -236,25 +247,18 @@ trait Add { } ``` -This should look familiar; it's a trait with one method and an associated type. But -there's one bit of syntax we haven't seen before: `RHS=Self`. What's up with that? +This should look familiar; it's a trait with one method and an associated type. +The new part is the `RHS=Self` in the angle brackets: this syntax is called +*default type parameters*. `RHS` is a generic type parameter (short for "right +hand side") that's used for the type of the `rhs` parameter in the `add` +method. If we don't specify a concrete type for `RHS` when we implement the +`Add` trait, the type of `RHS` will default to the type of `Self` (the type +that we're implementing `Add` on). -This syntax is called 'default type parameters'. It allows you to say "If a parameter -isn't provided, use this default instead." So in other words, these two trait definitions -are very similar: - -```rust,ignore -trait Add { -trait Add { -``` - -The only difference is, with the first definition, we are required to parameterize -`Add` with a type for `RHS`, which is short for "right hand side." In the latter -form, we aren't required to, and if we do not, the type of `RHS` will be the type -of `Self`. - -Let's look at an example. Imagine we have two units, `Feet` and `Inches`. We can -implement `Add` like this: +Let's look at another example of implementing the `Add` trait. Imagine we have +two structs holding values in different units, `Millimeters` and `Meters`. We +can implement `Add` for `Millimeters` in different ways as shown in Listing +19-26: ```rust use std::ops::Add; @@ -279,27 +283,43 @@ impl Add for Millimeters { } ``` -If we're adding `Millimeters` to other `Millimeters`, we don't need to parameterize -`Add`. If we want to add `Millimeters` to `Meters`, then we need to say `Add` -to set the value of the `RHS`. +Listing 19-26: Implementing the `Add` trait on +`Millimeters` to be able to add `Millimeters` to `Millimeters` and +`Millimeters` to `Meters` + +If we're adding `Millimeters` to other `Millimeters`, we don't need to +parameterize the `RHS` type for `Add` since the default `Self` type is what we +want. If we want to implement adding `Millimeters` and `Meters`, then we need +to say `impl Add` to set the value of the `RHS` type parameter. Default type parameters are used in two main ways: 1. To extend a type without breaking existing code. 2. To allow customization in a way most users don't want. -This is an example of the second purpose; most of the time, you're adding two -like types together. Using the default here makes it easier to do so without -the extra parameter. In other words, we've removed a little bit of boilerplate. +The `Add` trait is an example of the second purpose: most of the time, you're +adding two like types together. Using a default type parameter in the `Add` +trait definition makes it easier to implement the trait since you don't have to +specify the extra parameter most of the time. In other words, we've removed a +little bit of implementation boilerplate. -What about the first case? Well, it's sort of the same thing, but in reverse: -because our existing users won't have written down a type parameter, if we want -to add a type parameter to an existing trait, giving it a default will let us -not break that code. +The first purpose is similar, but in reverse: since existing implementations of +a trait won't have specified a type parameter, if we want to add a type +parameter to an existing trait, giving it a default will let us extend the +functionality of the trait without breaking the existing implementation code. -### Fully qualified syntax +### Fully Qualified Syntax for Disambiguation -Sometimes, methods can have the same names. Consider this code: +Rust cannot prevent a trait from having a method with the same name that +another trait's method has, nor can it prevent us from implementing both of +these traits on one type. We can also have a method implemented directly on the +type with the same name as well! In order to be able to call each of the +methods with the same name, then, we need to tell Rust which one we want to +use. Consider the code in Listing 19-27 where traits `Foo` and `Bar` both have +method `f` and we implement both traits on struct `Baz`, which also has a +method named `f`: + +Filename: src/main.rs ```rust trait Foo { @@ -320,77 +340,48 @@ impl Bar for Baz { fn f(&self) { println!("Baz’s impl of Bar"); } } -let b = Baz; +impl Baz { + fn f(&self) { println!("Baz's impl"); } +} + +fn main() { + let b = Baz; + b.f(); +} ``` -If we were to try to call `b.f()`, we’d get an error: +Listing 19-27: Implementing two traits that both have a +method with the same name as a method defined on the struct directly -```text -error[E0034]: multiple applicable items in scope - --> :21:3 - | -21 | b.f(); - | ^ multiple `f` found - | -note: candidate #1 is defined in an impl of the trait `main::Foo` for the type `main::Baz` - --> :13:5 - | -13 | fn f(&self) { println!("Baz’s impl of Foo"); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in an impl of the trait `main::Bar` for the type `main::Baz` - --> :17:5 - | -17 | fn f(&self) { println!("Baz’s impl of Bar"); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``` +For the implemetation of the `f` method for the `Foo` trait on `Baz`, we're +printing out `Baz's impl of Foo`. For the implementation of the `f` method for +the `Bar` trait on `Baz`, we're printing out `Baz's impl of Bar`. The +implementation of `f` directly on `Baz` prints out `Baz's impl`. What should +happen when we call `b.f()`? In this case, Rust will always use the +implementation on `Baz` directly and will print out `Baz's impl`. -We need a way to disambiguate which method we need. We can do that like this: - -```rust -# trait Foo { -# fn f(&self); -# } -# trait Bar { -# fn f(&self); -# } -# struct Baz; -# impl Foo for Baz { -# fn f(&self) { println!("Baz’s impl of Foo"); } -# } -# impl Bar for Baz { -# fn f(&self) { println!("Baz’s impl of Bar"); } -# } -# let b = Baz; -::f(&b); -::f(&b); -``` - -In other words, we can turn this: +In order to be able to call the `f` method from `Foo` and the `f` method from +`Baz` rather than the implementation of `f` directly on `Baz`, we need to use +the *fully qualified syntax* for calling methods. It works like this: for any +method call like: ```rust,ignore -foo.bar(args); +receiver.method(args); ``` -Into this: - -```rust,ignore -::bar(foo, args); -``` - -In a more generic sense, +We can fully qualify the method call like this: ```rust,ignore ::method(receiver, args); ``` -We only need the `Type as` part if it's ambiguous. And we only need the `<>` -part if we need the `Type as` part. So in some cases, you could write +So in order to disambiguate and be able to call all the `f` methods defined in +Listing 19-27, we specify that we want to treat the type `Baz` as each trait +within angle brackets, then use two colons, then call the `f` method and pass +the instance of `Baz` as the first argument. Listing 19-28 shows how to call +`f` from `Foo` and then `f` from `Bar` on `b`: -```rust,ignore -Trait::method(receiver, args); -``` - -This would have worked above: +Filename: src/main.rs ```rust # trait Foo { @@ -406,41 +397,30 @@ This would have worked above: # impl Bar for Baz { # fn f(&self) { println!("Baz’s impl of Bar"); } # } -# let b = Baz; -Foo::f(&b); -Bar::f(&b); -``` - -Here's an example of where the longer form is needed. We have an inherent -method `foo` and a trait method `foo`: - - -```rust -trait Foo { - fn foo() -> i32; -} - -struct Bar; - -impl Bar { - fn foo() -> i32 { - 20 - } -} - -impl Foo for Bar { - fn foo() -> i32 { - 10 - } -} - fn main() { - assert_eq!(10, ::foo()); - assert_eq!(20, Bar::foo()); + let b = Baz; + b.f(); + ::f(&b); + ::f(&b); } ``` -Using this syntax lets you call the trait method instead of the inherent one. +Listing 19-28: Using fully qualified syntax to call the +`f` methods defined as part of the `Foo` and `Bar` traits + +This will print: + +```text +Baz's impl +Baz’s impl of Foo +Baz’s impl of Bar +``` + +We only need the `Type as` part if it's ambiguous, and we only need the `<>` +part if we need the `Type as` part. So if we only had the `f` method directly +on `Baz` and the `Foo` trait implemented on `Baz` in scope, we could call the +`f` method in `Foo` by using `Foo::f(&b)` since we wouldn't have to +disambiguate from the `Bar` trait. ### Super traits From bd57f2daa27aaec637f397e0e293fa433bd14122 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 12 Apr 2017 09:40:17 -0400 Subject: [PATCH 19/30] Make a slightly more realistic super trait example down with metasyntactic variables! --- second-edition/src/ch19-03-advanced-traits.md | 125 ++++++++++++------ 1 file changed, 86 insertions(+), 39 deletions(-) diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 9ed31edee..57197d427 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -422,58 +422,105 @@ on `Baz` and the `Foo` trait implemented on `Baz` in scope, we could call the `f` method in `Foo` by using `Foo::f(&b)` since we wouldn't have to disambiguate from the `Bar` trait. -### Super traits +### Super Traits -Sometimes, you may want a trait to be able to rely on another trait existing. -For example, let's say that you have a `Foo` trait and a `Bar` trait, but you -want `Bar`'s methods to be able to call `Foo`'s methods. Let's try it. (It -won't work just yet...) +Sometimes, we may want a trait to be able to rely on another trait also being +implemented wherever our trait is implemented, so that our trait can use the +other trait's functionality. The required trait is a *super trait* of the trait +we're implementing. TODO OR IS IT THE OTHER WAY AROUND -```rust,ignore -trait Foo { - fn foo(&self) { - println!("Foo"); - } -} - -trait Bar { - fn bar(&self) { - self.foo(); - } -} -``` - -We get this error: +For example, let's say we want to make an `OutlinePrint` trait with an +`outline_print` method that will print out a value outlined in asterisks. That +is, if our `Point` struct implements `Display` to result in `(x, y)`, calling +`outline_print` on a `Point` instance that has 1 for `x` and 3 for `y` would +look like: ```text -error: no method named `foo` found for type `&Self` in the current scope - --> :10:14 - | -10 | self.foo(); - | ^^^ - | - = help: items from traits can only be used if the trait is implemented and in scope; the following trait defines an item `foo`, perhaps you need to implement it: - = help: candidate #1: `main::Foo` +********** +* * +* (1, 3) * +* * +********** ``` -In other words, we haven't said that anything that implements `Bar` also -implements `Foo`. We can do that with a `:`, like this: +In the implementation of `outline_print`, since we want to be able to use the +`Display` trait's functionality, we need to be able to say that the +`OutlinePrint` trait will only work for types that also implement `Display` and +provide the functionality that `OutlinePrint` needs. We can do that in the +trait definition by specifying `OutlinePrint: Display`. It's like adding a +trait bound to the trait. Listing 19-29 shows an implementation of the +`OutlinePrint` trait: ```rust -trait Foo { - fn foo(&self) { - println!("Foo"); - } -} +use std::fmt::Display; -trait Bar: Foo { - fn bar(&self) { - self.foo(); +trait OutlinePrint: Display { + fn outline_print(&self) { + let output = self.to_string(); + let len = output.len(); + println!("{}", "*".repeat(len + 4)); + println!("*{}*", " ".repeat(len + 2)); + println!("* {} *", output); + println!("*{}*", " ".repeat(len + 2)); + println!("{}", "*".repeat(len + 4)); } } ``` -This works fine. +Listing 19-29: Implementing the `OutlinePrint` trait that +requires the functionality from `Display` + +Because we've specified that `OutlinePrint` requires the `Display` trait, we +can use `to_string` in `outline_print` (`to_string` is automatically +implemented for any type that implements `Display`). If we hadn't added the `: +Display` after the trait name and we tried to use `to_string` in +`outline_print`, we'd get an error that no method named `to_string` was found +for the type `&Self` in the current scope. + +If we try to implement `OutlinePrint` on a type that doesn't implement +`Display`, such as the `Point` struct: + +```rust +struct Point { + x: i32, + y: i32, +} + +impl OutlinePrint for Point {} +``` + +We'll get an error that `Display` isn't implemented and that `Display` is +required by `OutlinePrint`: + +```text +error[E0277]: the trait bound `Point: std::fmt::Display` is not satisfied + --> src/main.rs:20:6 + | +20 | impl OutlinePrint for Point {} + | ^^^^^^^^^^^^ the trait `std::fmt::Display` is not implemented for + `Point` + | + = note: `Point` cannot be formatted with the default formatter; try using + `:?` instead if you are using a format string + = note: required by `OutlinePrint` +``` + +Once we implement `Display` on `Point` and satisfy the constraint that +`OutlinePrint` requires, like so: + +```rust +use std::fmt; + +impl fmt::Display for Point { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}, {})", self.x, self.y) + } +} +``` + +then implementing the `OutlinePrint` trait on `Point` will compile successfully +and we can call `outline_print` on a `Point` instance to display it within an +outline of asterisks. ### Coherence From 7e814642b4196cb21c41d337593bb1ec14d86415 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 12 Apr 2017 09:54:34 -0400 Subject: [PATCH 20/30] Cut coherence since we covered orphan rule in 10-02 --- second-edition/src/ch19-03-advanced-traits.md | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 57197d427..302858d32 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -522,57 +522,6 @@ then implementing the `OutlinePrint` trait on `Point` will compile successfully and we can call `outline_print` on a `Point` instance to display it within an outline of asterisks. -### Coherence - -Finally, traits have a concept called 'coherence'. This governs exactly who is -allowed to implement a trait. In short: - -> To implement a type for a trait, you must have defined either the type, the -> trait, or both. - -Put another way: - -> You cannot implement a trait you didn't define for a type you didn't define. - -For example, defining the `Display` trait, which is defined in the standard -library, on a tuple of string slices, which is defined in the standard library, -won't work: - -```rust,ignore -use std::fmt; - -impl fmt::Display for (&'static str, &'static str) { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "({}, {})", self.0, self.1) - } -} -``` - -gives - -```text -error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> :4:1 - | -4 | impl fmt::Display for (&'static str, &'static str) { - | _^ starting here... -5 | | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { -6 | | write!(f, "({}, {})", self.0, self.1) -7 | | } -8 | | } - | |_^ ...ending here: impl doesn't use types inside crate - | - = note: the impl does not reference any types defined in this crate -``` - -Why do we have this rule? Allowing this would lead to ambiguity, confusion, and -broken code. Imagine that we have a crate `foo` that has a type `A` and a -trait `B`. If we could implement `B` for `A` in our code, it would work, but -what if someone else _also_ implemented `B` for `A` in their code? Furthermore, -what if a new release of `foo` comes out and implements `B` for `A` themselves? -These problems are not insurmountable, of course; we could determine some kind -of complex precedent rules to determine which `impl` 'wins' and works. - ### The newtype pattern There is a way to get around this, though. We call it the 'newtype pattern'. From 3cd5b8d63ecf2621a9389b03c7d2772d8f3a8ae5 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 12 Apr 2017 09:55:19 -0400 Subject: [PATCH 21/30] eddyb confirmed that i got this right --- second-edition/src/ch19-03-advanced-traits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 302858d32..fa57e3671 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -427,7 +427,7 @@ disambiguate from the `Bar` trait. Sometimes, we may want a trait to be able to rely on another trait also being implemented wherever our trait is implemented, so that our trait can use the other trait's functionality. The required trait is a *super trait* of the trait -we're implementing. TODO OR IS IT THE OTHER WAY AROUND +we're implementing. For example, let's say we want to make an `OutlinePrint` trait with an `outline_print` method that will print out a value outlined in asterisks. That From 0274d3eeffbd8b3a6f88f8e72da4544175c11da0 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 12 Apr 2017 10:49:10 -0400 Subject: [PATCH 22/30] Use newtype to transititon from traits to types? This is a little weird but it just might work? --- second-edition/src/ch19-03-advanced-traits.md | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index fa57e3671..329e7945e 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -522,26 +522,56 @@ then implementing the `OutlinePrint` trait on `Point` will compile successfully and we can call `outline_print` on a `Point` instance to display it within an outline of asterisks. -### The newtype pattern +### The Newtype Pattern to Implement External Traits on External Types -There is a way to get around this, though. We call it the 'newtype pattern'. -You create a new type that's a thin wrapper around the type you want to -implement the trait for, and then implement the trait for the wrapper. This -*will* work: +In Chapter 10, we mentioned the orphan rule, which says we're allowed to +implement a trait on a type as long as either the trait or the type are local +to our crate. One way to get around this restriction is to use the *newtype +pattern*, which involves creating a new type using a tuple struct with one +field as a thin wrapper around the type we want to implement a trait for. Then +the wrapper type is local to our crate, and we can implement the trait on the +wrapper. "Newtype" is a term from Haskell, and in Rust, there's no runtime +performance penalty for using this pattern. + +For example, if we wanted to implement `Display` on `Vec`, we can make a +`Wrapper` struct that holds an instance of `Vec`. Then we can implement +`Display` on `Wrapper` and use the `Vec` value as shown in Listing 19-30: + +Filename: src/main.rs ```rust use std::fmt; -struct Wrapper((&'static str, &'static str)); +struct Wrapper(Vec); impl fmt::Display for Wrapper { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "({}, {})", (self.0).0, (self.0).1) + write!(f, "[{}]", self.0.join(", ")) } } + +fn main() { + let w = Wrapper(vec![String::from("hello"), String::from("world")]); + println!("w = {}", w); +} ``` -The downside is that since `Wrapper` is a new type, it has no methods; we'll -have to implement them all. If you want it to have every single method that the -inner type has, implementing `Deref` can help you there. Otherwise, you'll have -to implement the methods yourself. +Listing 19-30: Creating a `Wrapper` type around +`Vec` to be able to implement `Display` + +The implementation of `Display` uses `self.0` to access the inner `Vec`, and +then we can use the functionality of the `Display` type on `Wrapper`. + +The downside is that since `Wrapper` is a new type, it doesn't have the methods +of the value it's holding; we'd have to implement all the methods of `Vec` like +`push`, `pop`, and all the rest directly on `Wrapper` to delegate to `self.0` +in order to be able to treat `Wrapper` exactly like a `Vec`. If we wanted the +new type to have every single method that the inner type has, implementing the +`Deref` trait that we discussed in Chapter 15 on the wrapper to return the +inner type can be a solution. If we don't want the wrapper type to have all the +methods of the inner type, in order to restrict the wrapper type's behavior for +example, we'd have to implement just the methods we do want ourselves. + +That's how the newtype pattern is used in relation to traits; it's also a +useful pattern without having traits involved. Let's switch focus now to talk +about some advanced ways to interact with Rust's type system. From f52faa002086800c2ec921f71209043891835313 Mon Sep 17 00:00:00 2001 From: steveklabnik Date: Wed, 12 Apr 2017 11:32:32 -0400 Subject: [PATCH 23/30] remove TODOs --- .../src/ch19-02-advanced-lifetimes.md | 19 +++++++++++++------ second-edition/src/ch19-03-advanced-traits.md | 7 ++++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/second-edition/src/ch19-02-advanced-lifetimes.md b/second-edition/src/ch19-02-advanced-lifetimes.md index a47bece8b..e4153873a 100644 --- a/second-edition/src/ch19-02-advanced-lifetimes.md +++ b/second-edition/src/ch19-02-advanced-lifetimes.md @@ -280,9 +280,11 @@ something you have a reference to. In Chapter 10, we discussed how to use trait bounds on generic types. We can also add lifetime parameters as constraints on generic types. For example, -let's say we wanted to make a wrapper over references to any type in order -to... TODO. The struct definition without lifetime parameters would look like -Listing 19-16: +let's say we wanted to make a wrapper over references. Remember `RefCell` +from Chapter 15? This is how the `borrow` and `borrow_mut` methods work; +they return wrappers over references in order to keep track of the borrowing +rules at run time. The struct definition without lifetime parameters would look +like Listing 19-16: ```rust,ignore struct Ref(&T); @@ -348,9 +350,14 @@ struct StaticRef(&'static T); to constrain `T` to types that have only `'static` references or no references -Types with no references inside count as `'static`. Because `'static` is longer -than any other lifetime, a type like `T: 'a` can only be a type with no -references. TODO CONFUSED +Types with no references count as `T: 'static`. Because `'static` means "this +reference must live as long as the entire program," a type that contains no +references, well, all of them live as long as the entire program. This can be +a little bit hard to get at first, but think of it this way: if the borrow +checker is concerned about references living long enough, then there's no +real distinction between "this has no references" and "this has references +that live forever"; both of them are the same for the purpose of "does this +reference live shorter than what it refers to." ### Lifetimes in Trait Objects diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 329e7945e..4f0fecd07 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -185,9 +185,10 @@ While trait objects mean that we don't need to know the concrete type of the `graph` parameter at compile time, we do need to constrain the use of the `AGraph` trait in the `traverse` function by the concrete types of the associated types. If we didn’t provide this constraint, Rust wouldn't be able -to figure out which `impl` to match this trait object to. - -TODO: I basically copied the last sentence from the old book but i dont really understand it /Carol +to figure out which `impl` to match this trait object to. Think of it this +way: if we didn't define the associated types, and we had multiple implementations +of this trait for different associated types, there'd be no way to choose which +one of those implementations to use. ### Operator Overloading and Default Type Parameters From 27ceeb2dab37745f405f8e4f1ad5a17d4b8a50fc Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 12 Apr 2017 13:37:37 -0400 Subject: [PATCH 24/30] More edits --- second-edition/src/ch17-02-trait-objects.md | 3 +- .../src/ch19-02-advanced-lifetimes.md | 25 +- second-edition/src/ch19-03-advanced-traits.md | 12 +- second-edition/src/ch19-04-advanced-types.md | 295 +++++++++++------- ...ch19-05-advanced-functions-and-closures.md | 129 ++++---- 5 files changed, 284 insertions(+), 180 deletions(-) diff --git a/second-edition/src/ch17-02-trait-objects.md b/second-edition/src/ch17-02-trait-objects.md index c4b2caa62..4f6b7a4d4 100644 --- a/second-edition/src/ch17-02-trait-objects.md +++ b/second-edition/src/ch17-02-trait-objects.md @@ -44,7 +44,8 @@ instances and call `draw` on them. In Rust, though, we can define a trait that we'll name `Draw` and that will have one method named `draw`. Then we can define a vector that takes a *trait object*, which is a trait behind some sort of pointer, such as a `&` reference -or a `Box` smart pointer. +or a `Box` smart pointer. We'll talk about the reason trait objects have to +be behind a pointer in Chapter 19. We mentioned that we don't call structs and enums "objects" to distinguish structs and enums from other languages' objects. The data in the struct or enum diff --git a/second-edition/src/ch19-02-advanced-lifetimes.md b/second-edition/src/ch19-02-advanced-lifetimes.md index e4153873a..0036bc018 100644 --- a/second-edition/src/ch19-02-advanced-lifetimes.md +++ b/second-edition/src/ch19-02-advanced-lifetimes.md @@ -281,10 +281,10 @@ something you have a reference to. In Chapter 10, we discussed how to use trait bounds on generic types. We can also add lifetime parameters as constraints on generic types. For example, let's say we wanted to make a wrapper over references. Remember `RefCell` -from Chapter 15? This is how the `borrow` and `borrow_mut` methods work; -they return wrappers over references in order to keep track of the borrowing -rules at run time. The struct definition without lifetime parameters would look -like Listing 19-16: +from Chapter 15? This is how the `borrow` and `borrow_mut` methods work; they +return wrappers over references in order to keep track of the borrowing rules +at runtime. The struct definition, without lifetime parameters for now, would +look like Listing 19-16: ```rust,ignore struct Ref(&T); @@ -350,14 +350,15 @@ struct StaticRef(&'static T); to constrain `T` to types that have only `'static` references or no references -Types with no references count as `T: 'static`. Because `'static` means "this -reference must live as long as the entire program," a type that contains no -references, well, all of them live as long as the entire program. This can be -a little bit hard to get at first, but think of it this way: if the borrow -checker is concerned about references living long enough, then there's no -real distinction between "this has no references" and "this has references -that live forever"; both of them are the same for the purpose of "does this -reference live shorter than what it refers to." +Types with no references count as `T: 'static`. Because `'static` means the +reference must live as long as the entire program, a type that contains no +references meets the criteria of all references living as long as the entire +program (since there are no references). Think of it this way: if the borrow +checker is concerned about references living long enough, then there's no real +distinction between a type that has no references and a type that has +references that live forever; both of them are the same for the purpose of +determining whether or not a reference has a shorter lifetime than what it +refers to. ### Lifetimes in Trait Objects diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 4f0fecd07..5b32f5070 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -398,6 +398,10 @@ the instance of `Baz` as the first argument. Listing 19-28 shows how to call # impl Bar for Baz { # fn f(&self) { println!("Baz’s impl of Bar"); } # } +# impl Baz { +# fn f(&self) { println!("Baz's impl"); } +# } +# fn main() { let b = Baz; b.f(); @@ -423,7 +427,7 @@ on `Baz` and the `Foo` trait implemented on `Baz` in scope, we could call the `f` method in `Foo` by using `Foo::f(&b)` since we wouldn't have to disambiguate from the `Bar` trait. -### Super Traits +### Supertraits to Use One Trait's Functionality Within Another Trait Sometimes, we may want a trait to be able to rely on another trait also being implemented wherever our trait is implemented, so that our trait can use the @@ -482,6 +486,7 @@ If we try to implement `OutlinePrint` on a type that doesn't implement `Display`, such as the `Point` struct: ```rust +# trait OutlinePrint {} struct Point { x: i32, y: i32, @@ -510,6 +515,11 @@ Once we implement `Display` on `Point` and satisfy the constraint that `OutlinePrint` requires, like so: ```rust +# struct Point { +# x: i32, +# y: i32, +# } +# use std::fmt; impl fmt::Display for Point { diff --git a/second-edition/src/ch19-04-advanced-types.md b/second-edition/src/ch19-04-advanced-types.md index fdb600c64..51376a8c8 100644 --- a/second-edition/src/ch19-04-advanced-types.md +++ b/second-edition/src/ch19-04-advanced-types.md @@ -1,42 +1,79 @@ -# Advanced Types +## Advanced Types -There's a few aspects of Rust's type system we haven't gone over. Write a better -intro that isn't literally the same as every other section here :frown: +The Rust type system has some features that we've mentioned or used without +discussing. We started talking about the newtype pattern in regards to traits; +we'll start with a more general discussion about why newtypes are useful as +types. We'll then move to type aliases, a feature that is similar to newtypes +but has slightly different semantics. We'll also discuss the `!` type and +dynamically sized types. -## Type Aliases +### Using the Newtype Pattern for Type Safety and Abstraction -Rust provides the ability to declare a 'type alias' with the `type` keyword: +The newtype pattern that we started discussing at the end of the "Advanced +Traits" section, where we create a new type as a tuple struct with one field +that wraps a type can also be useful for statically enforcing that values are +never confused, and is often used to indicate the units of a value. We actually +had an example of this in Listing 19-26: the `Millimeters` and `Meters` structs +both wrap `u32` values in a new type. If we write a function with a parameter +of type `Millimeters`, we won't be able to compile a program that accidentally +tries to call that function with a value of type `Meters` or a plain `u32`. + +Another reason to use the newtype pattern is to abstract away some +implementation details of a type: the wrapper type can expose a different +public API than the private inner type would if we used it directly in order to +restrict the functionality that is available, for example. New types can also +hide internal generic types. For example, we could provide a `People` type that +wraps a `HashMap` that stores a person's ID associated with their +name. Code using `People` would only interact with the public API we provide, +such as a method to add a name string to the `People` collection, and that code +wouldn't need to know that we assign an `i32` ID to names internally. The +newtype pattern is a lightweight way to achieve encapsulation to hide +implementation details that we discussed in Chapter 17. + +### Type Aliases Create Type Synonyms + +The newtype pattern involves creating a new struct to be a new, separate type. +Rust also provides the ability to declare a *type alias* with the `type` +keyword to give an existing type another name. For example, we can create the +alias `Kilometers` to `i32` like so: ```rust -type Foo = i32; +type Kilometers = i32; ``` -This means that `Foo` is a _synonym_ for `i32`; it's not its own, new type. Which -means you can do this: +This means `Kilometers` is a *synonym* for `i32`; unlike the `Millimeters` and +`Meters` types we created in Listing 19-26, `Kilometers` is not a separate, new +type. Values that have the type `Kilometers` will be treated exactly the same +as values of type `i32`: ```rust -type Foo = i32; +type Kilometers = i32; let x: i32 = 5; -let y: Foo = 5; +let y: Kilometers = 5; println!("x + y = {}", x + y); ``` -Since `Foo` is an alias for `i32`, they're the same type, and we can add them together. -If you want a distinct type for `Foo`, you'd use the newtype pattern from Chapter XX. +Since `Kilometers` is an alias for `i32`, they're the same type. We can add +values of type `i32` and `Kilometers` together, and we can pass `Kilometers` +values to functions that take `i32` parameters. We don't get the type checking +benefits that we get from the newtype pattern that we discussed in the previous +section. -The main use-case for type synonyms is to reduce repetition. For example, you may have -a type like this: +The main use case for type synonyms is to reduce repetition. For example, we +may have a lengthy type like this: ```rust,ignore Box ``` -Typing this out all over the place can be tiresome and error-prone: +Writing this out in function signatures and as type annotations all over the +place can be tiresome and error-prone. Imagine having a project full of code +like that in Listing 19-31: -```rust,ignore -let f: Box = |x| x + 1; +```rust +let f: Box = Box::new(|| println!("hi")); fn takes_long_type(f: Box) { // ... @@ -44,15 +81,21 @@ fn takes_long_type(f: Box) { fn returns_long_type() -> Box { // ... +# Box::new(|| ()) } ``` -An alias makes this more manageable: +Listing 19-31: Using a long type in many places -```rust,ignore +A type alias makes this code more manageable by reducing the amount of +repetition this project has. Here, we've introduced an alias named `Thunk` for +the verbose type, and we can replace all uses of the type with the shorter +`Thunk` as shown in Listing 19-32: + +```rust type Thunk = Box; -let f: Thunk = |x| x + 1; +let f: Thunk = Box::new(|| println!("hi")); fn takes_long_type(f: Thunk) { // ... @@ -60,78 +103,97 @@ fn takes_long_type(f: Thunk) { fn returns_long_type() -> Thunk { // ... +# Box::new(|| ()) } ``` -Much easier. A related case is with the `Result` type. Consider the `std::io` -module in the standard library. I/O operations often return a `Result`, as they -may fail to work. So, there's a struct, `std::io::Error`, that represents all of these -different possible errors. Many of the functions in `std::io` will be returning a -`Result` where the `E` is an `std::io::Error`. For example, the `Write` trait: +Listing 19-32: Introducing a type alias `Thunk` to reduce +repetition -```rust,ignore +Much easier to read and write! Choosing a good name for a type alias can help +communicate your intent as well (*thunk* is a word for code to be evaluated at +a later time, so it's an appropriate name for a closure that gets stored). + +Another common use of type aliases is with the `Result` type. Consider +the `std::io` module in the standard library. I/O operations often return a +`Result`, since their operations may fail to work. There's a +`std::io::Error` struct that represents all of the possible I/O errors. Many of +the functions in `std::io` will be returning `Result` where the `E` is +`std::io::Error`, such as these functions in the `Write` trait: + +```rust use std::io::Error; +# use std::fmt::Arguments; pub trait Write { fn write(&mut self, buf: &[u8]) -> Result; fn flush(&mut self) -> Result<(), Error>; - fn write_all(&mut self, buf: &[u8]) -> Result<(), Error> { ... } - fn write_fmt(&mut self, fmt: Arguments) -> Result<(), Error> { ... } + fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>; + fn write_fmt(&mut self, fmt: Arguments) -> Result<(), Error>; } ``` -We're writing `Result<..., Error>` a lot. As such, `std::io` has this -declaration: +We're writing `Result<..., Error>` a lot. As such, `std::io` has this type +alias declaration: ```rust,ignore type Result = Result; ``` -Because this is in the `std::io` module, it's now `std::io::Result`; that is, -a `Result` with the `E` filled in as `std::io::Error`. This helps in two -ways: first, the `Write` trait ends up looking like this: +Because this is in the `std::io` module, the fully qualified alias that we can +use is `std::io::Result`; that is, a `Result` with the `E` filled in +as `std::io::Error`. The `Write` trait function signatures end up looking like +this: ```rust,ignore pub trait Write { fn write(&mut self, buf: &[u8]) -> Result; fn flush(&mut self) -> Result<()>; - fn write_all(&mut self, buf: &[u8]) -> Result<()> { ... } - fn write_fmt(&mut self, fmt: Arguments) -> Result<()> { ... } + fn write_all(&mut self, buf: &[u8]) -> Result<()>; + fn write_fmt(&mut self, fmt: Arguments) -> Result<()>; } ``` -This is easier to write *and* gives us a consistent interface across all -of `std::io`. But because it's an alias, it is just another `Result`, -which means we can use any methods that work on `Result` with it, -and special syntax like `?`. +The type alias helps in two ways: this is easier to write *and* it gives us a +consistent interface across all of `std::io`. Because it's an alias, it is just +another `Result`, which means we can use any methods that work on +`Result` with it, and special syntax like `?`. -## The 'never' type, `!` +### The Never Type, `!`, that Never Returns -Rust has a special type named `!`. In type theory lingo, it's called the 'bottom type', -but we prefer the name 'never'. The name describes what it does: +Rust has a special type named `!`. In type theory lingo, it's called the +*bottom type*, but we prefer the name *never*. The name describes what it does: +it stands in the place of the return type when a function will never return. +For example: ```rust,ignore fn bar() -> ! { ``` -This is read as "the function `bar` returns never." And in this case, that's what -it means! You cannot create values of the type `!`, and so `bar` can never possibly -return. How could it, if it can't create a value to return? +This is read as "the function `bar` returns never," and functions that return +never are called *diverging functions*. We can't create values of the type `!`, +so `bar` can never possibly return. What use is a type you can never create +values for? If you think all the way back to Chapter 2, we had some code that +looked like this, reproduced here in Listing 19-33: -What use is a type you can never create values for? If you think all the way back -to Chapter 2, we had some code that looked like this: - -```rust,ignore +```rust +# let guess = "3"; +# loop { let guess: u32 = match guess.trim().parse() { Ok(num) => num, Err(_) => continue, }; +# break; +# } ``` -At the time, we skipped over some details. For example, you've learned that -`match` arms must have the same value. This doesn't work: +Listing 19-33: A `match` with an arm that ends in +`continue` + +At the time, we skipped over some details in this code. In Chapter 6, we +learned that `match` arms must return the same type. This doesn't work: ```rust,ignore let guess = match guess.trim().parse() { @@ -140,20 +202,24 @@ let guess = match guess.trim().parse() { } ``` -What would the type of `guess` be here? It'd have to be both an integer and a string, -and that doesn't work. So why does `continue`? +What would the type of `guess` be here? It'd have to be both an integer and a +string, and Rust requires that `guess` can only have one type. So what +`continue` return? Why are we allowed to return a `u32` from one arm in Listing +19-33 and have another arm that ends with `continue`? -As you may have guessed, `continue` has a value of `!`. That is, when Rust goes to -compute the type of `guess`, it looks at both of the match arms. The former has a -value of `u32`, and the latter has a value of `!`. Since `!` can never have a value, -Rust is okay with this, and decides that the type of `guess` is `u32`. The fancy way -of saying this is that "never unifies with all other types". This works because -`continue` doesn't actually return a value; it instead moves control back to the top -of the loop. In the `Err` case, we never actually assign a value to `guess`. So -this is fine. +As you may have guessed, `continue` has a value of `!`. That is, when Rust goes +to compute the type of `guess`, it looks at both of the match arms. The former +has a value of `u32`, and the latter has a value of `!`. Since `!` can never +have a value, Rust is okay with this, and decides that the type of `guess` is +`u32`. The formal way of describing this behavior of `!` is that the never type +unifies with all other types. We're allowed to end this `match` arm with +`continue` because `continue` doesn't actually return a value; it instead moves +control back to the top of the loop, so in the `Err` case, we never actually +assign a value to `guess`. -Another example of the never type is `panic!`. Remember the `unwrap` function that -we call on `Option` values to produce a value or panic? Here's its definition: +Another use of the never type is `panic!`. Remember the `unwrap` function that +we call on `Option` values to produce a value or panic? Here's its +definition: ```rust,ignore impl Option { @@ -166,10 +232,11 @@ impl Option { } ``` -Here, the same thing happens: We know that `val` has the type `T`, and `panic!` has -the type `!`. So the result of the overall `match` expression is `T`. This works -because `panic!` doesn't produce a value; it panics. In the `None` case, we won't be -returning a value from `unwrap`, and so it all works out. +Here, the same thing happens as in the `match` in Listing 19-33: we know that +`val` has the type `T`, and `panic!` has the type `!`, so the result of the +overall `match` expression is `T`. This works because `panic!` doesn't produce +a value; it ends the program. In the `None` case, we won't be returning a value +from `unwrap`, so this code is valid. One final expression that has the type `!` is a `loop`: @@ -181,89 +248,93 @@ loop { } ``` -Here, the loop never ends, and so the value of the expression is `!`. This -wouldn't be true if we included a `break`, however, as the loop would terminate. +Here, the loop never ends, so the value of the expression is `!`. This wouldn't +be true if we included a `break`, however, as the loop would terminate when it +gets to the `break`. -## Dynamically Sized Types & `Sized` +### Dynamically Sized Types & `Sized` -Because Rust needs to know things like memory layout, there's a particular corner -of its type system that can be confusing, and that's the concept of 'dynamically -sized types.' Sometimes referred to as 'DSTs' or 'unsized types', these types let -us talk about things that we only know the size of at runtime. +Because Rust needs to know things like memory layout, there's a particular +corner of its type system that can be confusing, and that's the concept of +*dynamically sized types*. Sometimes referred to as 'DSTs' or 'unsized types', +these types let us talk about types whose size we can only know at runtime. -That's extremely abstract, so let's dig into the details of a dynamically sized -type that we've been using this whole book: `str`. That's right, not `&str`, but -`str`, on its own. `str` is a DST; we can't know how long the string is until -runtime. Since we can't know that, we can't create a variable of type `str`; -nor can we take an argument of type `str`. Consider this code, which does not -work: +Let's dig into the details of a dynamically sized type that we've been using +this whole book: `str`. That's right, not `&str`, but `str` on its own. `str` +is a DST; we can't know how long the string is until runtime. Since we can't +know that, we can't create a variable of type `str`, nor can we take an +argument of type `str`. Consider this code, which does not work: ```rust,ignore let s1: str = "Hello there!"; let s2: str = "How's it going?"; ``` -These two `str`s would need to have the exact same memory layout, but they have -different lengths: `s1` needs 12 bytes of storage, and `s2` needs 15. This is -why it's not possible to create a variable holding a dynamically sized type. +These two `str` values would need to have the exact same memory layout, but +they have different lengths: `s1` needs 12 bytes of storage, and `s2` needs 15. +This is why it's not possible to create a variable holding a dynamically sized +type. -So what to do? Well, you already know the answer in this case: `s1` and `s2` -aren't just `str`s, but `&str`s, and more specifically, `&'static str`s, though -the static bit isn't particularly relevant here. If you think back to Chapter 4, -we said this about `&str`: +So what to do? Well, you already know the answer in this case: the types of +`s1` and `s2` are `&str` rather than `str`. If you think back to Chapter 4, we +said this about `&str`: > ... it’s a reference to an internal position in the String and the number of > elements that it refers to. -So while a `&T` is a single value, storing the memory address of where the `T` -is located, a `&str` is _two_ values: the address of the `str`, and how long it +So while a `&T` is a single value that stors the memory address of where the `T` +is located, a `&str` is *two* values: the address of the `str` and how long it is. As such, a `&str` has a size we can know at compile time: it's two times the size of a `usize` in length. That is, we always know the size of a `&str`, no matter how long the string it refers to is. This is the general way in which dynamically sized types are used in Rust; they have an extra bit of metadata -that stores the dynamic information. This leads us to the golden rule of -dynamically sized types: - -You must always put values of dynamically sized types behind a pointer of some -kind. +that stores the size of the dynamic information. This leads us to the golden +rule of dynamically sized types: we must always put values of dynamically sized +types behind a pointer of some kind. While we've talked a lot about `&str`, we can combine `str` with all kinds of pointers: `Box`, for example, or `Rc`. In fact, you've already seen -this before, but with a different dynamically sized type: `Trait`. That is, -the name of a trait, without any sort of qualifications. In Chapter 17, -we only talked about `Box` as a trait object, but given that -just `Trait` on its own is a dynamically sized type, `Rc` or -`&Trait` work too. +this before, but with a different dynamically sized type: traits. Every trait +is a dynamically sized type we can refer to by using the name of the trait. In +Chapter 17, we mentioned that in order to use traits as trait objects, we have +to put them behind a pointer like `&Trait` or `Box` (`Rc` would +work too). Traits being dynamically sized is the reason we have to do that! - +#### The `Sized` Trait -### The Sized trait + To work with DSTs, Rust has a trait that determines if a type's size is known -at compile time or not: `Sized`. This trait is automatically implemented for -everything the compiler knows the size of at compile time. In addition, Rust -sneaks in a bound on `Sized` to every generic function. That is, +at compile time or not, which is `Sized`. This trait is automatically +implemented for everything the compiler knows the size of at compile time. In +addition, Rust implicitly adds a bound on `Sized` to every generic function. +That is, a generic function definitition like this: ```rust,ignore fn generic(t: T) { ``` -is actually +is actually treated as if we had written this: ```rust,ignore fn generic(t: T) { ``` -That is, by default, everything can only work on types that are sized at compile -time. There is, however, special syntax you can use to relax this restriction: +By default, generic functions will only work on types that have a known size at +compile time. There is, however, special syntax you can use to relax this +restriction: ```rust,ignore fn generic(t: &T) { ``` -There's two differences here: `?Sized` is the opposite of `Sized`, that is, this -reads as '`T` may or may not be `Sized`. This syntax is only available for `Sized`, -and not other traits. +A trait bound on `?Sized` is the opposite of a trait bound on `Sized`; that is, +we would read this as '`T` may or may not be `Sized`'. This syntax is only +available for `Sized`, no other traits. -Secondly, you'll note we switched to `&T`; because the argument may not be `Sized`, -we need to use it behind some kind of pointer, in this case, a reference. \ No newline at end of file +Also note we switched the type of the `t` parameter from `T` to `&T`: since the +type might not be `Sized`, we need to use it behind some kind of pointer. In +this case, we've chosen a reference. + +Next let's talk about functions and closures! diff --git a/second-edition/src/ch19-05-advanced-functions-and-closures.md b/second-edition/src/ch19-05-advanced-functions-and-closures.md index a0a3f1ad2..0c41e6558 100644 --- a/second-edition/src/ch19-05-advanced-functions-and-closures.md +++ b/second-edition/src/ch19-05-advanced-functions-and-closures.md @@ -1,14 +1,17 @@ -# Advanced Functions & Closures +## Advanced Functions & Closures -We've talked a lot about functions in this book, and a little bit about a -related feature, closures. There's a few bits we haven't covered yet, so let's -go over those now. +Finally, let's discuss some advanced features having to do with functions and +closures: function pointers, diverging functions, and returning closures. -## Function pointers +### Function pointers We've talked about how to pass closures to functions, but you can pass regular -functions to functions too! Functions have the type `fn()`, with a lower case 'f'. -Don't confuse it with the `Fn()` closure trait! The syntax is similar: +functions to functions too! Functions have the type `fn`, with a lower case 'f' +not to be confused with the `Fn` closure trait. `fn` is called a *function +pointer*. The syntax for specifying that a parameter is a function pointer is +similar to that of closures, as shown in Listing 19-34: + +Filename: src/main.rs ```rust fn add_one(x: i32) -> i32 { @@ -26,61 +29,65 @@ fn main() { } ``` -This prints `The answer is: 12`. This `f(i32) -> i32` syntax is called -a 'function pointer', and unlike closures, you don't use it as a trait, -you use it directly, as you can see in the signature of `do_twice`. +Listing 19-34: Using the `fn` type to accept a function +pointer as an argument -### Point-free style +This prints `The answer is: 12`. We specify that the parameter `f` in +`do_twice` is an `fn` that takes one parameter of type `i32` and returns an +`i32`. We can then call `f` in the body of `do_twice`. In `main`, we can pass +the function name `add_one` as the first argument to `do_twice`. -Function pointers implement all three of the closure traits: `Fn`, `FnMut`, and -`FnOnce`. So you can always pass a pointer to a function that expects a closure: +Unlike closures, `fn` is a type rather than a trait, so we specify `fn` as the +parameter type directly rather than declaring a generic type parameter with one +of the `Fn` traits as a trait bound. + +Function pointers implement all three of the closure traits (`Fn`, `FnMut`, and +`FnOnce`), so we can always pass a function pointer as an argument when calling +a function that expects a closure. Prefer to write functions using a generic +type and one of the closure traits, so that your functions can accept either +functions or closures. An example of a case where you'd only want to accept +`fn` is when interfacing with external code that doesn't have closures: C +functions can accept functions as arguments, but C doesn't have closures. + +For example, if we wanted to use the `map` function to turn a vector of numbers +into a vector of strings, we could use a closure: ```rust -// fold takes a FnMut closure... but we can use this function too! -fn add(acc: i32, x: &i32) -> i32 { - acc + *x -} - -let v = vec![1, 2, 3]; - -let six = v.iter().fold(0, |acc, &x| acc + x); -let six = v.iter().fold(0, add); +let list_of_numbers = vec![1, 2, 3]; +let list_of_strings: Vec = list_of_numbers + .iter() + .map(|i| i.to_string()) + .collect(); ``` -This is sometimes called 'point-free style', for fairly obscure reasons -that don't matter. This can work for anything where the types line up. -For example: +Or we could name a function as the argument to `map` instead of the closure: ```rust -let v = vec![1, 2, 3]; - -let strings: Vec = v.iter().map(|s| s.to_string()).collect(); - -// to_string is provided by the ToString trait -let strings: Vec = v.iter().map(ToString::to_string).collect(); +let list_of_numbers = vec![1, 2, 3]; +let list_of_strings: Vec = list_of_numbers + .iter() + .map(ToString::to_string) + .collect(); ``` +Note that we do have to use the fully qualified syntax that we talked about in +the "Advanced Traits" section because there are multiple functions available +named `to_string`; here, we're using the `to_string` function defined in the +`ToString` trait, which the standard library has implemented for any type that +implements `Display`. + Some people prefer this style, some people prefer the closure. They end up with the same code, so use whichever feels more clear to you. -## Diverging functions +### Returning Closures -In the previous section, we talked about the never type, `!`. Functions -that return never are called "diverging functions": +Because closures are represented by traits, returning closures is a little +tricky; we can't do it directly. In most cases where we may want to return a +trait, we can instead use the concrete type that implements the trait of what +we're returning as the return value of the function. We can't do that with +closures, though; we're not allowed to use `fn` as a return type, for example. -```rust -fn never_returns() -> ! { - panic!("oh no!"); -} -``` - -For more details, see the previous section. - -## Returning closures - -As we discussed before, closures are represented by traits: `Fn`, `FnMut`, and `FnOnce`. -This means that returning them is a little tricky; you can't do it directly. This will -give a compiler error: +This code that tries to return a closure directly won't compile: ```rust,ignore fn returns_closure() -> Fn(i32) -> i32 { @@ -88,21 +95,25 @@ fn returns_closure() -> Fn(i32) -> i32 { } ``` -It looks like this: +The compiler error is: ```text -error[E0277]: the trait bound `std::ops::Fn(i32) -> i32 + 'static: std::marker::Sized` is not satisfied +error[E0277]: the trait bound `std::ops::Fn(i32) -> i32 + 'static: +std::marker::Sized` is not satisfied --> :2:25 | 2 | fn returns_closure() -> Fn(i32) -> i32 { - | ^^^^^^^^^^^^^^ the trait `std::marker::Sized` is not implemented for `std::ops::Fn(i32) -> i32 + 'static` + | ^^^^^^^^^^^^^^ the trait `std::marker::Sized` is + not implemented for `std::ops::Fn(i32) -> i32 + 'static` | - = note: `std::ops::Fn(i32) -> i32 + 'static` does not have a constant size known at compile-time + = note: `std::ops::Fn(i32) -> i32 + 'static` does not have a constant size + known at compile-time = note: the return type of a function must have a statically known size ``` -What to do? With most things that implement traits, we could return them by naming -the type, but we can't do that with closures. Instead, we need to use a trait object: +The `Sized` trait again! Rust doesn't know much space it'll need to store the +closure. We saw a solution to this in the previous section, though: we can use +a trait object: ```rust fn returns_closure() -> Box i32> { @@ -110,4 +121,14 @@ fn returns_closure() -> Box i32> { } ``` -For more about trait objects, see Chapter 18. \ No newline at end of file +For more about trait objects, refer back to Chapter 18. + +## Summary + +Whew! Now we've gone over features of Rust that aren't used very often, but are +available if you need them. We've introduced a lot of complex topics so that +when you encounter them in error message suggestions or when reading others' +code, you'll at least have seen these concepts and syntax once before. + +Now, let's put everything we've learned throughout the book into practice with +one more project! From d02e79ceb133147c26095ebb4a6c7cdc5568deec Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 12 Apr 2017 18:47:58 -0400 Subject: [PATCH 25/30] spellingz --- second-edition/dictionary.txt | 5 +++++ second-edition/src/ch19-03-advanced-traits.md | 2 +- second-edition/src/ch19-04-advanced-types.md | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index b86dff4c9..a0a90473f 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -44,6 +44,7 @@ ChangeColor ChangeColorMessage chXX chYY +ConcreteType config Config const @@ -193,6 +194,7 @@ namespacing newfound NewsArticle newtype +newtypes nitty nocapture nomicon @@ -208,6 +210,7 @@ OptionalNumber OsStr OsString other's +OutlinePrint overread parameterize ParseIntError @@ -215,6 +218,7 @@ PartialEq PartialOrd PendingReview PendingReviewPost +PlaceholderType portia powi preprocessing @@ -295,6 +299,7 @@ Supertraits test's TextField That'd +there'd threadsafe timestamp Tiếng diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 5b32f5070..7ed4f12c3 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -354,7 +354,7 @@ fn main() { Listing 19-27: Implementing two traits that both have a method with the same name as a method defined on the struct directly -For the implemetation of the `f` method for the `Foo` trait on `Baz`, we're +For the implementation of the `f` method for the `Foo` trait on `Baz`, we're printing out `Baz's impl of Foo`. For the implementation of the `f` method for the `Bar` trait on `Baz`, we're printing out `Baz's impl of Bar`. The implementation of `f` directly on `Baz` prints out `Baz's impl`. What should diff --git a/second-edition/src/ch19-04-advanced-types.md b/second-edition/src/ch19-04-advanced-types.md index 51376a8c8..eea5e1fd9 100644 --- a/second-edition/src/ch19-04-advanced-types.md +++ b/second-edition/src/ch19-04-advanced-types.md @@ -282,9 +282,9 @@ said this about `&str`: > ... it’s a reference to an internal position in the String and the number of > elements that it refers to. -So while a `&T` is a single value that stors the memory address of where the `T` -is located, a `&str` is *two* values: the address of the `str` and how long it -is. As such, a `&str` has a size we can know at compile time: it's two times +So while a `&T` is a single value that stores the memory address of where the +`T` is located, a `&str` is *two* values: the address of the `str` and how long +it is. As such, a `&str` has a size we can know at compile time: it's two times the size of a `usize` in length. That is, we always know the size of a `&str`, no matter how long the string it refers to is. This is the general way in which dynamically sized types are used in Rust; they have an extra bit of metadata @@ -309,7 +309,7 @@ To work with DSTs, Rust has a trait that determines if a type's size is known at compile time or not, which is `Sized`. This trait is automatically implemented for everything the compiler knows the size of at compile time. In addition, Rust implicitly adds a bound on `Sized` to every generic function. -That is, a generic function definitition like this: +That is, a generic function definition like this: ```rust,ignore fn generic(t: T) { From 67876e3ef5323ce9d394f3ea6b08cb3d173d9ba9 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 12 Apr 2017 19:03:47 -0400 Subject: [PATCH 26/30] Adjust headings, spelling of supertraits --- second-edition/src/ch17-02-trait-objects.md | 6 +++--- second-edition/src/ch19-00-advanced-features.md | 8 +++++++- second-edition/src/ch19-03-advanced-traits.md | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/second-edition/src/ch17-02-trait-objects.md b/second-edition/src/ch17-02-trait-objects.md index 4f6b7a4d4..3fc6eaad7 100644 --- a/second-edition/src/ch17-02-trait-objects.md +++ b/second-edition/src/ch17-02-trait-objects.md @@ -394,9 +394,9 @@ trait Foo: Sized { } ``` -The trait `Sized` is now a *super trait* of trait `Foo`, which means trait -`Foo` requires types that implement `Foo` (that is, `Self`) to be `Sized`. -We're going to talk about super traits in more detail in Chapter 19. +The trait `Sized` is now a *supertrait* of trait `Foo`, which means trait `Foo` +requires types that implement `Foo` (that is, `Self`) to be `Sized`. We're +going to talk about supertraits in more detail in Chapter 19. The reason a trait like `Foo` that requires `Self` to be `Sized` is not allowed to be a trait object is that it would be impossible to implement the trait diff --git a/second-edition/src/ch19-00-advanced-features.md b/second-edition/src/ch19-00-advanced-features.md index 687135004..fc9294ba0 100644 --- a/second-edition/src/ch19-00-advanced-features.md +++ b/second-edition/src/ch19-00-advanced-features.md @@ -14,4 +14,10 @@ In this chapter, we're going to cover: tell the compiler that you will be responsible for upholding the guarantees instead * Advanced Lifetimes: Additional lifetime syntax for complex situations -* Advanced Traits: Associated Types, coherence, and disambiguation +* Advanced Traits: Associated Types, default type parameters, fully qualified + syntax, supertraits, and the newtype pattern in relation to traits +* Advanced Types: some more about the newtype pattern, type aliases, the + "never" type, and dynamically sized types +* Advanced Functions and Closures: function pointers and returning closures + +It's a panoply of Rust features with something for everyone! Let's dive in! diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index 7ed4f12c3..aa1d9cb0f 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -431,7 +431,7 @@ disambiguate from the `Bar` trait. Sometimes, we may want a trait to be able to rely on another trait also being implemented wherever our trait is implemented, so that our trait can use the -other trait's functionality. The required trait is a *super trait* of the trait +other trait's functionality. The required trait is a *supertrait* of the trait we're implementing. For example, let's say we want to make an `OutlinePrint` trait with an From d3fed52725bf56d21e0db460b961c484b60b3b14 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Wed, 12 Apr 2017 22:33:07 -0400 Subject: [PATCH 27/30] We're switching to supertrait, inform dictionary --- second-edition/dictionary.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index a0a90473f..c12e89f8b 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -295,7 +295,8 @@ substring subtree subtyping Summarizable -Supertraits +supertrait +supertraits test's TextField That'd From 60dc7eb2552f20668a584bf93fe8e14b20993f5f Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Thu, 13 Apr 2017 11:12:43 -0400 Subject: [PATCH 28/30] =?UTF-8?q?Edits=20to=20resolve=20most=20of=20matthe?= =?UTF-8?q?wjasper's=20comments=20=E2=9D=A4=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- second-edition/src/ch19-01-unsafe-rust.md | 56 ++++++++++--------- second-edition/src/ch19-03-advanced-traits.md | 36 ++++++------ second-edition/src/ch19-04-advanced-types.md | 5 +- 3 files changed, 54 insertions(+), 43 deletions(-) diff --git a/second-edition/src/ch19-01-unsafe-rust.md b/second-edition/src/ch19-01-unsafe-rust.md index 9d69a4c97..97dbdca35 100644 --- a/second-edition/src/ch19-01-unsafe-rust.md +++ b/second-edition/src/ch19-01-unsafe-rust.md @@ -33,7 +33,7 @@ superpowers." We haven't seen most of these features yet since they're only usable with `unsafe`! 1. Dereferencing a raw pointer -2. Calling an unsafe function +2. Calling an unsafe function or method 3. Accessing or modifying a mutable static variable 4. Implementing an unsafe trait @@ -54,14 +54,15 @@ be related to one of the places that you opted into this unsafety. That makes the cause of memory safety bugs much easier to find, since we know Rust is checking all of the other code for us. To get this benefit of only having a few places to investigate memory safety bugs, it's important to contain your unsafe -code to as small of an area as possible. Once you use `unsafe` inside of a -module, any of the code in that module is suspect: keep `unsafe` blocks small -and you'll thank yourself later. +code to as small of an area as possible. Any code inside of an `unsafe` block +is suspect when debugging a memory problem: keep `unsafe` blocks small and +you'll thank yourself later since you'll have less code to investigate. In order to isolate unsafe code as much as possible, it's a good idea to -enclose unsafe code within a safe abstraction and provide a safe API. Parts of -the standard library are implemented as safe abstractions over unsafe code that -has been audited. This prevents uses of `unsafe` from leaking out into all the +enclose unsafe code within a safe abstraction and provide a safe API, which +we'll be discussing once we get into unsafe functions and methods. Parts of the +standard library are implemented as safe abstractions over unsafe code that has +been audited. This prevents uses of `unsafe` from leaking out into all the places that you or your users might want to make use of the functionality implemented with `unsafe` code, since using a safe abstraction is safe. @@ -73,8 +74,9 @@ we'll look at some abstractions that provide a safe interface to unsafe code. Way back in Chapter 4, we first learned about references. We also learned that the compiler ensures that references are always valid. Unsafe Rust has two new types similar to references called *raw pointers*. Just like references, we can -have an immutable raw pointer and a mutable raw pointer. Listing 19-1 shows how -to create raw pointers from references: +have an immutable raw pointer and a mutable raw pointer. In the context of raw +pointers, "immutable" means that the pointer can't be directly dereferenced and +assigned to. Listing 19-1 shows how to create raw pointers from references: ```rust let mut num = 5; @@ -92,9 +94,10 @@ references, these pointers may or may not be valid. Listing 19-2 shows how to create a raw pointer to an arbitrary location in memory. Trying to use arbitrary memory is undefined: there may be data at that -address, there may not be any data at that address, or your program might -segfault. There's not usually a good reason to be writing code like this, but -it is possible: +address, there may not be any data at that address, the compiler might optimize +the code so that there is no memory access, or your program might segfault. +There's not usually a good reason to be writing code like this, but it is +possible: ```rust let address = 0x012345; @@ -133,9 +136,8 @@ tried to create an immutable and a mutable reference to `num` instead of raw pointers, this would not have compiled due to the rule that says we can't have a mutable reference at the same time as any immutable references. With raw pointers, we are able to create a mutable pointer and an immutable pointer to -the same location, and change data through the mutable pointer while the -immutable pointer expects the data not to change, potentially creating a data -race. Be careful! +the same location, and change data through the mutable pointer, potentially +creating a data race. Be careful! With all of these dangers, why would we ever use raw pointers? One major use case is interfacing with C code, as we'll see in the next section on unsafe @@ -143,11 +145,12 @@ functions. Another case is to build up safe abstractions that the borrow checker doesn't understand. Let's introduce unsafe functions then look at an example of a safe abstraction that uses unsafe code. -### Calling an Unsafe Function +### Calling an Unsafe Function or Method The second operation that requires an unsafe block is calling an unsafe -function. Unsafe functions look exactly like regular functions, but they have -an extra `unsafe` out front: +function. Unsafe functions and methods look exactly like regular functions and +methods, but they have an extra `unsafe` out front. Bodies of unsafe functions +are effectively `unsafe` blocks. Here's an unsafe function named `dangerous`: ```rust unsafe fn dangerous() {} @@ -205,7 +208,7 @@ fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) { assert!(mid <= len); (&mut slice[..mid], - &mut slice[(len - mid)..]) + &mut slice[mid..]) } ``` @@ -229,7 +232,7 @@ error[E0499]: cannot borrow `*slice` as mutable more than once at a time | 5 | (&mut slice[..mid], | ----- first mutable borrow occurs here -6 | &mut slice[(len - mid)..]) +6 | &mut slice[mid..]) | ^^^^^ second mutable borrow occurs here 7 | } | - first borrow ends here @@ -362,7 +365,8 @@ languages to call Rust functions. Instead of an `extern` block, we can add the `extern` keyword and specifying the ABI to use just before the `fn` keyword. We also add the `#[no_mangle]` annotation to tell the Rust compiler not to mangle the name of this function. The `call_from_c` function in this example would be -accessible from C code: +accessible from C code, once we've compiled to a shared library and linked from +C: ```rust #[no_mangle] @@ -404,7 +408,7 @@ type, which is `&'static str` in this case. Only references with the `'static` lifetime may be stored in a static variable. Accessing immutable static variables is safe. Values in a static variable have a fixed address in memory, and using the value will always access the same data. Constants, on the other -hand, duplicate their data whenever they are used. +hand, are allowed to duplicate their data whenever they are used. Another way in which static variables are different from constants is that static variables can be mutable. Both accessing and modifying mutable static @@ -448,10 +452,10 @@ that data accessed from different threads is done safely. ### Implementing an Unsafe Trait -Finally, the last action we're only allowed to take within an `unsafe` block is -implementing an unsafe trait. We can declare that a trait is `unsafe` by adding -the `unsafe` keyword before `trait`, and then implementing the trait must be -marked as `unsafe` too, as shown in Listing 19-11: +Finally, the last action we're only allowed to take when we use the `unsafe` +keyword is implementing an unsafe trait. We can declare that a trait is +`unsafe` by adding the `unsafe` keyword before `trait`, and then implementing +the trait must be marked as `unsafe` too, as shown in Listing 19-11: ```rust unsafe trait Foo { diff --git a/second-edition/src/ch19-03-advanced-traits.md b/second-edition/src/ch19-03-advanced-traits.md index aa1d9cb0f..cb1bd7c92 100644 --- a/second-edition/src/ch19-03-advanced-traits.md +++ b/second-edition/src/ch19-03-advanced-traits.md @@ -185,10 +185,9 @@ While trait objects mean that we don't need to know the concrete type of the `graph` parameter at compile time, we do need to constrain the use of the `AGraph` trait in the `traverse` function by the concrete types of the associated types. If we didn’t provide this constraint, Rust wouldn't be able -to figure out which `impl` to match this trait object to. Think of it this -way: if we didn't define the associated types, and we had multiple implementations -of this trait for different associated types, there'd be no way to choose which -one of those implementations to use. +to figure out which `impl` to match this trait object to, because the +associated types can be part of the signatures of the methods that Rust needs +to look up in the vtable. ### Operator Overloading and Default Type Parameters @@ -197,10 +196,11 @@ specify the default type for a generic type. A great example of a situation where this is useful is operator overloading. Rust does not allow you to create your own operators or overload arbitrary -operators, but the operations listed in `std::ops` can be overloaded by -implementing the traits associated with the operator. For example, Listing -19-25 shows how to overload the `+` operator by implementing the `Add` trait on -a `Point` struct so that we can add two `Point` instances together: +operators, but the operations and corresponding traits listed in `std::ops` can +be overloaded by implementing the traits associated with the operator. For +example, Listing 19-25 shows how to overload the `+` operator by implementing +the `Add` trait on a `Point` struct so that we can add two `Point` instances +together: Filename: src/main.rs @@ -311,14 +311,13 @@ functionality of the trait without breaking the existing implementation code. ### Fully Qualified Syntax for Disambiguation -Rust cannot prevent a trait from having a method with the same name that -another trait's method has, nor can it prevent us from implementing both of -these traits on one type. We can also have a method implemented directly on the -type with the same name as well! In order to be able to call each of the -methods with the same name, then, we need to tell Rust which one we want to -use. Consider the code in Listing 19-27 where traits `Foo` and `Bar` both have -method `f` and we implement both traits on struct `Baz`, which also has a -method named `f`: +Rust cannot prevent a trait from having a method with the same name as another +trait's method, nor can it prevent us from implementing both of these traits on +one type. We can also have a method implemented directly on the type with the +same name as well! In order to be able to call each of the methods with the +same name, then, we need to tell Rust which one we want to use. Consider the +code in Listing 19-27 where traits `Foo` and `Bar` both have method `f` and we +implement both traits on struct `Baz`, which also has a method named `f`: Filename: src/main.rs @@ -427,6 +426,11 @@ on `Baz` and the `Foo` trait implemented on `Baz` in scope, we could call the `f` method in `Foo` by using `Foo::f(&b)` since we wouldn't have to disambiguate from the `Bar` trait. +We could also have called the `f` defined directly on `Baz` by using +`Baz::f(&b)`, but since that definition of `f` is the one that gets used by +default when we call `b.f()`, it's not required to fully specify that +implementation if that's what we want to call. + ### Supertraits to Use One Trait's Functionality Within Another Trait Sometimes, we may want a trait to be able to rely on another trait also being diff --git a/second-edition/src/ch19-04-advanced-types.md b/second-edition/src/ch19-04-advanced-types.md index eea5e1fd9..e0200c00a 100644 --- a/second-edition/src/ch19-04-advanced-types.md +++ b/second-edition/src/ch19-04-advanced-types.md @@ -292,6 +292,9 @@ that stores the size of the dynamic information. This leads us to the golden rule of dynamically sized types: we must always put values of dynamically sized types behind a pointer of some kind. + + While we've talked a lot about `&str`, we can combine `str` with all kinds of pointers: `Box`, for example, or `Rc`. In fact, you've already seen this before, but with a different dynamically sized type: traits. Every trait @@ -330,7 +333,7 @@ fn generic(t: &T) { ``` A trait bound on `?Sized` is the opposite of a trait bound on `Sized`; that is, -we would read this as '`T` may or may not be `Sized`'. This syntax is only +we would read this as "`T` may or may not be `Sized`". This syntax is only available for `Sized`, no other traits. Also note we switched the type of the `t` parameter from `T` to `&T`: since the From a969a88c97be1847aa753bbe8e33a00a72bf3af2 Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Thu, 13 Apr 2017 11:16:57 -0400 Subject: [PATCH 29/30] Clarify that was intentional --- second-edition/src/ch19-05-advanced-functions-and-closures.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/second-edition/src/ch19-05-advanced-functions-and-closures.md b/second-edition/src/ch19-05-advanced-functions-and-closures.md index 0c41e6558..260ea91dd 100644 --- a/second-edition/src/ch19-05-advanced-functions-and-closures.md +++ b/second-edition/src/ch19-05-advanced-functions-and-closures.md @@ -85,7 +85,8 @@ Because closures are represented by traits, returning closures is a little tricky; we can't do it directly. In most cases where we may want to return a trait, we can instead use the concrete type that implements the trait of what we're returning as the return value of the function. We can't do that with -closures, though; we're not allowed to use `fn` as a return type, for example. +closures, though. They don't have a concrete type that's returnable; we're not +allowed to use the function pointer `fn` as a return type, for example. This code that tries to return a closure directly won't compile: From 2d29c27a76454df4457dd86eb0505d20c70187dd Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" Date: Thu, 13 Apr 2017 11:44:18 -0400 Subject: [PATCH 30/30] Uhhh guess we haven't said vtable before --- second-edition/dictionary.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/second-edition/dictionary.txt b/second-edition/dictionary.txt index c12e89f8b..e61896ac3 100644 --- a/second-edition/dictionary.txt +++ b/second-edition/dictionary.txt @@ -341,6 +341,7 @@ variant's vers versa Versioning +vtable wasn WeatherForecast WebSocket