Remove the need to use tuple for enum of enums

Hi.

Is it possible to make this code work without having FileExtension be made up of tuples for SubtitleExtension and VideoExtension?
Play Ground

I see no FileExtension enum and no tuples :confused:

In the code presented, it is not made up of tuples.

This is not considered two tuples?

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum FileExtension {
    Subtitle(SubtitleExtension),
    Video(VideoExtension),
}

A tuple in Rust is a type like (String, i32), or (), there’s also single-element tuples written e.g. as (Foo,).

There’s also “tuple-structs” which are structs that follow a tuple-like syntax. This is about the syntax of

  • construction
  • accessing fields

and it is – crucially – syntax only that distinguishes tuple-structs from other structs. Tuple-structs don’t have field names, but the fields are identified by their order. You access them by index, e.g. foo.0 or by destructuring let Foo(x, y) = foo; while ordinary structs use field names e.g. foo.bar, or destructuring let Bar { number: x, flag: y } = bar;.

E.g. a tuple struct definition can look like

struct Foo(i32, bool);

while an “ordinary” struct looks like

struct Bar {
    number: i32,
    flag: bool,
}

Finally, there’s enums, which have enum variants whose declaration mirrors struct definitions.

Enum variants are not types, so there are no tuple, and not even a tuple-struct in an enum like

enum Foo {
    Variant(i32, bool),
    AnotherVariant(float),
}

but Foo::Variant and Foo::AnotherVariant are … well I don’t actually know if there’s official terminology … but you could call them tuple-struct-style enum variants or something like that. Here’s an enum that also has a unit-struct-style variant and an ordinary-struct-style variant

enum Baz {
    Variant(i32, bool),
    UnitVariant,
    AnotherVariant {
        field: f32,
    },
}

Enum fields are accessed by pattern matching; the difference between the different styles of variants is only syntax, too: Constructors and patterns look different, accordingly. E.g.

let baz1 = Bar::Variant(42, true);
let baz2 = Bar::UnitVariant;
let baz3 = Bar::AnotherVariant { field: 1.5 };
let baz4: Baz = …;
match baz4 {
    Baz::Variant(x, y) => …,
    Baz::UnitVariant => …,
    Baz::AnotherVariant { field: z } => …,
}

P.S.: There’s actually one more difference beyond syntax: Calling the constructors of tuple-structs and tuple-struct-style enum variants doesn’t only look a lot like a function-call, these constructors can also be used as a function. E.g. for struct Foo(i32);, you can then create a function pointer let foo_constructor: fn(i32) -> Foo = Foo; or use the constructor Foo where a closure is expected.

5 Likes

This may be off-topic, but I suggest

impl std::str::FromStr for FileExtension {
    type Err = ExtensionError;
    fn from_str(input: &str) -> Result<Self, Self::Err> {
        let lowered = input.to_ascii_lowercase();
        lowered.parse().map(FileExtension::Subtitle)
            .or_else(|_| lowered.parse().map(FileExtension::Video) )
            .map_err(|_| ExtensionError::Err(input.to_string()) )
    }
}

To avoid duplication.

1 Like

This looks good. I will give it a spin.

I was hoping to have a nicer implementation.

Thanks for this,. It worked really well.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.