具有特定键的可选类型值的TypeScript散列映射的Rust等价物是什么?

时间:2018-04-29 18:32:35

标签: rust

我希望能够拥有一个可以包含特定类型键的数据结构(如Rust结构),但可能具有不需要存在的可选值(如HashMap)。是否存在介于两者之间的数据类型?最好的方法是什么?

我正在学习Rust,但每天都使用TypeScript。我知道这两种类型的系统是不同的,我只是想尝试实现类似的东西。在TypeScript中,我可以这样做:

interface Example {
  value1: string;
  optional?: number;
  value3?: "hello" | "goodbye";
}

并在创建匹配它的对象时:

const obj: Example = {
  value1: "this works"
}

我正在探索对DenisKolodin/yew的贡献,我想要尝试的第一件事就是以惯用的Rust方式实现free-style。能够在具有特定枚举类型的结构中声明样式(" flex"," block"等)但不需要每个可能的样式/ CSS选择器是这种库的关键。

欢迎任何和所有例子。

2 个答案:

答案 0 :(得分:2)

TypeScript和JavaScript有两个nullable typesnullundefined

大多数其他语言最初没有,因为拥有两种选项类型并不是明确的胜利(参考https://www.youtube.com/watch?v=PSGEjv3Tqo0&feature=youtu.be&t=9m21s来自https://basarat.gitbooks.io/typescript/docs/javascript/null-undefined.html; https://github.com/Microsoft/TypeScript/wiki/Coding-guidelines#null-and-undefined)。

在Rust中,您使用的是Option,而(大部分时间)都与JavaScript和TypeScript的nullundefined相对应。

struct Example {
    value1: String,
    optional: Option<i64>,
    value3: Option<String>
}

P.S。为了只指定您要指定的字段,并将所有其他字段保留在None,您可以告诉语言为结构实现Default

#[derive(Default)]
struct Example {
    value1: String,
    optional: Option<i64>,
    value3: Option<String>
}

fn new_example() -> Example {
    Example {
        value1: "foobar".into(),
        ..Default::default()
    }
}

答案 1 :(得分:2)

这不是Rust中HashMaps的用例。 HashMap具有在编译时未知的键,这不是您想要的。结构具有一组固定的已知键,而枚举具有一组固定的可能性。

代码的直接翻译会创建结构和枚举,使用Option表示可选字段:

#[derive(Debug)]
struct Example {
    value1: String,
    optional: Option<i32>,
    value3: Option<Value3Values>,
}

#[derive(Debug)]
enum Value3Values {
    Hello,
    Goodbye,
}

虽然可以使用,但与您习惯的相比,这可能是恼人的

Example {
    value1: String::from("hello"),
    optional: None,
    value3: Some(Value3Values::Goodbye),
}

我们可以采取一些措施来改进它。使用Default加上 struct literal update语法可以使所有默认值更容易:

#[derive(Debug, Default)]
struct Example { /* ... */ }
Example {
    value1: String::from("hello"),
    value3: Some(Value3Values::Goodbye),
    ..Example::default()
}

然后,您还可以应用Into / From特征来删除部分转化:

Example {
    value1: "hello".into(),
    value3: Value3Values::Goodbye.into(),
    ..Example::default()
}

你可以将它包装在宏中以避免重复:

macro_rules! thing {
    ($t:ident, { $( $name:ident : $val:expr ),*, }) => (
        $t {
            $( $name: Into::into($val) ),*,
            .. $t::default()
        }
    );
}

fn main() {
    thing!(Example, {
        value1: "hello",
    });

    thing!(Example, {
        value1: "hello",
        optional: 32,
    });

    thing!(Example, {
        value1: "hello",
        value3: Value3Values::Hello,
    });

    thing!(Example, {
        value1: "hello",
        optional: 32,
        value3: Value3Values::Hello,
    });
}

甚至有方法可以为value3使用字符串文字,但我会避免使用它。 &#34; Stringly类型的&#34; API很烦人。

macro_rules! value3 {
    ("hello") => (Value3Values::Hello);
    ("goodbye") => (Value3Values::Goodbye);
}

fn main() {
    thing!(Example, {
        value1: "hello",
        value3: value3!("hello"),
    });

    thing!(Example, {
        value1: "hello",
        optional: 32,
        value3: value3!("goodbye"),
    });
}

甚至可能有更聪明的宏窍门,以避免需要调用value3!内的宏thing!

非常高级技术是使用build script为每组CSS属性生成自定义宏,可能使用MDN CSS JSON DB。你最终会得到类似的东西:

macro_rules! example {
    ( $( $name:ident : $val:tt ),*, ) => (
        Example {
            $( $name: example!(field @ $name : $val) ),*,
            .. Example::default()
        }
    );

    // Internal details
    ( field @ value1 : $val:expr ) => (Into::into($val));
    ( field @ optional : $val:expr ) => (Into::into($val));
    ( field @ value3 : hello ) => (Some(Value3Values::Hello));
    ( field @ value3 : goodbye ) => (Some(Value3Values::Goodbye));
    ( field @ value3 : $val:expr ) => (
        compile_error!(r#"value3 can only be "hello" or "goodbye""#)
    );
}

fn main() {
    example! {
        value1: "name",
    };

    example! {
        optional: 42,
    };

    example! {
        value1: "name",
        value3: hello,
    };

    example! {
        value1: "name",
        optional: 42,
        value3: goodbye,
    };
}