Types 如何在Rust中重用类似但不同类型的代码?

Types 如何在Rust中重用类似但不同类型的代码?,types,rust,code-reuse,Types,Rust,Code Reuse,我有一个带有一些功能的基本类型,包括trait实现: 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.v

我有一个带有一些功能的基本类型,包括trait实现:

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(),
        })
    }
}
使用std::fmt;
使用std::str::FromStr;
发布结构MyIdentifier{
值:字符串,
}
impl fmt::显示MyIdentifier{
fn fmt(&self,f:&mut fmt::Formatter)->fmt::Result{
写!(f,“{}”,self.value)
}
}
MyIdentifier的impl FromStr{
类型Err=();
fn来自_str(s:&str)->结果{
Ok(我的标识符){
值:s.to_string(),
})
}
}
这是一个简化的例子,实际代码将更加复杂

我想介绍两种类型,它们的字段和行为与我描述的基本类型相同,例如
MyUserIdentifier
MyGroupIdentifier
。为了避免在使用它们时出错,编译器应该将它们视为不同的类型


我不想复制我刚写的全部代码,我想重新使用它。对于面向对象的语言,我将使用继承。如何处理生锈问题?

有几种方法可以解决此类问题。下面的解决方案是使用所谓的newtype模式,即newtype包含的对象的统一特征和newtype的特征实现

(解释将是内联的,但如果您希望看到整个代码并同时测试它,请转到。)

首先,我们创建一个描述我们希望从标识符中看到的最小行为的特征。在Rust中,你没有遗传,你有组合,也就是说,一个对象可以实现任何数量的描述其行为的特征。如果你想在你所有的对象中都有一些共同的东西——你可以通过继承来实现——那么你必须为它们实现同样的特性

使用std::fmt;
特征标识符{
fn值(&self)->&str;
}
然后我们创建一个新类型,其中包含一个值,该值是一个泛型类型,它被约束以实现我们的
标识符
特征。这种模式的好处在于,编译器最终会对其进行优化

struct-Id(T);
现在我们有了一个具体的类型,我们为它实现了
Display
trait。由于
Id
的内部对象是一个
标识符
,因此我们可以在其上调用
方法,因此我们只需实现此特性一次

impl<T> fmt::Display for Id<T>
where
    T: Identifier,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0.value())
    }
}
最后但并非最不重要的一点是,您将如何使用它们:

fn main(){
让mid=Id(MyIdentifier(“Hello.to_string());
让uid=Id(MyUserIdentifier{
值:“世界”。到_字符串(),
用户:“Cybran.”to_string(),
});
println!(“{}”,mid);
println!(“{}”,uid);
}
显示
很容易,但是我不认为您可以将
与str
统一起来,正如我上面的示例所示,很可能不同的标识符有不同的字段,而不仅仅是
(公平地说,有些甚至没有
,毕竟
标识符
特性只需要对象实现一个名为
的方法)。从语义上讲,
FromStr
应该从字符串构造一个新实例。因此,我将分别为所有类型实现
FromStr

使用a将类型参数添加到您的
标识符
。这允许您“标记”给定标识符:

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>) {}
使用std:{fmt,marker::PhantomData,str::FromStr};
发布结构标识符{
值:字符串,
_种类:幻影数据,
}
impl fmt::标识符的显示{
fn fmt(&self,f:&mut fmt::Formatter)->fmt::Result{
写!(f,“{}”,self.value)
}
}
标识符的impl-FromStr{
类型Err=();
fn来自_str(s:&str)->结果{
Ok(标识符{
值:s.to_string(),
_种类:幻影数据,
})
}
}
结构用户;
结构组;
fn main(){
让u_id:Identifier=“howdy”.parse().unwrap();
让g_id:Identifier=“howdy”.parse().unwrap();
//do_group_thing(&u_id);//失败
执行组任务(g\U id);
}
fn do_group_thing(id:&标识符){}
错误[E0308]:类型不匹配
-->src/main.rs:32:20
|
32 |做集体的事(&u_id);
|^^^应为结构“组”,找到结构“用户”`
|
=注意:应为类型`&标识符`
找到类型`&标识符`

不过,以上这些并不是我自己真正要做的

我想介绍两种具有相同字段和行为的类型

两种类型不应该有相同的行为-它们应该是相同的类型

我不想复制我刚写的全部代码,我想重新使用它

然后就重用它。我们通过将它们组合为更大类型的一部分,一直重用
String
Vec
等类型。这些类型的行为不像
String
s或
Vec
s,它们只是使用它们

可能标识符是域中的一种基本类型,它应该存在。创建诸如
User
Group
之类的类型,并传递(引用)用户或组。您当然可以添加类型安全性,但这需要程序员承担一定的费用