matklad
November 14, 2016, 1:12pm
1
Hi! The following does not compile:
trait T {
fn foo(&self) {
bar(self)
}
}
fn bar(x: &T) {}
fn baz(x: Box<T>) {
x.foo()
}
It says "the trait bound Self: std::marker::Sized
is not satisfied" inside the default method. But if I add a Sized
bound, I can't call foo
on the boxed T
. Is there any way to solve this dilemma?
2 Likes
Eh2406
November 14, 2016, 3:03pm
2
did you want?
trait T {
fn foo(&self) {
bar(&self)
}
}
Just trying to understand.
matklad
November 14, 2016, 3:06pm
3
This does not work either: Rust Playground
Eh2406
November 14, 2016, 3:14pm
4
oops... trait object, I have yet to get the hang of them. I expect to learn from the competent replies. Sorry for the noize.
Hi, You just need to provide a trait bound to your bar
method, and things will work, as functions takes arguments which have a known size at compile time.
The following works :
use std::marker::Sized;
struct Tee;
trait T {
fn foo(&self) {
bar(&self)
}
}
fn bar<T: Sized>(x: &T) {}
fn baz(x: Box<T>) {
x.foo();
}
impl T for Tee {}
fn main() {
let tee = Tee;
baz(Box::new(tee));
}
matklad
November 14, 2016, 9:32pm
6
Yep, if just monomorphize everything it works, but I really want to use trait objects here
If I manually repeat the default implementation for each impl than it works:
trait T {
fn foo(&self);
}
impl T for () { // generic impl like `impl<S> T for S` would also work
fn foo(&self) {
bar(self)
}
}
Why default methods are special?
They aren't special. That one works because ()
is sized. See my thread here:
When trying to make something object safe, I ran into the following problem:
trait Trait {}
struct View<'a>(&'a mut Wrapper<Trait + 'a>);
struct Wrapper<A: ?Sized + Trait>(A);
impl<T: ?Sized + Trait> Wrapper<T> {
// I want this to work on both `Wrapper<SomeType>` and `Wrapper<Trait>`.
fn view<'a>(&'a mut self) -> View<'a> {
// FIXME: Not sized so we can't do this.
View(self as &mut Wrapper<Trait + 'a>)
}
}
fn main() {}
However, I fell there should be some way to…
And a related RFC Issue:
opened 06:00PM - 11 May 16 UTC
T-lang
## Proposal
Allow receivers automatically "unsize" self. For example, given:
`… `` rust
trait Trait {}
struct View<'a>(&'a mut Wrapper<Trait + 'a>);
struct Wrapper<A: ?Sized + Trait>(A);
```
Desugar:
``` rust
impl<T: ?Sized + Trait> Wrapper<T> {
// Unsizing receiver
fn view<'b>(self: &'b mut Wrapper<Trait + 'b>) -> View<'b> {
View(self)
}
}
```
To:
``` rust
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,
1. If someone were to come across this `Self: Unsize<...>` bound in a struct's documentation, they'd probably have no idea what it means.
2. The fact that `Self: Unsize<...>` is the correct bound to achieve this is non-obvious. For example, in [a forum post](https://users.rust-lang.org/t/unexpected-sized-requirement/5534), both the compiler and @bluss told a user that a method needed a `Self: Sized` when, 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:
``` rust
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:
``` rust
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:
``` rust
#![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:
``` rust
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.
With the unsize
feature, you could write:
#![feature(unsize)]
use std::marker::Unsize;
trait T {
fn foo<'a>(&'a self) where Self: 'a + Unsize<T + 'a> {
bar(self)
}
}
fn bar(x: &T) {}
fn baz(x: Box<T>) {
x.foo()
}
fn biz<V: T>(x: V) {
x.foo()
}
But it's rather messy, unstable, and has some weird edge cases.
1 Like
sinkuu
November 15, 2016, 1:26am
8
This may not be what you want, but adding Sized
bound and implmenting T
for Box<T>
seems to work.
trait T {
fn foo(&self) where Self: Sized {
bar(self)
}
}
impl T for Box<T> {
}
fn bar(x: &T) {}
fn baz(x: Box<T>) {
x.foo()
}
2 Likes
matklad
November 16, 2016, 11:27am
9
Ouch, this DST stuff is complex.
Is there a definitive reference about them? The docs I've found are a bit skinny:
https://doc.rust-lang.org/beta/book/unsized-types.html
This post from early 2014 is helpful, but I'd want to read something more up to date
The thing that puzzles me most is how on earth can the raw pointer be fat?
trait T { fn foo(&self); }
fn main() {
println!("ref: {} {} {}\nptr: {} {} {}",
std::mem::size_of::<&i32>(),
std::mem::size_of::<&[i32]>(),
std::mem::size_of::<&T>(),
std::mem::size_of::<*const i32>(),
std::mem::size_of::<*const [i32]>(),
std::mem::size_of::<*const T>(),
)
}
1 Like
Probably because - at least for the *const T
- about the only reasonable thing you could do with it is to transmute it back into a &T
, and then it'll need its vtable again
The best I could find is this . Looks like:
trait Trait : AsTrait {
fn foo(&self) where Self : AsTrait {
bar(self.as_trait());
}
}
trait AsTrait {
fn as_trait(&self) -> &Trait;
}
impl<T : Trait + Sized> AsTrait for T {
fn as_trait(&self) -> &Trait { self }
}
It is pretty ugly and I didn't manage to create generic implementation of it, because compiler didn't like
trait AsObject<Object> { ... }
trait Trait : AsObject<Trait> { ... }
// ^^^^^ This kind of recursion appears not to be permitted.