【问题标题】:Hierarchical API with a common value具有共同价值的分层 API
【发布时间】:2017-01-27 06:41:05
【问题描述】:

我有一个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,我希望孙辈可以在他们的impls 中访问。

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 实现为Parenttrait,并保持单独的客户端structs。

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,因为我必须导入所有这些特征并调用 trait 方法来遍历家谱:

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();

有没有一种很好的方法可以将这个秘密传到家谱中?只有一个父级,所以它由父级拥有是有意义的。后人怎么借?

【问题讨论】:

  • 我认为很难想出解决问题的方法,除非您提供有关您尝试编写的库类型的更多信息。例如,引用计数Secret 是否有意义?如果是,那你就不再有秘诀活不够长的问题了。

标签: rust


【解决方案1】:

您可以将 Secret 的所有权移到 Parent 之外,只共享对 Secret 的引用:

#[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&lt;T&gt;:

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

【讨论】:

  • 这是次优的,因为涉及到秘密,而不仅仅是一个字符串,我宁愿不将它暴露给客户端 API。
  • @RúnarBerg 我添加了一个使用引用计数指针的解决方案。不过,您可能想为孩子们使用Weak<T>
  • 谢谢,这行得通。这是带有完整工作版本的playground/gist
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-17
  • 2020-10-27
  • 1970-01-01
  • 2017-07-24
  • 2021-12-25
  • 1970-01-01
相关资源
最近更新 更多