About the Box<Self> thing

I'm reading the Rust Book, and in chapter 17.3 I find something a bit weird.
The use of self:Box<Self>.

The explanation it gives it's something like this:
"This syntax means the method is only valid when called on a Box holding the type."

I don't seem to wrap my head around what this actually means.

A little help please? :smiley:

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    // --snip--
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);
    }

    pub fn content(&self) -> &str {
        ""
    }

    pub fn request_review(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review())
        }
    }
}

trait State {
    fn request_review(self: Box<Self>) -> Box<dyn State>;
}

struct Draft {}

impl State for Draft {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        Box::new(PendingReview {})
    }
}

struct PendingReview {}

impl State for PendingReview {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

fn main() {}

Huh, that's interesting. It's qualifying that the form of self (the instance of the variable the function is called on / passed) must be its own type (Self) within a Box; thus of type Box<Self>.

I'm guessing this is also playing around with auto-dereferencing for Boxes and/or the auto-dereferencing which occurs on a variable when calling one of its implemented functions. Such as:

let x = &object; // Make x a reference to an arbitrary variable
x.function();
// looks nice, but syntactically Rust converts it first to:
(*x).function();

I hope that's at least a start towards understanding it.

1 Like

It kind of does help a lot.
So, what you're saying is...

Lets say that the signature was different and allowed more generic types to be used (Signature to change):

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

We could theoritically make sure to only request_review in the structures in which self is inside a box?

Normally, the self argument is special, because you don't need to declare its type, as long as it is one of these three supported types:

  • fn foo(self) is short for fn foo(self: Self)
  • fn foo(&self) is short for fn foo(self: &Self)
  • fn foo(&mut self) is short for fn foo(self: &mut Self)

fn foo(self: Box<Self>) doesn't have a special short-hand form, but otherwise it is just like the above.

3 Likes

Thanks for the response.
So that means that self is of type Box, we're taking it as one.

BTW you can do all sorts of things with self, for example Tokio Semaphore has acquire_owned() defined like:

pub async fn acquire_owned(
    self: Arc<Self>
) -> Result<OwnedSemaphorePermit, AcquireError>

Which requires the semaphore to be wrapped in Arc. Otherwise the method can't be used.

1 Like

impl Type {...} is just like mod path {...}.
Inside impl then defines Self to be alias for Type.
When the first parameters of a function is self it can then be called using the dot operator after some variable. As @mbrubeck shows self type can be chosen from a few alternatives.

1 Like

Tokio is the famous http library of Rust, right?
I'm genuinely impressed how cool this community is xD.

Another cool example can be found in the stdlib and is the Future trait:

pub trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

tokio is a runtime for writing async programs. You might confuse it with actix, which runs on top of tokio.

See also the nightly #[arbitrary_self_types] feature and RFC 2362: Custom Self types. The RFC was postponed after Rc, Arc, and Pin became receivers, so it's hard to say if or when the general case will regain momentum.

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.