-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Chapter 15: Smart pointers #407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just checked for typos.
src/ch15-00-smart-pointers.md
Outdated
That is, they have extra capabilities that references don't, hence the "smart" | ||
nickname. | ||
|
||
You've already encounted a few smart pointers in the book, we didn't call them |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/encounted/encountered
src/ch15-00-smart-pointers.md
Outdated
nickname. | ||
|
||
You've already encounted a few smart pointers in the book, we didn't call them | ||
that by name, though. For example, in a certian sense, `String` and `Vec<T>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/certian/certain
src/ch15-00-smart-pointers.md
Outdated
Most of this should look familliar: a struct, a trait, a main function. There | ||
is one tricky bit: like we said in chapter 13 on Iterators, the `type Target = | ||
T;` syntax is "associated types", which is covered in Chapter 20. Don't worry | ||
about it too much, it is a slightly different way of delcaring a generic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/delcaring/declaring
src/ch15-00-smart-pointers.md
Outdated
The first two are the same, except for mutability: if you have a `&T`, and | ||
`T` implements `Deref` to some type `U`, you can get a `&U` transparently. Same | ||
for mutable references. The last one is more tricky: if you have a mutable | ||
reference, it will also coerece to an immutable one. The other case is _not_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/coerece/coerce
src/ch15-00-smart-pointers.md
Outdated
`T` implements `Deref` to some type `U`, you can get a `&U` transparently. Same | ||
for mutable references. The last one is more tricky: if you have a mutable | ||
reference, it will also coerece to an immutable one. The other case is _not_ | ||
possible though: immutable references will never coerece to mutable ones. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/coerece/coerce
src/ch15-00-smart-pointers.md
Outdated
```rust,ignore | ||
enum List<T> { | ||
Cons(T, List<T>), | ||
Nil, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok soooo I know it's usually called Nil
in lisp, but wdyt about calling this variant Empty
instead, to avoid confusion with "i thought Rust didn't have nil?!?!" Just a thought, I don't feel super strongly about this.
src/ch15-00-smart-pointers.md
Outdated
type `SomeEnum<T>`, so we need to figure out how much memory a `SomeEnum<T>` | ||
needs. Let's look at..." into infinity. In order to figure out how much memory | ||
`SomeEnum::A` needs, we need to figure out how much memory `SomeEnum::A` needs. | ||
It's impossible to know! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
omg i got the point after like 2 iterations of this :P
src/ch15-00-smart-pointers.md
Outdated
make `main::List` representable | ||
``` | ||
|
||
Because a `Box<T>` is a pointer, we always know what size it is: a `usize`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've talked about usize
being the type of an index, but I don't see that we've explicitly said "usize
is how big pointers are"-- do you think this would be a good spot to say that? if so, I'm happy to add something in my pass through :)
src/ch15-00-smart-pointers.md
Outdated
``` | ||
|
||
We use `*y` to access the thing that `y` refers to, rather than `y` itself. | ||
Here's an example of overloading `*` using `Deref`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would we want to overload Deref like this? When shouldn't we overload Deref?
src/ch15-00-smart-pointers.md
Outdated
### `Deref` coercions. | ||
|
||
There's one other trick with `Deref`: it's one place where Rust will do | ||
automatic coercions. Consider this code: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm I wouldn't call Deref the "place" where Rust will do automatic coercion-- the place is function arguments, as opposed to uhh what are the places that people expect automatic deref and it doesn't happen? I forget.
I would reword this as "There's one other trick with Deref
and function calls: Rust will do automatic coercion using Deref on arguments passed to functions" or similar, wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check the RFC on this
src/ch15-00-smart-pointers.md
Outdated
|
||
The ability to run some code when something goes out of scope is very powerful. | ||
For example, here's something that looks similar to a box. This code won't | ||
_work_, but it illustrates the concept: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would reword "This code won't work" as "here's some pseudocode that assumes we have functions allocate_memory
and free_memory
that implement a custom memory allocator" (is that the only reason it won't work?)
src/ch15-00-smart-pointers.md
Outdated
So first, we declare `MyBox<T>` to have a reference to some data. We can't use | ||
actual `&T` references for this, we'd use `*const T`, but we won't talk about | ||
*raw pointer*s until Chapter 20. So for now, pretend that this works; it's the | ||
same idea. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hnnngg i see, ok. This seems like a lot to handwave over :-/ What if we show a socket that we call code to close on Drop
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed that an example that a) doesn't work, b) looks forward to chapter in the future is maybe a bit much. Something that's a little more real-world and builds on what they know (socket sounds good, yeah) sounds like it might be a better fit here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had some time so I decided to review what you've got so far, I've got a few questions that if you clear up for me what your intentions are, I can probably fix the text when I take my pass through :)
src/ch15-00-smart-pointers.md
Outdated
|
||
You've already encounted a few smart pointers in the book, we didn't call them | ||
that by name, though. For example, in a certian sense, `String` and `Vec<T>` | ||
are both smart pointers. They own some memory, and allow you to manipualate it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/manipualate/manipulate
src/ch15-00-smart-pointers.md
Outdated
12: it manages a file handle that the operating system gives us. | ||
|
||
Given that this is a design pattern in Rust, this chapter won't cover every | ||
smart pointer that exists. Many libraries will build their own, as well, and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/own, as well/own as well
fdb45a9
to
ac61cbd
Compare
@carols10cents @jonathandturner this is ready for a review! 🎊 |
oh, for the cycle thing, i took an example from here https://www.reddit.com/r/rust/comments/34dy5z/reference_counted_cycles/cqtsbkt/?st=izhduq8s&sh=f47a5d42 we should give them some kind of shout-out |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sections up to RefCell feel pretty good with some nits and some areas for polish.
RefCell and some of the sections after feel like they need a tighter narrative. There feels like a fair amount of "this stuff is hard, and complicated" but I think that we could lead the reader through understanding how this stuff works so that they can write their code successfully.
I think we could tighten it up a bit. I mention not having the Cell section at all, which I think could work since we don't use it much after that section.
I think it's important to talk about cycles, though something like Rc<RefCell> feels perhaps like it's something also we could mention but not spend as much time on. Yes, some code definitely uses it, but I'm thinking about complexity budget for this chapter and how much the reader can hold in their heads. If we remove a few things from the last few sections and focus on working with the patterns like interior mutability and working around cycles, we're doing pretty good, I think.
src/ch15-00-smart-pointers.md
Outdated
So what are smart pointers, anyway? Well, we've learned about references in | ||
Rust. "Pointer" is a generic term for something like a reference, that is, | ||
pointers "point at" something else. References are a kind of pointer that only | ||
borrows data; in many cases, smart pointers *own* the data that they point to. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs better linking phrase to set up comparison.
src/ch15-00-smart-pointers.md
Outdated
Heap allocated | ||
Express Ownership of a heap allocated thing | ||
Just like any value that has ownership, when a box goes out of scope, it will | ||
be dealloacated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/dealloacated/deallocated
src/ch15-00-smart-pointers.md
Outdated
|
||
The three situations to use Box | ||
It turns out that putting a single value on the heap isn't very useful, so you | ||
won't use boxes very often. When do you need a box? When you want to ensure |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you mean something like "so you won't use boxes by themselves very often." to contrast to your next sentence.
src/ch15-00-smart-pointers.md
Outdated
The compiler will look at this type, and say "How much memory do I need to | ||
allocate for a value of `SomeEnum`? Let's look at `A`. Well, it has a value of | ||
type `Box<SomeEnum<T>>`, and we know that a box always has the size of a | ||
`usize`. Then, let's look at `B`. It doesn't save a value, so we don't need any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this, but how does the reader know this?
You just said SomeEnum leads to recursion, how does adding one more level of abstraction break the loop? Don't I have to know the size of SomeEnum to know the Box of it? How is Box different?
I mean, you don't have to go into too much detail, but you're asking them to trust you here but I think there are probably a couple sentences mixing that lets them follow you the whole way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm adding this text just before the enum definition that uses Box
, I think this will resolve it, please comment if not (and feel free to wait til i push up my changes to see them in context):
Because a
Box<T>
is a pointer, we always know what size it is: ausize
, which is the size of a pointer. The value of theusize
will be the address of the heap data. The heap data can be any size, but the address to the start of that heap data will always fit in ausize
.
The part you commented on now reads (also changed this example to use List
/Cons
/Nil
):
The
Cons
variant will need the size of whateverT
is, plus the space to store ausize
, since a box always has the size of ausize
.
thus removing the "we know" part.
src/ch15-00-smart-pointers.md
Outdated
value: T, | ||
} | ||
|
||
impl<T> Deref for DerefExample<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that we go into explaining Deref. It feels like it helps unlock their ability to learn Rust more deeply on their own.
I was wondering if we wanted to show the Deref trait so they know what you're implementing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't covered associated types at this point yet, so we kind of want to gloss over that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also it's basically just this. I'm going to add a sentence before this example that says "the Deref trait requires implementing the deref
method that borrows self and returns a reference to whatever part of the data you want returned" or similar.
src/ch15-00-smart-pointers.md
Outdated
|
||
1. We use `5` in sample one, but `Cell::new(5)` in sample two. | ||
2. We use `=` in sample one, but `set` in sample two. | ||
3. `five` is mutable in sample one, but not in sample two. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These three points don't help me understand what interior mutability is or why you would want it. I think the reader can assume that some patterns will be syntactically different. It feels like the first thing the reader will want is "okay what is this interior mutability thing?"
src/ch15-00-smart-pointers.md
Outdated
} | ||
``` | ||
|
||
There's a lot going on here. Fundamentally, `Cycle` is a type that contains an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably don't need "Fundamentally,"
src/ch15-00-smart-pointers.md
Outdated
``` | ||
|
||
There's a lot going on here. Fundamentally, `Cycle` is a type that contains an | ||
`Rc<Cycle>`. This means that we can have a 'referene cycle', hence the name of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/referene/reference
src/ch15-00-smart-pointers.md
Outdated
|
||
Now, as you can see, doing this is very hard. To be honest, your authors had to | ||
look up previous discussions of an example to get this right. But it can | ||
happen, and the way that it happens is reasonably obvious: If you have an |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/If/if
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i reworded and mooted
src/ch15-00-smart-pointers.md
Outdated
|
||
Now let's talk about concurrency, and some smart pointers that can be used | ||
with multiple threads. | ||
Next, let's talk about concurrency in Rust. We'll even learn aobut a few new |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/aobut/about
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some thoughts.
Also +1 to most of Jonathan's comments.
src/ch15-00-smart-pointers.md
Outdated
A(SomeEnum<T>), | ||
B, | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happened to List<T>
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i like you, literally just said the same thing to steve before reading this
src/ch15-00-smart-pointers.md
Outdated
enum List<T> { | ||
Cons(T, List<T>), | ||
Nil, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the following examples, would make things simpler to not use generics?
src/ch15-00-smart-pointers.md
Outdated
{ | ||
let y = &mut x; | ||
|
||
*y += 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example should probably be a use of Deref
, not DerefMut
.
Alternatively, it could have one of each and be referred to when DerefMut
is mentioned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, good point. This example doesn't actually exist in chapter 4 currently; we're also going to go back there and add something along these lines and then rework this example so that it's actually referring back to something.
src/ch15-00-smart-pointers.md
Outdated
parameter. | ||
|
||
if you look at the `assert_eq!`, we're comparing `'a'` to `*example`: the `*` | ||
is what calls `Deref::deref`. And in the implementation, we can see that we |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it would help to mention that
(a) *example
is turned into *Deref::deref(&example)
.
(b) *
on references is built in.
src/ch15-00-smart-pointers.md
Outdated
string slices! This means that `&String` will automatically coerce to a slice | ||
of the full string. | ||
|
||
There's also a `DerefMut` trait for overriding `*` on `&mut T`s in the same |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overriding *
in assignments. This is where the example at the start of the previous could be used.
src/ch15-00-smart-pointers.md
Outdated
``` | ||
|
||
The difference between `Cell<T>` and `RefCell<T>` mirrors the difference | ||
between types that are `Copy` and types that are not; if we don't need the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point here is then that with Copy
types we can pass around copies to avoid references.
src/ch15-00-smart-pointers.md
Outdated
call interior mutability a "pattern" is that it's not really a language | ||
feature, it's a design pattern for libraries. More specifically, it's a pattern | ||
that uses `unsafe` code inside to bend Rust's usual rules. We haven't yet covered | ||
unsafe code in-depth, we will in Chapter 20. Luckily for us, you don't have to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if Chapter 20 can hope to cover unsafe code "in-depth".
src/ch15-00-smart-pointers.md
Outdated
|
||
### The Interior Mutability Pattern | ||
`Weak<T>` is exactly like `Rc<T>`, except that its reference count doesn't, well, | ||
count when determining if something should be freed. You can turn an `Rc<T>` into |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dropped, not freed.
src/ch15-00-smart-pointers.md
Outdated
### The Interior Mutability Pattern | ||
`Weak<T>` is exactly like `Rc<T>`, except that its reference count doesn't, well, | ||
count when determining if something should be freed. You can turn an `Rc<T>` into | ||
a `Weak<T>` with the `Rc::downgrade` associated method, which takes an `&Rc<T>` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
associated function
src/ch15-00-smart-pointers.md
Outdated
So in this example, when we call `oh_no.clone()`, we increment the count to two. | ||
But when we pass that clone to `downgrade`, that count goes down again, to one. | ||
Now, at the end of the function, when `oh_no` goes out of scope, it reduces the | ||
count from one to zero, and the memory is freed. Success! We've broken the cycle. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, being accurate with drop/free would make this more complicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dropping the T
value in the Rc<T>
and freeing the underlying memory are separate steps.
That said, I don't think that this matters any more (here).
To say a bit more:
Memory is freed once there are no Rc<T>
or Weak<T>
pointing to that memory (to keep the reference counts around), so I thought that it's clone
that actually frees the memory. But in this case clone
is inside the Rc
so oh_no
frees the memory.
Ok @steveklabnik @matthewjasper @jonathandturner @parir @gypsydave5, I think this is ready-ish for rereview. There are still a few TODOs, but the main content is done and I'd love your feedback. The TODOs I have are:
|
src/ch15-06-reference-cycles.md
Outdated
To create `Weak<T>` values, we call the `Rc::downgrade` associated function, | ||
which takes an `&Rc<T>` as an argument, and gives a `Weak<T>` back. | ||
|
||
Listing 15-16 shows a `main` method where we're trying to create `a` and `b` lists that point to each other, similarly to what we did in Listing 15-14, but this time we won't have a reference cycle and the values will be dropped when they go out of scope at the end of `main`: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs wrapped
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interior mutability section looks solid! I left just a few suggestions/clarifications, as well as some notes on other parts of the chapter.
src/ch15-00-smart-pointers.md
Outdated
sense, `String` and `Vec<T>` from Chapter 8 are both smart pointers. They own | ||
some memory and allow you to manipulate it, and have metadata (like their | ||
capacity) and extra capabilities or guarantees (`String` data will always be | ||
valid UTF-8). Another good example is `File`, which we used for our I/O project |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think File
is probably not a good example, if you're treating Deref
as crucial (which I think you should).
src/ch15-00-smart-pointers.md
Outdated
frequently in Rust, this chapter won't cover every smart pointer that exists. | ||
Many libraries have their own and you may write some yourself. The ones we | ||
cover here are the most common ones from the standard library: `Box<T>`, | ||
`Rc<T>`, and `RefCell<T>`. Along the way, we'll also cover: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that RefCell
itself isn't a smart pointer, but the access to data it provides (Ref
and RefMut
) is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well poop. Uh. Idk what to do about this now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like..... should we make RefCell/interior mutability into its own chapter...? Frame RefCell as something that's also useful to use with smart pointers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well so RefCell
does involve smart pointers -- the Ref
and RefMut
it gives you when you borrow are smart pointers. I think it's fine to go in this chapter, but the text just needs a little bit more precision.
It's worth talking about Ref
and RefMut
in more detail anyway, since understanding what happens when you drop them can help you understand how RefCell
works (which I think is important for grokking "deep Rust").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this book meant to help people understand "deep Rust" though, or is that more for the nomicon....?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh, let me try a different argument. I personally find understanding how RefCell
works really helpful for getting another perspective on borrow checking, and for seeing how various pieces (like Deref
and Drop
) come together. Obviously it's your call what makes the most sense here in the book. But at the very least, this section can be fixed up by just saying that when you call borrow
what you get back is a smart pointer, Ref
, which tracks the borrowing dynamically, and for which drop
releases the borrow dynamically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be super clear: I think the best definition of a "smart pointer" in Rust is just something that implements Deref
. The RefCell
type doesn't itself, but it crucially depends on its sibling types Ref
and RefMut
, which do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok @aturon, in this commit I tried to change the text to be more accurate but not go off into the weeds too much. Wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aturon oops and this commit too, forgot to hit save
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect!
src/ch15-01-box.md
Outdated
|
||
The most straightforward smart pointer is a *box*, whose type is written | ||
`Box<T>`. Boxes allow you to put a single value on the heap, and the pointer to | ||
that value lives on the stack. (We talked about the stack vs. the heap in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pointer to the value doesn't necessarily live on the stack; you can put Box<T>
values themselves on the heap.
src/ch15-03-drop.md
Outdated
|
||
The other trait that's important to the smart pointer pattern is the `Drop` | ||
trait. `Drop` lets us run some code when a value is about to go out of scope. | ||
This is especially useful for smart pointers that manage a resource as opposed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is another case where you're talking about things like File
as smart pointers, which isn't right (they don't implement Deref
). You should make clear that Drop
is a more general concept, that happens to almost always be used when building a smart pointer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sooooo do you think the WebSocket
example in this section should be changed then? If so, what would be a better example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope! You just need to be more clear that the section is a digression about Drop
, which is more general than smart pointers. Alternatively, the material on Drop
could be moved into an earlier chapter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like either of those options :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, let me recommend something more specific. Change:
This is especially useful for smart pointers that manage a resource as opposed to those that manage memory: often resources like files or network connections need to be closed when our code is done with them.
to:
Smart pointers perform important cleanup when being dropped, like deallocating memory or decrementing a reference count. More generally, data types can manage resources beyond memory, like files or network connections, and use
Drop
to release those resources when our code is done with them.
@@ -0,0 +1,238 @@ | |||
## `RefCell<T>` and the Interior Mutability Pattern | |||
|
|||
*Interior mutability* is a design pattern in Rust for allowing you to mutate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This opening sentence is a bit problematic; it's not that you can mutate immutable data, it's that you can mutate data even though it's behind a shared reference, which would normally prevent mutation.
src/ch15-05-interior-mutability.md
Outdated
*Interior mutability* is a design pattern in Rust for allowing you to mutate | ||
data that's immutable. The interior mutability pattern involves using `unsafe` | ||
code inside a data structure to bend Rust's usual rules around mutation and | ||
borrowing. We haven't yet covered unsafe code, we will in Chapter 20. The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it'd be good to add here: the pattern is used when you can somehow ensure that the rules about borrowing will be followed at runtime, even though the compiler can't prove that to itself.
|
||
### `RefCell<T>` has Interior Mutability | ||
|
||
Unlike `Rc<T>`, the `RefCell<T>` type represents single ownership over the data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nikomatsakis, do you have a good explanation for the name Cell
?
src/ch15-05-interior-mutability.md
Outdated
Static analysis, like the Rust compiler performs, is inherently conservative. | ||
That is, if Rust accepts an incorrect program, people would not be able to | ||
trust in the guarantees Rust makes. If Rust rejects a correct program, the | ||
programmer will be inconvenienced, but nothing catastrophic can occur. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably want to add: it's not possible for a static analysis to have perfect precision (maybe with a link to the halting problem?) Otherwise it's not clear why it's inherently conservative.
src/ch15-05-interior-mutability.md
Outdated
|
||
1. When you know that the borrowing rules are respected, but when the compiler | ||
can't understand that that's true. | ||
2. When you need interior mutability. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would drop the second of these situations, because it follows from the first.
src/ch15-05-interior-mutability.md
Outdated
|
||
Here, we've created a new `RefCell<T>` containing the value 5. We can get an | ||
immutable reference to the value inside the `RefCell<T>` by calling the `borrow` | ||
method. More interestingly, we can get a mutable reference to the value inside |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's hard for the reader to see the really key point here: the fact that you can do this with just a &
pointer to data
. Maybe you can break out the body of main into a function that takes &RefCell<i32>
to drive this home.
src/ch15-06-reference-cycles.md
Outdated
`weak_count` of references to an `Rc`. When an `Rc` goes out of scope, the | ||
inner value will get dropped if the `strong_count` is 0, even if the | ||
`weak_count` is not 0. When we attempt to use a `Weak<T>` reference, we'll get | ||
an `Option<T>` that will be `Some` if the `Rc` value has not been dropped yet, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think 'attempt to use a Weak' is a bit ambiguous. I think you mean: to be able to use [get the value from] a Weak<T>
, it first has to be upgraded to an Option<Rc<T>>
[that will be some ...].
Some thoughts on the
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are hopefully my last comments.
src/ch15-05-interior-mutability.md
Outdated
@@ -162,16 +180,20 @@ Well, remember when we said that `Rc<T>` has to store immutable data? Given | |||
that `RefCell<T>` is immutable, but has interior mutability, we can combine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One last (I think) case of 'immutable data'.
src/ch15-03-drop.md
Outdated
always used when implementing smart pointers. | ||
|
||
In some other languages, we have to remember to call code to free the memory or | ||
resource every time we finish using an instance of a smart pointer. If we |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is 'smart pointer' the right way refer to things in other languages?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is in C++, and other similar kinds of languages, yeah
i hope so too omg i'm so sick of this chapter |
I cannot for the life of me get the arrow currently pointing from a -> 10 to point from 10 -> a instead, dir=back has no effect, changing the order makes the whole graph go kablooey, i even tried hand-editing the svg and i cannot
I'm going to ship this to nostarch and explain that the one diagram isn't quite what we want but we're working on fixing it. I'm expecting to have to rearrange and reword lots of stuff when we get comments from nostarch, so if anyone has any comments/suggestions/feedback between now and then, please open a new issue and we'll take care of it then! Thank you to everyone who helped on this chapter! ❤️ |
This isn't done yet, but it's a start.