Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Recursive and unsized enums #1151

Open
tomaka opened this Issue Jun 5, 2015 · 8 comments

Comments

Projects
None yet
10 participants
@tomaka
Copy link

tomaka commented Jun 5, 2015

It is currently forbidden to have this enum, because it is recursive:

enum Sequence<T> {
    Element { value: T, next: Sequence<T> },
    End
}

However in theory we could accept it by making Sequence unsized.

@glaebhoerl

This comment has been minimized.

Copy link
Contributor

glaebhoerl commented Jun 6, 2015

I think this is a really cool idea, if it can work (I don't momentarily see any reason why it couldn't, but I haven't thought very hard). Reminds me a bit of serialization formats where you have a flat stream of bytes with tags to indicate what comes next.

@P1start

This comment has been minimized.

Copy link
Contributor

P1start commented Jun 6, 2015

It could be a little tricky to define how to construct one of these. A similar instance of the same problem exists today: it’s valid to to define a struct whose last field is a non-type-parameter unsized type…

struct Dst {
    x: [u8],
}

…but it’s basically impossible to construct one of these, because there’s no Sized equivalent to coerce from. I believe this proposal suffers from the same problem. The obvious solution would be to somehow allow unsized types in struct initialisers, but I’m not sure about how that would work in practice.

@Kimundi

This comment has been minimized.

Copy link
Member

Kimundi commented Jun 7, 2015

@P1start: Afaik @eddyb has a few ideas how to make that work.

@eddyb

This comment has been minimized.

Copy link
Member

eddyb commented Jun 7, 2015

@P1start you would only allow it in an in-place context (&expr, box expr, in place { expr }) and then drag around enough context to save the extra information after (while?) writing the data.
Though panics might be tricky, it should still be possible.

@ticki

This comment has been minimized.

Copy link
Contributor

ticki commented Nov 16, 2015

I think this is really a cool idea, and is needed for the sake of consistency.

@nrc nrc added the T-lang label Aug 29, 2016

@serprex

This comment has been minimized.

Copy link

serprex commented Nov 8, 2016

A useful extension of this is that an unsized enum which is non recursive could also be useful for taking up less space (consider enum BigOrSmall { A([u8; 2000]), B([u16; 10])}). Perhaps a #[repr(unsized)]

@tupshin

This comment has been minimized.

Copy link

tupshin commented Jan 7, 2017

I'll just weigh in in favor of this given my internals question where I unexpectedly ran into this same limitation.

@burdges

This comment has been minimized.

Copy link

burdges commented Jan 7, 2017

There are discussions elsewhere about doing match on "artificial" enums, so another idea might be a trait

trait Match {
    type MatchEnum;
    fn run_match(&self) -> MatchEnum;
    fn run_match_mut(&mut self) -> MatchEnum;
}

so that if impl Match for FakeEnum exists then match fake_enum { .. } becomes match run_match(&fake_enum) { .. } or the mut version depending upon the patterns.

Assuming the trait fields RFC, there is a version with nicer borrowing semantics

trait Match {
    type MatchEnum;
    type EnumField;
    target: EnumField;
    fn run_match(&EnumField) -> MatchEnum;
    fn run_match_mut(&mut EnumField) -> MatchEnum;
}

where match fake_enum { .. } becomes match run_match(&fake_enum.target) { .. }.

We can maybe do the recursive unwrapping these describe using this approach like

struct Sequence<T>([T]);

enum SequenceEnum<T> {
    Element(pub T, &Sequence<T>),
    End
}

impl<T> Match for Sequence<T> {
    type MatchEnum = SequenceEnum<T>;
    fn run_match(&EnumField) -> MatchEnum {
        if self.0.len() > 0 { Element(self.0[0],Sequence<T>(&self.0[1])) else { End }
    }
}

except with lifetimes and unsafety around the borrowing of self.0. And maybe even an explicit length field if [T] lacks that.

All this looks less ergonomic of course, but you could maybe fix that with macros somehow. And it's definitely something lower-level developers run into frequently.

I donno if this approach might place less nicely with future schemes for type-level numerics though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.