2

I'm working on a Rust trait, LayoutSection, and ran into an issue where I had to make two nearly identical functions for the trait: one that's an associated function and one thats a method: section_type() and section_kind(&self).

Ideally, I would only have the section_type() associated function, however LayoutSection is unable to be made into a trait object unless section_type() has a where Self: Sized clause.

However I need to use section_type() in methods that use trait objects, so I was forced to create section_kind(&self), which is exactly the same as section_type(), but it can be called on trait objects.

I know this is a horrible hack, and there has to be some alternative, but I can't think of any other way to do this.

Here is a simplified example of how these functions are defined and being used: (here's the Rust Playground)

fn main() {
    print_generic_info::<Header>(None);
}

fn print_generic_info<S: LayoutSection>(game: Option<Game>) {
    if let Some(s) = game {
        let section = s.get_section::<S>();
    }

    // The reason I wanted to use a type parameter in `Game::get_section`,
    // rather than pass a `SectionType`, is because I'm doing something 
    // like this later on:
    // let section = <S as LayoutSection>::with_offset(...);
}

struct Game;

impl Game {
    fn get_section<S: LayoutSection>(&self) -> Option<&LayoutSection> {
        Some(match <S as LayoutSection>::section_type() {
            SectionType::Header => unimplemented!(),
        })
    }
}

#[derive(Debug)]
enum SectionType {
    Header
}

trait LayoutSection {
    fn section_kind(&self) -> SectionType;
    fn section_type() -> SectionType
    where
        Self: Sized;

    fn print_generic_info(&self) {
        println!("Type: {:?}", self.section_kind());
    }
}

struct Header;

impl LayoutSection for Header {
    fn section_kind(&self) -> SectionType {
        SectionType::Header
    }

    fn section_type() -> SectionType {
        SectionType::Header
    }
}

What would be a better alternative to this? I wanted to use an associated constant to store the SectionType, however using those still wouldn't allow LayoutSection to be used as a trait object. But something like that would be even better than the section_type() associated function.

Addison
  • 3,791
  • 3
  • 28
  • 48
  • Sounds good! Some other points to consider... *make two nearly identical functions for the trait* — the code shown doesn't have any implementation for either. *this prevents me from being able to use it* — without the error, it's difficult to tell what this means – Shepmaster Jun 19 '18 at 02:24
  • Related Q&A that may be relevant: [Is it possible to extend a default method implementation of a trait in a struct?](https://stackoverflow.com/q/31461902/155423), [Why would I implement methods on a trait instead of as part of the trait?](https://stackoverflow.com/q/34438755/155423) – Shepmaster Jun 19 '18 at 02:26

1 Answers1

0

I was able to figure out a solution using the SectionType enum, rather than using type parameters. I wanted to have an associated function called SectionType::with_offset, but I was able to add SectionType::with_offset a method to SectionType instead. Here is the updated example:

fn main() {
    print_generic_info(None, &SectionType::Header);
}

fn print_generic_info(game: Option<Game>, section_type: &SectionType) {
    if let Some(s) = game {
        let section = s.get_section(section_type);

        // Now I can do this:
        let other_section = section_type.with_offset(0);
    }
}

struct Game;

impl Game {
    fn get_section(&self, section_type: &SectionType) -> Option<&LayoutSection> {
        Some(match section_type {
            SectionType::Header => unimplemented!(),
        })
    }
}

#[derive(Debug)]
enum SectionType {
    Header
}

impl SectionType {
    fn with_offset(&self, offset: u64) -> Box<LayoutSection> {
        match self {
            SectionType::Header => Box::new(Header),
        }
    }
}

trait LayoutSection {
    fn section_kind(&self) -> SectionType;

    fn print_generic_info(&self) {
        println!("Type: {:?}", self.section_kind());
    }
}

struct Header;

impl LayoutSection for Header {
    fn section_kind(&self) -> SectionType {
        SectionType::Header
    }
}

Link to the Rust Playground

Addison
  • 3,791
  • 3
  • 28
  • 48