I wrote my first program in the late 70s so I’ve been learning new languages for quite a while. Your advanced videos are great because the pain seems to drive my old brain to become more plastic. I suppose its the masochistic method of understanding. Thank you so much for this resource Jon.
2:12:10 Automatically implementing Unpin for async blocks and functions that don't need pinning is probably something that could cause problems for API stability. If a library author exposes an async function that gets impl Unpin automatically because it doesn't currently have any self-references, if they want to change the implementation to something that isn't Unpin, that would be a breaking change.
That’s a pretty great point about API stability. This actually does cause problems with Send and Sync because they are automatically implemented for async blocks depending on the block’s content. So the API contract of an async function’s return type can change purely based on its body. I’ve encountered this before in a backend project where I used an itertools group_by function, which for some reason isn’t Send, in an async function. Didn’t hit any compiler errors while I was working on that crate, then when I tried to run the server there I got a gnarly error because the fully composed futures returned from the server’s router functions weren’t Send and that’s no bueno with tokio. And additionally, even if you don’t hold those !Send types across await points, the async block generated is still !Send. That’s what’ll really trip you up.
I think that an easier way to see why Pin is important is with a simple struct like struct Packet { skb: [u8; 512], header: &'skb [u8], body: &'skb [u8], } `header` and `body` are slices to bytes in `skb`. Using futures as an example of self-referential structs can be confusing, because they contain much more details.
Wow, what a guy. Enormous knowledge paired with understandable explanation. Now I know what it really means to be a programmer! Jon, thanks for the videos and your time. Unbelievable...
In 1:26:20, shouldn't this only be unsound if you actually called a _poll()_ on _x_ after you created it? Because if you pin and still _mem::replace()_ it, you didn't yet actually depended on that pinning right? btw you often explicitly inserted _poll()_ calls around some _mem::replace()_ after pinning, so I wondered if in this case you didn't? (Thx for the explanation!! XD)
Hey Jon! Always appreciate your videos hehe...question about your real-time compile messages(?) that you get in vim. What did you install? I'm using jetbrain rust plugin but you only get detailed error only when you compile
from my understanding, the only reason, why pinning is neccessary is, because it is forbidden to move a struct, that contains a pointer to a field of itself, because that move does not change the value of the pointer and so the pointer points to the old memory location ando nobody knows about its contents anymore. But if i used a relative pointer instead, would that get rid of the problem, because when i move the struckt with the (relative) pointer to a field of itself, the relative pointer now still points to the currect location, because the layout of the struct does not change. I think that I overlook someting so please clarify me
Ah, so, in the specific case where the target of the pointer is stored within the struct, that's fine, but in the general case, relative pointers are not sufficient. For example, consider a struct that holds a Vec (so a heap-allocated vector) and a type with pointers into that Vec. There's no relative pointer you can use there to make moves okay
@@XiaoZhang-x1a0 In the case of a Vec, relative pointers hurt rather than help because when you move the struct, the Vec's buffer stays in place on the heap, so relative pointers into it are no longer valid. If you used absolute pointers, mutating the Vec risks invalidating them, because it might reallocate or simply drop entries you're pointing to.
Good stuff, thanks for sharing. How do you know when you should "take" a value from an option rather than just unwrapping it. I know it's about ownership, but don't really understand why the necessity. Is this basically saying that the function call the taken value is passed to is a pass by value semantics, and can't take a borrow/ref so is required in this case, or is there more to it? I see this occasionally and always confused me. In this video, referencing code at 46:37 - self.value.take().unwrap() . Thanks for any thoughts.
You use take if you want to leave None behind in the Option that you're calling take on. unwrap() on the other hand consumers the Option, and makes it so that it cannot be used by anyone ever again.
Yeah, I've added a shock mount to my wish list to deal with some of those kinds of issues. The new mic also picks up keyboard sounds more than the old one did, which the shock mount should also help with.
I go through my setup in a decent amount of details here: ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-ycMiMDHopNc.html. My full configs are here: github.com/jonhoo/configs
as i understand it which maybe wrong your passing ownership. the actual self referential struct hasnt been moved. just the owning pointer/mutable reference has!