Cyb*_*ran 3 code-reuse types rust
我有一个具有一些功能的基本类型,包括特征实现:
use std::fmt;
use std::str::FromStr;
pub struct MyIdentifier {
value: String,
}
impl fmt::Display for MyIdentifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl FromStr for MyIdentifier {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(MyIdentifier {
value: s.to_string(),
})
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个简化的例子,真正的代码会更复杂。
我想介绍两种类型,它们具有与我描述的基本类型相同的字段和行为,例如MyUserIdentifier和MyGroupIdentifier。为避免在使用这些时出错,编译器应将它们视为不同的类型。
我不想复制我刚写的整个代码,我想重用它。对于面向对象的语言,我会使用继承。我将如何为 Rust 做到这一点?
Use a PhantomData to add a type parameter to your Identifier. This allows you to "brand" a given identifier:
use std::{fmt, marker::PhantomData, str::FromStr};
pub struct Identifier<K> {
value: String,
_kind: PhantomData<K>,
}
impl<K> fmt::Display for Identifier<K> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl<K> FromStr for Identifier<K> {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Identifier {
value: s.to_string(),
_kind: PhantomData,
})
}
}
struct User;
struct Group;
fn main() {
let u_id: Identifier<User> = "howdy".parse().unwrap();
let g_id: Identifier<Group> = "howdy".parse().unwrap();
// do_group_thing(&u_id); // Fails
do_group_thing(&g_id);
}
fn do_group_thing(id: &Identifier<Group>) {}
Run Code Online (Sandbox Code Playgroud)
use std::{fmt, marker::PhantomData, str::FromStr};
pub struct Identifier<K> {
value: String,
_kind: PhantomData<K>,
}
impl<K> fmt::Display for Identifier<K> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl<K> FromStr for Identifier<K> {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Identifier {
value: s.to_string(),
_kind: PhantomData,
})
}
}
struct User;
struct Group;
fn main() {
let u_id: Identifier<User> = "howdy".parse().unwrap();
let g_id: Identifier<Group> = "howdy".parse().unwrap();
// do_group_thing(&u_id); // Fails
do_group_thing(&g_id);
}
fn do_group_thing(id: &Identifier<Group>) {}
Run Code Online (Sandbox Code Playgroud)
The above isn't how I'd actually do it myself, though.
I want to introduce two types which have the same fields and behaviour
Two types shouldn't have the same behavior — those should be the same type.
I don't want to copy the entire code I just wrote, I want to reuse it instead
Then just reuse it. We reuse types like String and Vec all the time by composing them as part of our larger types. These types don't act like Strings or Vecs, they just use them.
Maybe an identifier is a primitive type in your domain, and it should exist. Create types like User or Group and pass around (references to) users or groups. You certainly can add type safety, but it does come at some programmer expense.
| 归档时间: |
|
| 查看次数: |
327 次 |
| 最近记录: |