Pre-RFC: Allow self parameter be any type


#1

Created from PR.

Motivation

  • It allows to avoid required wrapper structs. It is ugly and wrapper structs don’t derive traits automatically.

    Before:

use std::cell::UnsafeCell;
use std::rc::Rc;

struct Entity {
}

impl Entity {
    fn process(&mut self) {
    }
}

#[derive(Clone)]
struct EntityWrapper(Rc<UnsafeCell<Entity>>);

impl EntityWrapper {
    fn entity_mut(&self) -> &mut Entity {
        unsafe {
            &mut *self.0.get()
        }
    }
}
After:
use std::cell::UnsafeCell;
use std::rc::Rc;

struct Entity {
}

impl Entity {
    fn process(&mut self) {
    }

    fn entity_mut(self: &Rc<UnsafeCell<Self>>) -> &mut Self {
        unsafe {
            &mut *self.get()
        }
    }
}
  • Allows to express unavailable right now things. Write GC-like code without GC.
use std::cell::UnsafeCell;
use std::rc::Rc;

struct Sound {
}

impl Sound {
    fn play(&self) {
        // Implementation details
    }

    fn play_with_continuation<F>(&self, continuation: F)
        where F: FnOnce()
    {
        // Implementation details
        continuation();
    }
}

struct Game {
    first_sound: Sound,
    second_sound: Sound,
}

impl Game {
    fn mut_self(self: &Rc<UnsafeCell<Self>>) -> &mut Self {
        unsafe {
            &mut *self.get()
        }
    }

    fn play_sounds(self: &Rc<UnsafeCell<Self>>) {
        self.mut_self().first_sound.play_with_continuation(move || {
            self.mut_self().second_sound.play();
        });  
    }
}

Detailed design

Allow self be any type that parameterized with Self:

* Rc<Self>.
* Iterator<Item=Self>.

Drawbacks

  • More complex method resolving strategy.
  • Requirement to make improvement to rustdoc.

Alternatives

Don’t do it.

Unresolved questions

  • What to do with cases like:
    • Disallow.
    • Allow, but require use UFCS.
impl SomeStruct {
    fn len(self: &Vec<Self>) -> usize {
        // Implementation details
    }
}

#2

To quote myself, from the PR:

The guarantee needs to be stronger, in order to be able to implement this in a meaningful way. Last time I looked into this, Deref<Target=Self> seemed to be enough for inherent methods. That means that the Self type is found when doing recursive dereferences of typeof self.

Trait methods, however, if they can be used in trait objects, require that typeof self is some X<Self> where X<Self>: CoerceUnsized<X<Trait>> where X<Self> and X<Trait> are structurally identical down to a matching pointer/reference to Self, and Trait, respectively. Otherwise, associated types could be used to convert a pointer into a reference, in safe code.

cc @nikomatsakis


#3

@eddyb I didn’t understand you. Should I?