如何在 Rust 中为相似但不同的类型重用代码? [英] How do I reuse code for similar yet distinct types in Rust?

查看:68
本文介绍了如何在 Rust 中为相似但不同的类型重用代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a basic type with some functionality, including trait implementations:

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(),
        })
    }
}

这是一个简化的例子,真正的代码会更复杂.

This is a simplified example, real code would be more complex.

我想介绍两种具有与我描述的基本类型相同的字段和行为的类型,例如 MyUserIdentifierMyGroupIdentifier.为避免在使用这些时出错,编译器应将它们视为不同的类型.

I want to introduce two types which have the same fields and behaviour as the basic type I described, for instance MyUserIdentifier and MyGroupIdentifier. To avoid making mistakes when using these, the compiler should treat them as distinct types.

我不想复制我刚写的整个代码,我想重用它.对于面向对象的语言,我会使用继承.我将如何为 Rust 做到这一点?

I don't want to copy the entire code I just wrote, I want to reuse it instead. For object-oriented languages I would use inheritance. How would I do this for Rust?

推荐答案

使用 PhantomData 将类型参数添加到您的 Identifier.这允许您标记"给定的标识符:

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>) {}

error[E0308]: mismatched types
  --> src/main.rs:32:20
   |
32 |     do_group_thing(&u_id);
   |                    ^^^^^ expected struct `Group`, found struct `User`
   |
   = note: expected type `&Identifier<Group>`
              found type `&Identifier<User>`

<小时>

不过,以上并不是我自己实际操作的方式.


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

然后只需重用它.我们一直重用 StringVec 等类型,将它们组合为更大类型的一部分.这些类型的行为不像 Strings 或 Vecs,它们只是使用它们.

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.

也许标识符是您域中的原始类型,它应该存在.创建像 UserGroup 这样的类型并传递(引用)用户或组.您当然可以添加类型安全,但这确实需要程序员付出一些代价.

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.

这篇关于如何在 Rust 中为相似但不同的类型重用代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆