Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This explains fairly clearly a bit of rust that, while not the most obscure, is IMO fairly poorly explained in most resources I’ve found, and covers a lot of edges that are really useful to understand… I can keep digging and digging into the details presented. I find it absurd to say it’s like a freshman in college - I’d love to hire those freshman if you can point them out:

> Explain when pin is important in rust when dealing with asynchronous code, particularly when dealing with Box<dyn> types.

In Rust, Pin is a concept that is central to understanding the implementation of asynchronous operations. To explain why, let's first cover the basics.

Asynchronous tasks often involve future types that encapsulate a computation that will produce a value at some point in the future. These futures may need to poll other futures, which in turn might not be ready, causing them to be suspended and resumed later on. This suspension and resumption is where the Pin concept comes in.

By default, Rust allows moving types around in memory. However, when you have self-referential structures, like many of the future types in asynchronous programming, this can lead to problems. A future may hold a reference to another part of itself, and if the future is moved in memory, that internal reference may become invalid.

Pin is Rust's solution to this problem. If a type is "pinned" (wrapped in a Pin), it is a guarantee that the memory location of the value inside will not change. This means that you can safely keep references to different parts of the same structure because you know they won't move around.

Box<dyn Future> is a heap-allocated future object. Using Pin with Box<dyn Future> (i.e., Pin<Box<dyn Future>>) is common in async code because it guarantees that the future object will not be moved around in memory, making it safe to poll.

However, one should note that simply creating a Pin<Box<T>> does not mean that the T will stay put; it's also necessary to ensure that T does not move itself or its interior around. The contract of Pin is such that, once a value has been pinned, it is not allowed to move or have its memory invalidated until it is dropped. For a type to uphold these guarantees, it must be Unpin or ensure that all its operations maintain these guarantees. Most native Rust types are Unpin and therefore don't need to worry about these guarantees.

In short, Pin is important in Rust's async code as it provides a mechanism to safely handle self-referential structures, which are common in async programming. The Rust async ecosystem uses Pin to ensure that once a future begins executing (is polled), it will remain at a fixed location in memory until it has completed.



This is also a good demonstration of how it can be slightly wrong, or maybe misleading

> If a type is "pinned" (wrapped in a Pin), it is a guarantee that the memory location of the value inside will not change.

Pin is a wrapper around a pointer type and disallows moving the value that that pointer points at. You don’t pin a value, you pin a reference to a value. IMHO this sentence is unclear about that.

You sorta get the right vibes from the rest of the post, but there’s a major important concept in here it explains decently but also just wrong enough that you may internalize the incorrect thing.


Interestingly I read what it said as precisely what you said. But you’re right it elides the crucial nuance that the pointer is pinned, and “the value inside” can ambiguously be read as the type is a value and not a pointer to a value. I read “inside” to mean “inside the pinned pointer” but it can (maybe more accurately?) be read as inside the pin is a value.

I agree - it’s not a panacea devoid of human involvement. I think really importantly, whatever failings the technique has, it’s reflective of humanity itself, and humans are often slightly wrong or misleading. I think confusing it’s output with that of a deterministic program is wrong - it’s much more akin to the output of a human than not.

My argument though is it’s not a freshman in college level of human failings. It’s clarity is far beyond that on many subjects, and it’s facility to “understand” is remarkable for a human let alone a computer. The fact I can interrogate it and ask it the clarifying question you bring out is beyond useful - I can dig into the specific areas of confusion I have, and often if it’s wrong I’ll cop to it unlike a human oracle.

> Clarify then: If a type is "pinned" (wrapped in a Pin), it is a guarantee that the memory location of the value inside will not change.

When we say "the memory location of the value inside will not change," what we mean is that the value that the pointer points to is guaranteed to not move in memory. This is a critical guarantee when working with certain types of data, like self-referential structs or certain async code in Rust.

Let's take an example of Pin<Box<T>>. Here's what it means:

Box<T>: This is a heap-allocated instance of T. The Box is a smart pointer that points to this heap-allocated T. Pin<Box<T>>: This is a Pin wrapping the Box. This means that the T that the Box points to is pinned. This does not mean that the Box itself cannot be moved. The Box is a pointer, and pointers can be copied and moved freely. But because we have used Pin, the actual heap-allocated T that it points to cannot be moved. This is what we mean when we say "the memory location of the value inside will not change." We're talking about the T, not the Box.

This is important when the type T has self-references, because moving T in memory would invalidate those self-references. By using Pin, we ensure that T stays put, and those self-references remain valid. This is a key part of the design of Rust's async programming model.

However, this guarantee is only upheld as long as the type T does not implement Unpin, which is a marker trait that indicates that it is safe to move a type out of a Pin. Types that are not Unpin uphold the guarantee that they cannot be moved out of the Pin once they are placed in it.


> I think really importantly, whatever failings the technique has, it’s reflective of humanity itself, and humans are often slightly wrong or misleading.

Absolutely, and I don't think every explanation has to be perfect to be useful, hence me saying "you gotta watch out" and not "fuck this tool, nobody should use it."




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: