0

I am struggling with returning a common trait, given lines I have currently:

#[derive(Debug)]
pub struct GroupAddressThreeLevel {
    main: u8,
    middle: u8,
    sub: u8,
}

#[derive(Debug)]
pub struct GroupAddressTwoLevel {
    main: u8,
    sub: u16,
}

pub trait GroupAddressBytes {
    fn as_bytes(&self) -> [u8; 2];
    fn from_bytes(bytes: [u8; 2]) -> Result<Self, CustomError> where Self : Sized;
}


impl GroupAddressBytes for GroupAddressThreeLevel {
    pub fn new(main: u8, middle: u8, sub: u8) -> Result<Self, CustomError> {
       ... omitted ...
    }
}

impl GroupAddressBytes for GroupAddressTwoLevel {
    pub fn new(main: u8, sub: u16) -> Result<Self, CustomError> {
       ... omitted ...
    }
}

impl GroupAddress {
    pub fn parse(address: &str) -> Result<dyn GroupAddressBytes, CustomError> {

        let splitted_address: Vec<_> = address.split('/').collect();
        match splitted_address.len() {
            3 => {
                // Group Address is Three-level
                GroupAddressThreeLevel::new(
                    u8::from_str(splitted_address[0]).unwrap(),
                    u8::from_str(splitted_address[1]).unwrap(),
                    u8::from_str(splitted_address[2]).unwrap(),
                )
           }
            2 => {
                // Group Address is Two-Level
                GroupAddressTwoLevel::new(
                    u8  ::from_str(splitted_address[0]).unwrap(),
                    u16::from_str(splitted_address[1]).unwrap(),
                )
            }
            _ => {
                Err(CustomError(format!("Unsupported address format provided. Supported are: 0/0/0 or 0/0")))
            }
        }
    }
}

What I am getting from compiler is:

37  |     pub fn parse(address: &str) -> Result<dyn GroupAddressBytes, CustomError> {
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    | 

According to https://doc.rust-lang.org/reference/items/traits.html#object-safety it seems be object safe as I added "Self : Sized" to the method of GroupAddressBytes trait.

How can I accomplish that GroupAddress#parse(&str) function will compile successfully and return the GroupAddressBytes as an object?

The idea of GroupAddress#parse(&str) function is when user provides format "0/0/0" then GroupAddressThreeLevel is returned, if "0/0" is provided then GroupAddressTwoLevel is returned. Both structs will have their own implementation which varies bit differently, but share the same methods provided by GroupAddressBytes.


Alternatively, if the approach above is not possible with Rust, what about following approach. Here I also failed:

    pub fn parse<T : GroupAddressBytes>(address: &str) -> Result<T, CustomError> {

which returns by compiler:

 expected type parameter `T`, found struct `GroupAddressThreeLevel`
 expected type parameter `T`, found struct `GroupAddressTwoLevel`

Thank you, Christoph

pitschr
  • 156
  • 1
  • 8
  • `dyn Trait` values have to always be behind some sort of reference. In your case `Box`ing them will work. – Ivan C Apr 22 '21 at 19:52
  • Thanks. `Result, CustomError>` solved the issue. I could not use the `impl` keyword as it is returning either of `GroupAddressThreeLevel` or `GroupAddressTwoLevel` which is complained by compiler. So I had to unwrap the value from e.g. `GroupAddressThreeLevel::new` and wrap it into a Box as suggested. – pitschr Apr 23 '21 at 17:13
  • Additional to comment before (might helpful for others): Solved by e.g. `match GroupAddressThreeLevel::new( u8::from_str(splitted_address[0]).unwrap(), u8::from_str(splitted_address[1]).unwrap(), u8::from_str(splitted_address[2]).unwrap(), ) { Ok(group_address) => Ok(Box::new(group_address)), Err(e) => Err(e) }` – pitschr Apr 23 '21 at 17:16

0 Answers0