Есть ли способ прямого доступа к значению поля в структуре enum без сопоставления с образцом? - PullRequest
0 голосов
/ 06 января 2019

Хотелось бы, чтобы перечисления в Rust можно было использовать как продуктивный тип Haskell. Я хочу

  • прямой доступ к значению поля
  • назначить значение поля напрямую или создать клон с изменяющимся значением.

Непосредственно означает, что не используется слишком длинный код сопоставления с образцом, но просто может получить доступ как let a_size = a.size.

В Хаскеле:

data TypeAB = A {size::Int, name::String} | B {size::Int, switch::Bool} deriving Show

main = do
    let a = A 1 "abc"
    let b = B 1 True
    print (size a)      -- could access a field's value directly
    print (name a)      -- could access a field's value directly
    print (switch b)    -- could access a field's value directly
    let aa = a{size=2}  -- could make a clone directly with the changing value
    print aa

Я пробовал два стиля определения перечисления Rust, как

Стиль A:

#[derive(Debug)]
enum EntryType {
    A(TypeA),
    B(TypeB),
}

#[derive(Debug)]
struct TypeA {
    size: u32,
    name: String,
}

#[derive(Debug)]
struct TypeB {
    size: u32,
    switch: bool,
}

fn main() {
    let mut ta = TypeA {
        size: 3,
        name: "TAB".to_string(),
    };
    println!("{:?}", &ta);
    ta.size = 2;
    ta.name = "TCD".to_string();
    println!("{:?}", &ta);

    let mut ea = EntryType::A(TypeA {
        size: 1,
        name: "abc".to_string(),
    });
    let mut eb = EntryType::B(TypeB {
        size: 1,
        switch: true,
    });
    let vec_ab = vec![&ea, &eb];

    println!("{:?}", &ea);
    println!("{:?}", &eb);
    println!("{:?}", &vec_ab);
    // Want to do like `ta.size = 2` for ea
    // Want to do like `ta.name = "bcd".to_string()` for ea
    // Want to do like `tb.switch = false` for eb
    // ????
    println!("{:?}", &ea);
    println!("{:?}", &eb);
    println!("{:?}", &vec_ab);
}

Стиль B:

#[derive(Debug)]
enum TypeCD {
    TypeC { size: u32, name: String },
    TypeD { size: u32, switch: bool },
}

fn main() {
    // NOTE: Rust requires representative struct name before each constructor
    // TODO: Check constructor name can be duplicated
    let mut c = TypeCD::TypeC {
        size: 1,
        name: "abc".to_string(),
    };
    let mut d = TypeCD::TypeD {
        size: 1,
        switch: true,
    };

    let vec_cd = vec![&c, &d];

    println!("{:?}", &c);
    println!("{:?}", &d);
    println!("{:?}", &vec_cd);

    // Can't access a field's value like
    // let c_size = c.size
    let c_size = c.size; // [ERROR]: No field `size` on `TypeCD`
    let c_name = c.name; // [ERROR]: No field `name` on `TypeCD`
    let d_switch = d.switch; // [ERROR]: No field `switch` on `TypeCD`
                             // Can't change a field's value like
                             // c.size = 2;
                             // c.name = "cde".to_string();
                             // d.switch = false;

    println!("{:?}", &c);
    println!("{:?}", &d);
    println!("{:?}", &vec_cd);
}

Я не мог получить доступ / назначить значения напрямую в любом стиле. Должен ли я реализовать функции или черту только для доступа к значению поля? Есть какой-нибудь способ получить вещи, чтобы помочь этой ситуации?

Ответы [ 2 ]

0 голосов
/ 06 января 2019

А как насчет стиля C:

#[derive(Debug)]
enum Color {
    Green { name: String },
    Blue { switch: bool },
}

#[derive(Debug)]
struct Something {
    size: u32,
    color: Color,
}

fn main() {
    let c = Something {
        size: 1,
        color: Color::Green {
            name: "green".to_string(),
        },
    };
    let d = Something {
        size: 2,
        color: Color::Blue { switch: true },
    };

    let vec_cd = vec![&c, &d];

    println!("{:?}", &c);
    println!("{:?}", &d);
    println!("{:?}", &vec_cd);

    let _ = c.size;
}

Если у всех вариантов есть что-то общее, зачем их разделять?


Конечно, мне тоже нужно получить доступ к не общему полю.

Это будет означать, что Rust должен определить, что делать, если фактический тип во время выполнения не содержит требуемого поля. Так что я не думаю, что Раст добавил бы это однажды.

Ты мог бы сделать это сам. Для этого потребуется несколько строк кода, но это соответствует поведению вашего кода на Haskell. Однако я не думаю, что это лучшее, что можно сделать. Haskell - это Haskell, я думаю, вам следует кодировать на Rust, а не пытаться кодировать на Haskell с помощью Rust. По общему правилу, некоторая особенность Rust взята непосредственно из Haskell, но то, что вы хотите здесь, на мой взгляд, очень странно для кода Rust.

#[derive(Debug)]
enum Something {
    A { size: u32, name: String },
    B { size: u32, switch: bool },
}

impl Something {
    fn size(&self) -> u32 {
        match self {
            Something::A { size, .. } => *size,
            Something::B { size, .. } => *size,
        }
    }

    fn name(&self) -> &String {
        match self {
            Something::A { name, .. } => name,
            Something::B { .. } => panic!("Something::B doesn't have name field"),
        }
    }

    fn switch(&self) -> bool {
        match self {
            Something::A { .. } => panic!("Something::A doesn't have switch field"),
            Something::B { switch, .. } => *switch,
        }
    }

    fn new_size(&self, size: u32) -> Something {
        match self {
            Something::A { name, .. } => Something::A {
                size,
                name: name.clone(),
            },
            Something::B { switch, .. } => Something::B {
                size,
                switch: *switch,
            },
        }
    }

    // etc...
}

fn main() {
    let a = Something::A {
        size: 1,
        name: "Rust is not haskell".to_string(),
    };
    println!("{:?}", a.size());
    println!("{:?}", a.name());

    let b = Something::B {
        size: 1,
        switch: true,
    };
    println!("{:?}", b.switch());

    let aa = a.new_size(2);
    println!("{:?}", aa);
}
0 голосов
/ 06 января 2019

Я думаю, что в настоящее время нет встроенного способа доступа к size непосредственно для типа enum. А до тех пор вам может помочь enum_dispatch или решение на основе макросов .

...