Search code examples
rusttraitsself-type

Why use Self instead of the type's name in a concrete implementation?


The documentation for Add gives the following example:

use std::ops::Add;

#[derive(Debug, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Add for Point {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Self {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

Why did the author of the docs use Self here, instead of mentioning Point by name? Is there a technical difference, or is it purely for style points?


Solution

  • There are two main reasons:

    • flexibility. If you decide to change the name of your type, it's one less place to update.
    • conciseness. Self is shorter than MyType or SomeOtherType and especially ThisTypeWithGenerics<'a, 'b, A, String>.

    Is there a technical difference

    Yes and no, depending on how you look at it. Self is the type that has been "completely filled in" with regards to generics. This is relevant in cases like this:

    struct Container<T>(T);
    
    impl<T> Container<T> {
        fn replace<U>(self, new: U) -> Self {
            Container(new)
        }
    }
    
    error[E0308]: mismatched types
     --> src/lib.rs:5:19
      |
    3 | impl<T> Container<T> {
      |      - expected type parameter
    4 |     fn replace<U>(self, new: U) -> Self {
      |                - found type parameter
    5 |         Container(new)
      |                   ^^^ expected type parameter `T`, found type parameter `U`
      |
      = note: expected type parameter `T`
                 found type parameter `U`
      = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound
      = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
    

    Self is the complete type Container<T>, not the type constructor Container. This can cause hard-to-understand errors.

    See also: