我希望能够拥有一个可以包含特定类型键的数据结构(如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选择器是这种库的关键。
欢迎任何和所有例子。
答案 0 :(得分:2)
TypeScript和JavaScript有两个nullable types:null
和undefined
。
大多数其他语言最初没有,因为拥有两种选项类型并不是明确的胜利(参考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的null
和undefined
相对应。
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,
};
}