当特征需要比结构中包含的更多状态时,如何为结构实现特征?

问题描述 投票:0回答:1

当特征需要比结构中包含的更多状态时,如何为结构实现特征?例如,我如何为下面显示的Employee结构实现Human特征?

struct Human {
    name: &str,
}

trait Employee {
    fn id(&self) -> i32;
    fn name(&self) -> &str;
}

impl Employee for Human {
    fn id(&self) -> i32 {
        // From where do I get the ID?
    }
    fn name(&self) -> &str {
        self.name
    }
}

我没有看到任何方法将额外的状态塞进impl或进入特质。

是唯一的选项,创建一个新的HumanToEmployeeAdapter结构,保存缺少的信息,然后为新结构实现Employee特征?

附:我的背景是C#。以下是我用这种语言处理它的方法:

class Human
{
    public string Name { get; }

    public Human(string name) { Name = name; }
}

interface IEmployee
{
    int Id { get; }
    string Name { get; }
}

class HumanToEmployeeAdapter : IEmployee
{
    readonly Human _human;

    public int Id { get; }
    public string Name => _human.Name;

    public HumanToEmployeeAdapter(
        Human human,
        int id)
    {
        _human = human;
        Id = id;
    }
}

您会注意到这是“创建一个新的HumanToEmployeeAdapter结构”路径。那么,这是Rustaceans解决这个问题的方式吗?

oop struct rust traits composition
1个回答
4
投票

您可以几乎完全翻译您的C#代码,如下所示:

struct Human<'a> {
    name: &'a str,
}

trait Employee {
    fn id(&self) -> i32;
    fn name(&self) -> &str;
}

struct HumanToEmployeeAdapter<'a> {
    human: &'a Human<'a>,
    id: i32,
}

impl<'a> HumanToEmployeeAdapter<'a> {
    fn new(id: i32, human: &'a Human<'a>) -> Self {
        HumanToEmployeeAdapter { id, human }
    }
}

impl<'a> Employee for HumanToEmployeeAdapter<'a> {
    fn id(&self) -> i32 {
        self.id
    }

    fn name(&self) -> &str {
        self.human.name
    }
}

如果你的Human类型可以制作Copy(其行为类似于C# value type)那么你可以通过使HumanToEmployeeAdapter拥有Human来简化问题,这意味着你不必担心引用的生命周期:

#[derive(Copy, Clone)]
struct Human<'a> {
    name: &'a str,
}

trait Employee {
    fn id(&self) -> i32;
    fn name(&self) -> &str;
}

struct HumanToEmployeeAdapter<'a> {
    human: Human<'a>,
    id: i32,
}

impl<'a> HumanToEmployeeAdapter<'a> {
    fn new(id: i32, human: Human<'a>) -> Self {
        HumanToEmployeeAdapter { id, human }
    }
}

impl<'a> Employee for HumanToEmployeeAdapter<'a> {
    fn id(&self) -> i32 {
        self.id
    }

    fn name(&self) -> &str {
        self.human.name
    }
}

请注意,您仍然需要跟踪name的生命周期,因为&str是一个参考。如果你把它变成一个拥有的String,那么你就不需要Human的life参数,但是Human不能是Copy。这是因为Strings无法安全地复制到内存中,因为它们的Drop impl(类似于C# finalizer),如果Rust允许你这样做会导致双重释放 - 这就是为什么它没有。

© www.soinside.com 2019 - 2024. All rights reserved.