具有共同值的分层API

时间:2017-01-27 06:41:06

标签: rust

我有一个Parent结构,一个Child结构和一个GrandChild结构:

pub struct Parent {
    pub child_a: ChildA,
    pub child_b: ChildB,
    family_secret: Secret,
}

pub struct ChildA {
    pub grand_child_x: GrandChildX,
    pub grand_child_y: GrandChildY,
}

pub struct GrandChildX {}

// etc.

父母拥有一个家庭Secret,我希望孙子女可以在他们的impl中访问。

impl GrandChildX {
    pub fn method(&self) {
        // Here I need to use the family secret.
    }
}

我正在尝试公开分层API。

let parent = Parent::new("our secret");
parent.child_a.grand_child_x.method();
parent.child_b.grand_child_y.method();    // slightly different

我尝试了几种方法来实现这一目标,包括将秘密传递到家谱中。

pub struct ChildA {
    family_secret: Secret,
    // ...
}

pub struct ChildB {
    family_secret: Secret,
    // ...
}

这在儿童之间移动值时遇到了问题(已移至ChildA::new(family_secret: secret))。

impl Parent {
    pub fn new(secret) -> Parent {
        let secret = Secret::new(secret);
        Parent {
            family_secret: secret,
            child_a: ChildA { family_secret: &secret },
            // error move after use ---------^
    }
}

我尝试将其作为参考传递下去,但之后该值的存活时间不够长:

impl Parent {
    pub fn new(secret) -> Parent {
        let secret = Secret::new(secret);
        Parent {
            child_a: ChildA { family_secret: &secret },
            //                               ^-----<
            // error does not live long enough ----^
    }
}

我唯一的成功就是将method作为trait Parent实施,并保留单独的客户struct

pub struct ChildAClient<'a> {
    family_secret: &'a Secret,
}

pub trait ChildA {
    fn child_a(&self) -> ChildAClient,        
}

impl ChildA for Parent {
    fn child_a(&self) -> ChildAClient {
        ChildAClient {
            family_secret: &self.family_secret,
        }
    }
}

// Same for ChildB, etc.

pub struct GrandChildXClient<'a> {
    family_secret: &'a Secret,
}

pub trait GrandChildX {
    fn grand_child_x(&self) -> GrandChildXClient,        
}

impl<'a> GrandChildX for ChildAClient<'a> {
    fn grand_child_x(&self) -> GrandChildXClient {
        GrandChildXClient {
            family_secret: self.family_secret,
        }
    }
}

这不仅让作者感到笨拙,而且它提供了一个笨重的API,因为我必须导入所有这些特征,并调用特征方法来遍历家谱:

use my_api::child_a::ChildA;
use my_api::child_a::grand_child_x::GrandChildX;
use my_api::child_b::ChildB;
use my_api::child_b::grand_child_Y::GrandChildY;

let parent = my_api::Parent::new("my secret");

parent.child_a().grand_child_x().method();
parent.child_b().grand_child_y().method();

有没有一种方法可以将这个秘密传递到家谱中?只有一个父母,所以它由父母拥有是有道理的。我怎样才能在后代借用它?

1 个答案:

答案 0 :(得分:4)

您可以将“秘密”的所有权移到“父级”之外,只是共享对该秘密的引用:

#[derive(Clone)]
pub struct Parent<'a> {
    pub child_a: ChildA<'a>,
    family_secret: &'a str,
}

#[derive(Clone)]
pub struct ChildA<'a> {
    pub grand_child_x: GrandChildX<'a>,
    family_secret: &'a str,
}

#[derive(Clone)]
pub struct GrandChildX<'a> {
    family_secret: &'a str,
}

impl<'a> Parent<'a> {
    pub fn new(secret: &'a str) -> Parent<'a> {
        Parent {
            family_secret: secret,
            child_a: ChildA::new(secret),
        }
    }
}

impl<'a> ChildA<'a> {
    pub fn new(secret: &'a str) -> ChildA<'a> {
        ChildA {
            family_secret: secret,
            grand_child_x: GrandChildX::new(secret),
        }
    }
}

impl<'a> GrandChildX<'a> {
    pub fn new(secret: &'a str) -> GrandChildX<'a> {
        GrandChildX{family_secret: secret}
    }
    pub fn method(&self) {
        println!("Secret: {} from grand child", self.family_secret);
    }
}

fn main() {
    let secret = "my secret".to_string();
    let parent = Parent::new(&secret);
    parent.child_a.grand_child_x.method();
}

playground

另一种解决方案是使用reference counting pointer Rc<T>

use std::rc::Rc;

#[derive(Clone)]
pub struct Parent {
    pub child_a: ChildA,
    family_secret: Rc<String>,
}

#[derive(Clone)]
pub struct ChildA {
    pub grand_child_x: GrandChildX,
    family_secret: Rc<String>,
}

#[derive(Clone)]
pub struct GrandChildX {
    family_secret: Rc<String>,
}

impl Parent {
    pub fn new(secret: String) -> Parent {
        let secret = Rc::new(secret);
        Parent {
            family_secret: secret.clone(),
            child_a: ChildA::new(secret),
        }
    }
}

impl ChildA {
    pub fn new(secret: Rc<String>) -> ChildA {
        ChildA {
            family_secret: secret.clone(),
            grand_child_x: GrandChildX::new(secret),
        }
    }
}

impl GrandChildX {
    pub fn new(secret: Rc<String>) -> GrandChildX {
        GrandChildX{family_secret: secret}
    }
    pub fn method(&self) {
        println!("Secret: {} from grand child", self.family_secret);
    }
}

fn main() {
    let secret = "my secret".to_string();
    let parent = Parent::new(secret);
    parent.child_a.grand_child_x.method();
}

playground