0

I have a Template struct implementing a encoder function that returns a reference to a Boxed Encoder.

I also have a FixedEncoder struct that implements Encoder

I can create the Template and get the Encoder out, but how do I test the functions of FixedEncoder? I'm only looking to get FixedEncoder for testing purposes, so "unsafe" solutions are fine (though safe ones are preferred)

In my following example I get the error

error[E0599]: no method named `length` found for type `&std::boxed::Box<(dyn Encoder + 'static)>` in the current scope

Example (playground):

pub struct Template {
    encoder: Box<Encoder>
}

impl Template {
    fn new(encoder: Box<Encoder>) -> Template {
        Template { encoder }
    }

    fn encoder(&self) -> &Box<Encoder> {
        &self.encoder
    }
}

pub trait Encoder {
    fn isEncoder(&self) -> bool {
        true
    }
}

pub struct FixedEncoder {
    length: usize
}

impl FixedEncoder {
    pub fn new(length: usize) -> FixedEncoder {
        FixedEncoder { length }
    }

    pub fn length(&self) -> usize {
        self.length
    }
}

impl Encoder for FixedEncoder {}

fn main() {
    let fixed_encoder = FixedEncoder::new(1);
    let template = Template::new(Box::new(fixed_encoder));
    assert_eq!(template.encoder().isEncoder(), true); // works
    assert_eq!(&template.encoder().length(), 1); // error[E0599]: no method named `length` found for type `&std::boxed::Box<(dyn Encoder + 'static)>` in the current scope
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
A F
  • 7,424
  • 8
  • 40
  • 52
  • 2
    It isn't possible to "cast" one trait object into another. Probably a more idiomatic approach would be to use an `enum` of the possible encoders, rather than traits. – Peter Hall Nov 14 '18 at 20:45
  • 2
    For example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=a8193cbc1a72d00bf552b657a89142de – Peter Hall Nov 14 '18 at 20:55
  • @PeterHall thanks! I'll try integrating and see if it works out. Very much appreciated – A F Nov 14 '18 at 21:03
  • 1
    This answer may help to explain why you can't cast between traits - even if you know the type implements them: https://stackoverflow.com/a/25247480/493729 – Peter Hall Nov 14 '18 at 21:04
  • @PeterHall thanks for the link. I was aware of the inability to cast between traits and *sorta* understand why (that link certainly helps). I was looking for a workaround an I think the enum solution you provided should work (integrating now). – A F Nov 14 '18 at 21:14

1 Answers1

0

I was able to accomplish this by using Any.

  1. Add an as_any declaration to Encoder
  2. Add an as_any function to FixedEncoder
  3. Use .as_any().downcast_ref().unwrap() on the retreived Encoder

playground

use std::any::Any;

pub struct Template {
    encoder: Box<Encoder>
}

impl Template {
    fn new(encoder: Box<Encoder>) -> Template{
        Template {
            encoder
        }
    }
    fn encoder(&self) -> &Box<Encoder> {
        &self.encoder
    }
}

pub trait Encoder {
    fn isEncoder(&self) -> bool {
        true
    }
    fn as_any(&self) -> &dyn Any;
}

pub struct FixedEncoder {
    length: usize
}
impl FixedEncoder {
    pub fn new(length: usize) -> FixedEncoder {
        FixedEncoder { length }
    }
    pub fn length(&self) -> usize {
        self.length
    }
}



impl Encoder for FixedEncoder {
    fn as_any(&self) -> &dyn Any {
        self
    }
}

fn main() {
    let fixed_encoder = FixedEncoder::new(1);
    let template = Template::new(Box::new(fixed_encoder));
    assert_eq!(template.encoder().isEncoder(), true); // works

    let fixed_encoder_from_template : &FixedEncoder = &template.encoder().as_any().downcast_ref().unwrap();
    assert_eq!(&fixed_encoder_from_template.length, &(1 as usize));
}
A F
  • 7,424
  • 8
  • 40
  • 52