简体   繁体   中英

Can't box a struct that implements a trait as a trait object

I'm playing with Rust and here is the code:

pub trait TFilter {
    fn get_text(&self) -> &String;
}
...
pub struct ContentFilter {
    text: String,
    domains: String,
    body: String,
}

impl TFilter for ContentFilter {
    fn get_text(&self) -> &String {
        return &self.text
    }
}

impl ContentFilter {
    pub fn from_text(text: String, domains: String, body: String) -> Self {
        return ContentFilter {
            text,
            domains,
            body
        }
    }
}
...

fn filter_from_text(&mut self, text: String) -> &Box<dyn TFilter> {
...
&Box::new(ContentFilter::from_text(text, domains, body)) // is returned
...
}

I'm getting the error message:

expected trait object dyn filter::TFilter , found struct filter::ContentFilter

= note: expected reference &std::boxed::Box<(dyn filter::TFilter + 'static)> found reference &std::boxed::Box<filter::ContentFilter>

This is misleading as:

  1. it does implements the trait
  2. the compiler does know the size of ContentFilter struct

Any clue?

PS. The code is not good anyway (as it's unclear who owns the Box that is returned), but the message is misleading.

PPS. How a caching proxy can be implemented then:

pub trait TFilterBuilder {
    fn filter_from_text(&mut self, text: String) -> &Box<dyn TFilter>;
}
...
struct FilterCachingProxy {
    filter_builder: Box<dyn TFilterBuilder>,
    cache: Box<dyn TFilterCache>
}

impl FilterCachingProxy {
    fn new_for_builder(filter_builder: Box<dyn TFilterBuilder>, cache: Box<dyn TFilterCache>) -> Self {
        return FilterCachingProxy {
            filter_builder,
            cache
        }
    }
}

impl TFilterBuilder for FilterCachingProxy {
    fn filter_from_text(&mut self, text: String) -> &Box<dyn TFilter> {
        let boxed_filter = match self.cache.get(&text) {
            Some(found_boxed_filter) => found_boxed_filter,
            _ => {
                // delegate creation to wrapped TFilterBuilder impl (`filter_builder`)
                let boxed_filter = self.filter_builder.filter_from_text(text.clone());
                // ... and put to the cache
                self.cache.put(&text, &boxed_filter); // boxed_filter should be moved to make cache own it?
                &boxed_filter // looks strange: who owns it then?
            }
        };
        return boxed_filter;
    }
}

The return type should be just a Box , not a reference to a Box :

fn filter_from_text(&mut self, text: String) -> Box<dyn TFilter> {
    ...
    Box::new(ContentFilter::from_text(text, domains, body)) // is returned
}

( Minimal compiling version on the playground )

The box is newly created in your function, so you have to return ownership of the box. It's impossible to just return a reference to a temporary, since it will go out of scope at the end of the function.

A side effect of this change is that the unsized coercion from Box<ContentFilter> to Box<dyn TFilter> will now actually work. Coercions are only applied in coercion site like the return value. In your code, the type that needs to be coerced is nested in a reference, so no unsized coercion is performed by the compiler.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM