Как реализовать черту для структуры, когда черта требует больше состояния, чем содержится в структуре? - PullRequest
0 голосов
/ 25 февраля 2019

Как реализовать черту для структуры, когда черта требует больше состояния, чем содержится в структуре?Например, как бы я реализовал черту 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 для новой структуры?

PS Мой фон находится в 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 struct».Итак, так ли решают эту проблему русты?

1 Ответ

0 голосов
/ 25 февраля 2019

Вы можете перевести свой код 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 #тип значения ) тогда вы можете упростить дело, сделав 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, но тогда Human не может быть Copy.Это потому, что String s не может быть безопасно скопировано в память из-за их Drop impl (аналогично C # finalizer ), что приведет к двойному освобождению, если Rust позволит вам это сделать - чтовот почему это не так.

...