Unsized receivers
Nobody has claimed this yet.
- Dominant language
- Markdown
- Stars
- 6.6k
- Forks
- 1.7k
- Avg merge
- 16h 14m
- Merged PRs (30d)
- 1
Description
Proposal
Allow receivers automatically "unsize" self. For example, given:
trait Trait {}
struct View<'a>(&'a mut Wrapper<Trait + 'a>);
struct Wrapper<A: ?Sized + Trait>(A);
Desugar:
impl<T: ?Sized + Trait> Wrapper<T> {
// Unsizing receiver
fn view<'b>(self: &'b mut Wrapper<Trait + 'b>) -> View<'b> {
View(self)
}
}
To:
impl<T: ?Sized + Trait> Wrapper<T> {
fn view<'b>(&mut self) -> View<'b>
where Self: Unsize<Wrapper<Trait + 'b>>
{
View(self as &mut Wrapper<Trait + 'b>)
}
}
Motivation
This can be useful for achieving object safety. That is, given the Self: Unsize<...> bound, one can call view on either *&mut Wrapper<T: Trait> or &mut Wrapper<Trait>. Unfortunately,
- If someone were to come across this
Self: Unsize<...>bound in a struct's documentation, they'd probably have no idea what it means. - The fact that
Self: Unsize<...>is the correct bound to achieve this is non-obvious. For example, in a forum post, both the compiler and @bluss told a user that a method needed aSelf: Sizedwhen, really,Self: Unsize<Trait>was the optimal (most flexible) bound (note: obviously,Unsize<Trait>isn't the best solution for now because it's unstable).
For future reference, the code under discussion was:
trait Inner {
fn inner_add(&self, &Outer);
}
trait Outer {
fn inner(&self) -> &Inner;
fn add(&self) {
self.inner().inner_add(self);
}
}
Where the question was "I don't understand why the Sized is needed, since I'm only ever using references."
The suggested fix was:
trait Inner {
fn inner_add(&self, &Outer);
}
trait Outer {
fn inner(&self) -> &Inner;
fn add(&self) where Self: Sized {
self.inner().inner_add(self);
}
}
The most flexible fix (once stable) would be:
#![feature(unsize)]
use std::marker::Unsize;
trait Inner {
fn inner_add(&self, &Outer);
}
trait Outer {
fn inner(&self) -> &Inner;
fn add<'a>(&'a self)
where Self: Unsize<Outer + 'a>,
{
self.inner().inner_add(self);
}
}
With the suggested sugar, this would be reduced to:
trait Inner {
fn inner_add(&self, &Outer);
}
trait Outer {
fn inner(&self) -> &Inner;
fn add(self: &Outer) {
self.inner().inner_add(self);
}
}
Which, IMO, is much cleaner.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the proposal's receiver examples and desugaring, then review the linked forum discussion for the motivation around object safety and Unsize bounds. Determine how the proposed receiver syntax should behave for sized and unsized implementors, and use those cases to define completion criteria; the issue names no files or tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 25/100