The proper ownership for “caching proxy” in Rust?

不羁的心 提交于 2021-01-29 15:48:25

问题


I'd like to use Factory to build an object from the String and have multiple impls: 1) actual building and 2) caching (stores in-memory in HashMap). The problem is that in case #1 it have to pass the ownership and in case #2 HashMap owns the value and a reference can be returned only.


use std::collections::HashMap;

// product interface
pub trait TProduct {
    fn get_title(&self) -> &String;
}

// and concrete impls
pub struct ConcreteProduct1 {
}

impl TProduct for ConcreteProduct1 {
// ...
}

pub struct ConcreteProduct2 {
}

impl TProduct for ConcreteProduct2 {
// ...
}

// factory interface
pub trait TProductFactory {
    fn product_from_text(&mut self, text: String) -> Box<dyn TProduct>;
    // QUESTION: should it be Box (required for ProductFactory) or &Box (required for ProductCachingProxy)?
}

// actual building factory
pub struct ProductFactory {
}

impl TProductFactory for ProductFactory {
    fn product_from_text(&mut self, text: String) -> Box<dyn TProduct> {
    //...
    // depending on some conditions 
    Box::new(ConcreteProduct1::from_text(text)); // has to pass the ownership
    // or
    Box::new(ConcreteProduct2::from_text(text)); // has to pass the ownership
    //...
    }
}

// caching proxy
trait TProductCache {
    fn put(&mut self, text: &String, product: Box<dyn TProduct>);
    fn get(&self, text: &String) -> Option<&Box<dyn TProduct>>;
    fn clear(&mut self);
}

struct InMemoryProductCache {
    map: HashMap<String, Box<dyn TProduct>>
}

impl InMemoryProductCache {
    fn new() -> Self {
        return InMemoryProductCache {
            map: HashMap::new()
        }
    }
}

impl TProductCache for InMemoryProductCache {
    fn put(&mut self, text: &String, product: Box<dyn TProduct>) {
        self.map.insert(text.to_string(), product);
    }

    fn get(&self, text: &String) -> Option<&Box<dyn TProduct>> {
        return match self.map.get(text) {
            Some(boxed_product) => Some(boxed_product), // have to pass a reference to let it still own the value
            None => None
        }
    }

    fn clear(&mut self) {
        self.map.clear();
    }
}

struct ProductCachingProxy {
    product_factory: Box<dyn TProductFactory>,
    cache: Box<dyn TProductCache>
}

impl ProductCachingProxy {
    fn new_for_factory(product_factory: Box<dyn TProductFactory>, cache: Box<dyn TProductCache>) -> Self {
        return ProductCachingProxy {
            product_factory,
            cache
        }
    }
}

impl TProductFactory for ProductCachingProxy {
    fn product_from_text(&mut self, text: String) -> &Box<dyn TProduct> { // can't pass ownership
        let boxed_product = match self.cache.get(&text) {
            Some(found_boxed_product) => found_boxed_product,
            _ => {
                // delegate creation to wrapped TProductFactory impl (`product_factory`)
                let boxed_product = self.product_factory.product_from_text(text.clone());
                // ... and put to the cache
                self.cache.put(&text, boxed_product);
                &boxed_product
            }
        };
        return boxed_product;
    }
}

// QUESTION: should it be Box (required for ProductFactory) or &Box (required for ProductCachingProxy) to be returned from TProductFactory.fn product_from_text(&mut self, text: String) -> Box<dyn TProduct>; ?

If caching proxy to return a Box, how can it be created from a reference without copying/cloning (TProductCache.get(..))?


回答1:


Replace Box with Rc (or Arc if you use threads). It provides shared ownership and suites both your cases with single signature. Another option is to use Cow that is a enum of owned and borrowed states.



来源:https://stackoverflow.com/questions/65920550/the-proper-ownership-for-caching-proxy-in-rust

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!