3

I want to notify that "this method will return boxed Struct which implements Trait and Sized". Simple resolution is just put Struct into Box but I can't because Struct has so big generic parameter that I can't write manually.

// I can't edit these trait and struct.
trait Trait {}
struct Struct();
impl Trait for Struct {}

// This is my code.
fn func() -> Box<Trait> {
    Box::new(Struct{})
}

// This is not my code.
fn need_sized<T: Trait + Sized>(item: T) {
    println!("ok");
}

fn main() {
    // This can not be compiled!
    need_sized(func());
}

I can edit func function but I can't others.

How can I specify that Trait implements Sized? Is it something kind like below?

fn func() -> Box<Trait + Sized>

3 Answers3

2

I think you need to be able to change either func or need_sized for this to work. As they are, func returns a trait object which is unsized, while need_sized requires a concrete type (more than that, it requires full ownership of its parameter item, not just a reference).

Approach 1: if you control and can change func(), you can change it to return a Box<Struct> instead of Box<Trait>:

fn func() -> Box<Struct> {
    Box::new(Struct{})
}

fn main() {
    let s1 = func(); // Box<Struct>, needs dereferencing with *
                     // to become a Struct
    need_sized(*s1); // this now works
}

Approach 2: if you don't control func() but do control need_sized and Trait you can downcast the Box back to a Struct as in this answer: https://stackoverflow.com/a/33687996/497364

playground

use std::any::Any; // needed for downcast

trait Trait {
    fn as_any(&self) -> &Any;
}

struct Struct{}
impl Trait for Struct {
    fn as_any(&self) -> &Any {
        self
    }
}

fn func() -> Box<Trait> {
    Box::new(Struct{})
}


// changed to accept a &T
fn need_sized<T: Trait + Sized>(_item: &T) { 
    println!("ok");
}

fn main() {
    let s2 = func(); 
    if let Some(st) = s2.as_any().downcast_ref::<Struct>() {
        need_sized(st);
    }
}
Community
  • 1
  • 1
Paolo Falabella
  • 24,914
  • 3
  • 72
  • 86
  • Thanks for nice answer. But what makes this problem difficult is I can't write `Struct` simply. Because `Struct` has so big generic parameter that I can't write simply. Do you have any ideas ? – Atsuki Takahashi May 11 '17 at 11:42
  • 1
    @AtsukiTakahashi If typing is your primary concern, you may be able to just make a [type alias](https://doc.rust-lang.org/book/type-aliases.html) (e.g. `type Short = Long<'a, T, Box>` or what have you, and then use `Short` instead of the long version. – trent May 11 '17 at 20:09
2

Given the specification the only way you can bypass this is to make newtype wrapper.

// I can't edit these trait and struct.
trait Trait {}
struct Struct();
impl Trait for Struct {}


// This is my code.
struct MyStruct(Struct);
impl Trait for Box<MyStruct> { /* if there is code here delegate to inner `Struct` */}

fn func() -> Box<MyStruct> {
    Box::new(MyStruct(Struct{}))
}

// This is not my code.
fn need_sized<T: Trait + Sized>(item: T) {
    println!("ok");
}

fn main() {
    // This can not be compiled!
    need_sized(func());
}

playground

Why other approaches won't solve your issues:

  1. Monomorphisized trait

    fn func<T: Trait>() -> Box<T> { ... }

You want to pass func to need_size. More specifically, you want to pass Box to the need_size function.

  1. Changing function signature to Box<Struct>

error[E0277]: the trait bound std::boxed::Box<Struct>: Trait is not satisfied

--> <anon>:22:5
  |
22|     need_sized(func());
  |     ^^^^^^^^^^ the trait `Trait` is not implemented for   `std::boxed::Box<Struct>`
  |= help: the following implementations were found:
         <std::boxed::Box<MyStruct> as Trait>
   = note: required by `need_sized`

This won't work because you can't control either Box or Struct since they aren't defined in you crate, and it would mess up with coherence rules. In future this might be solved using special coherence rules.

rodrigocfd
  • 6,450
  • 6
  • 34
  • 68
Daniel Fath
  • 16,453
  • 7
  • 47
  • 82
1

The error message is;

error[E0277]: the trait bound std::boxed::Box<Trait>: Trait is not satisfied

So adding; impl Trait for Box<Trait> {} after line three lets it compile.

kvsari
  • 11
  • 1
  • Thanks for answered. So in case we can't `impl Trait for Box` (ex. `Trait` is not our code), how do we do? – Atsuki Takahashi May 11 '17 at 11:55
  • Try using a newtype struct. – kvsari May 11 '17 at 11:58
  • I think this [here](https://doc.rust-lang.org/nightly/book/second-edition/ch19-03-advanced-traits.html#the-newtype-pattern-to-implement-external-traits-on-external-types) explains what you may need to do. – kvsari May 11 '17 at 12:06