Create a factory class that returns an implementation with an empty structure

I would like to create a generic interface for formatters that will take input and format it according to its purpose.

I am currently returning a Box containing the formatting implementation (wrapped in a result). But I don't think this is the best way to do it. Since Formatter implementations are empty structures, allocating heap memory for the box doesn't make sense.

pub trait Formatter {
    fn format_information(&self, information: Result<Information, Error>) -> Result<String, Error>;
    fn format_information_collection(&self, information: InformationCollection) -> Result<String, Error>;
}

pub struct JsonFormatter;
impl Formatter for JsonFormatter {...}

pub struct XmlFormatter;
impl Formatter for XmlFormatter {...}


// Factory to create a formatter
pub struct Factory;
impl Factory {
    pub fn get_formatter(format: &str) -> Result<Box<Formatter>, Error> {
        match format {
            "json" => Ok(Box::new(JsonFormatter {})),
            "xml" => Ok(Box::new(XmlFormatter {})),
            _ => Err(Error::new(format!("No formatter found for format {}", format)))
        }
    }
}

// Use the factory
let formatter_box = Factory::get_formatter(format).unwrap();
let formatter = &*formatter_box as &Formatter;

      

What's the correct way to do this in Rust?

+3


source to share


1 answer


Since Formatter implementations are empty structures, it Box

doesn't make sense to allocate heap memory for .

And since it doesn't make any sense, the heap of memory won't be allocated at all. Let's try to try ( Playground ):

// `()` doesn't occupy any space, like e.g. your `JsonFormatter`
let b1 = Box::new(());
let b2 = Box::new(());

println!("{:p}\n{:p}", &*b1, &*b2);

      

This results in the output:

0x1
0x1

      

ZST (Zero Size Types) are often handled in a special way. At least you know that you are not paying for heap allocation. Note, however, that the layout of your memory Box<Formatter>

is thick pointer and looks like this: (*mut Formatter, *mut VTable)

. The first pointer is always 0x1

, the second is a link to a statically allocated mailing table containing a function pointer ( vtable - Wikipedia ). This is probably a good thing in your situation.


Another possibility is to create an enum like this:

enum FormatterSd {
    Json(JsonFormatter),
    Xml(XmlFormatter),
}

      



And now you can implement Formatter for FormatterSd

; in this implementation, you will use simple match

dispatch blocks . This way you don't need to use Box

.


Finally: you don't need to have a factory type! It looks like you are trying to pull ideas from the OO programming language into Rust. This is often not the best or most idiomatic solution. For example, Rust has free functions. Therefore, you can simply write:

fn get_formatter(format: &str) -> Result<Box<Formatter>, Error> {
    // ...
}

      

Yes, no type Factory

! Empty types are much less common in Rust than empty classes (which means: no fields). You can just use a free function for this kind of thing, no need to bind to a type.

And finally, one last thing: you don't need to manually get the link from Box

:

let formatter = &*formatter_box as &Formatter;

      

You can just tell formatter_box.format_information(...);

by being forced to drift.

+10


source







All Articles