How is it even possible? self type's is not Self but Pin<&mut Self>

In other languages and in rust itself, the first argument of a method (not a function) is the self keyword, which refers to the instance of the object/struct etc.. that calls the method through the use of the dot symbol followed by the method's name. And in this case, the type of the struct is its type. To be clear, I meant that the first argument of a method is always self (the instance) which by definition is the instance of type Self.

And I might be confusing between different concepts, but I can't wrap my head around the poll method's definition.

Here's the struct definition:

    struct AsyncFuture {
        fut_one: FutOne,
        fut_two: FutTwo,
        state: State,
    }

Here is the code that implements future.

    impl Future for AsyncFuture {
        type Output = ();

        fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
         /*...*/
        }
    }

It means that the future struct 'AsyncFuture' needs first to be wrapped inside a Pin before being allowed to call the method poll?

let fut = AsyncFuture {/../}; //hypothetical code

let pinned_fut = Pin::new(fut); //this code might not be correct I am just trying to understand the concept.

And only then, can call its implemented method (trait) pinned_fut.poll(...)

I understand that the code is generated but still, I guess that the generated code compiles as rust code and thus is valid syntax.

Thank you in advance

Edit:
Forgot to mention the link to the documentation from where I took the above code snippets.

Thinking out loud, I feel like it might just be a way to introduce another constraint on how the future trait's method 'poll' should be called by preventing a any type that implements future to directly call it and force it to be wrapped inside a Pin before it can basically make any progress by invoking its poll method.

Yes. And you might be wondering why. The easy response is that Pin asserts that the wrapped type's pointee (in this case, the pointee of &mut Self) won't change memory addresses. It's possible with unsafe code to violate this, and as such, in order to run async code, one needs to guarantee that the pointee won't move in the address space. Rust is really awesome because it can enforce certain guarantees (within the context of safe code), and throw compile-time errors if those guarantees aren't provided.

1 Like

Thanks, it makes sense now. And also, I am just scratching the surface of what rust can do and it is a really subtle language. Each concept requires some time (and a lot of thought process) to digest.

1 Like

Just in case that isn't clear, Pin is pretty hard to understand in detail (lots of subtleties), and rather unimportant for rust beginners. There's technical reasons based on how async works internally for why it appears in the Future trait. You should probably focus on other things first ^^

In case you're wondering how and why self can be such a complex type, you can find the details in the reference

If the type of the self parameter is specified, it is limited to types resolving to one generated by the following grammar (where 'lt denotes some arbitrary lifetime):

P = &'lt S | &'lt mut S | Box<S> | Rc<S> | Arc<S> | Pin<P>
S = Self | P

Shorthand syntax can be used without specifying a type, which have the following equivalents:

Shorthand Equivalent
self self: Self
&'lifetime self self: &'lifetime Self
&'lifetime mut self self: &'lifetime mut Self

Note : Lifetimes can be, and usually are, elided with this shorthand.

5 Likes

Read several times about 70% of the rust book and I missed this part. The way I read the book is by reading it a first time and understanding maybe 10% of it and then rechecking each section in detail when I am stuck or don't understand a specific concept. And I guess I missed this part, thank you for your guidance.

I would like to apologize to the previous person that answered but the exact answer is this one. Especially this part:

P = &'lt S | &'lt mut S | Box<S> |  Rc<S> | Arc<S> | Pin<P>
S = Self | P

Make sure you don't confuse the rust book with the rust reference though (different books). The reference contains some details that the rust book doesn't even mention. I don't actually know about this particular case whether the rust book explains it or not.

1 Like

Yes my bad, you are right. Thank you again.

Also, by looking at the reference's definition, I realized that Pin is just another type of pointer so it makes perfect sense now. The struct AsyncFuture is not 'altered' or anything, it is just the way to access it, in this case through the Pin pointer (and not through a simple reference &) that's specified in the method definition.

Sticking with the rust book is good though since everything in there is relevant to know, and it is actually structured to be read through from start to finish without getting boring or too detailed. Unlike the reference which is all about details and completeness and structured for looking things ult. There are a bunch of other books to know about though like the ones you can find here

or even some of those only listed here

https://github.com/sger/RustBooks

2 Likes

Thanks! will check them out.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.