【问题标题】:Reduce handling of Optional by leveraging the type system?通过利用类型系统来减少对 Optional 的处理?
【发布时间】:2022-08-18 15:58:51
【问题描述】:

我正在尝试访问一个 api,我可以在其中指定我希望在结果中包含哪些类型的字段。 (例如 \"basic\"、\"advanced\"、\"irrelevant\"

代表它的 Rust 结构看起来像

Values {
  a: Option<String>;
  b: Option<String>;
  c: Option<String>;
  d: Option<String>;
}

或者可能更好:

Values {
  a: Option<Basic>;      // With field a
  b: Option<Advanced>;   // With fields b,c
  c: Option<Irrelevant>; // With field d
}

使用它是可能的,但我希望减少调用者对 Option 的处理。 是否可以利用类型系统来简化使用? (或者我没有意识到的任何其他方式?)

我的想法是朝着这个方向发展,但我认为生锈可能无法实现(至少没有宏):

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=093bdf1853978af61443d547082576ca

struct Values {
    a: Option<&\'static str>,
    b: Option<&\'static str>,
    c: Option<&\'static str>,
}

trait ValueTraits{}

impl ValueTraits for dyn Basic{}
impl ValueTraits for dyn Advanced{}
impl ValueTraits for Values{}

trait Basic {
    fn a(&self) -> &\'static str;
}

trait Advanced {
    fn b(&self) -> &\'static str;
    fn c(&self) -> &\'static str;
}

impl Basic for Values {
   fn a(&self) -> &\'static str {
       self.a.unwrap()
   }
}

impl Advanced for Values {
    fn b(&self) -> &\'static str {
        self.b.unwrap()
    }
    
    fn c(&self) -> &\'static str {
        self.c.unwrap()
    }
}

//Something like this is probably not possible, as far as I understand Rust
fn get_values<T1, T2>() -> T1 + T2{
    Values {
        a: \"A\",
        b: \"B\",
        c: \"C\"
    }
}

fn main() {
    let values = get_values::<Basic, Advanced>();
    println!(\"{}, {}, {}\", values.a(), values.b(), values.c());
}

澄清(编辑)

Values 结构包含来自我调用的 api 的反序列化 json 数据。我可以请求将字段组包含在响应中(1-n 个请求的字段组),这些字段属于不同类型。

如果我事先知道返回哪些字段,我将不需要它们是 Option,但是当调用者决定返回哪些字段时,这些字段需要是 Option(直接或按字段组分组)

有太多可能的组合来为每个组合创建一个结构。

我完全意识到这是行不通的,它只是“伪锈”:

fn get_values<T1, T2>() -> T1 + T2{
    Values {
        a: \"A\",
        b: \"B\",
        c: \"C\"
    }
}

但我的思考过程是: 理论上,我可以通过泛型请求字段组,因此我可以创建一个实现这些特征的“动态”类型,因为我知道请求了哪些特征。

特征应该像实际结构中的“视图”一样起作用,因为如果事先请求它们,我知道我应该从 api 请求它们以将它们包含在结构中。

我对泛型和特征的了解不足以自信地说“这根本不可能”,在我问到这里之前,我找不到确定的答案。

很抱歉最初的问题不清楚实际问题是什么,我希望澄清有助于解决这个问题。

  • 你能解释一下你的代码应该做什么吗?看起来它只是到处调用unwrap。如果这就是您处理它们的方式,您也可能没有Options。
  • @isaactfa 看起来他们想要某种视图系统,其中视图转换将检查视图的要求是否得到满足,然后允许对相关项目的非可选访问(这些项目本身是可选的)。
  • @isaactfa 我没有包含任何错误处理,因为与此示例无关,我可以请求将包含在来自 API 的响应中的字段(在此示例中为 Basic,Advance),并且我知道它们将出现在响应中.但我想要一个功能,我可以在其中请求任何可能的字段。因为我不知道调用者会请求什么字段,所以一切都必须是一个选项。如果我可以使用类型系统来声明存在的字段,我可以只返回它们的值而不是 Option

标签: rust types traits


【解决方案1】:

我无法完全判断您是否希望能够从问题中请求和返回多种不同类型的字段。但是,如果返回的所有信息都是单一类型的,您可以尝试使用HashMap

use std::collections::HashMap;

fn get_values(fields: &[&'static str]) -> HashMap<&'static str, &'static str> {
    let mut selected = HashMap::new();
    for field in fields {
        let val = match *field {
            "a" => "Value of a",
            "b" => "Value of b",
            "c" => "Value of c",
            // Skip requested fields that don't exist.
            _ => continue,
        };
        selected.insert(*field, val);
    }
    selected
}

fn main() {
    let fields = ["a","c"];
    let values = get_values(&fields);
    for (field, value) in values.iter() {
        println!("`{}` = `{}`", field, value);
    }
}

此外,您给我的印象是您还没有完全能够在泛型和特征之间形成关系。我强烈建议阅读本书的"Generic Types, Traits, and Lifetimes" 部分。

它的要点是泛型的存在是为了将函数、结构、枚举甚至特征泛化到任何类型,而特征用于将行为分配给类型。特征不能作为通用参数传递,因为特征不是类型,它们是行为。这就是为什么这样做:get_values::&lt;Basic, Advanced&gt;(); 不起作用。 Basic 和 Advanced 都是特征,而不是类型。

如果您想练习泛型,请尝试泛化get_values,以便它可以接受任何可以转换为产生&amp;'static strs 的迭代器的类型。

【讨论】:

  • 感谢您的回答,我对原来的问题进行了澄清。这本书的章节没有涵盖我的问题。我知道我在示例中使用它的方式不是正确使用 Rust 泛型,但我不知道你如何或是否真的可以做我想到的用例。不过,这似乎更令人困惑然后很有帮助。
猜你喜欢
  • 2022-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-01
  • 2019-11-18
  • 1970-01-01
  • 2018-10-16
  • 2017-02-10
相关资源
最近更新 更多