6

RFC: Anonymous Associated Types by knickish · Pull Request #3342 · rust-lang/rfc...

 1 year ago
source link: https://github.com/rust-lang/rfcs/pull/3342
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

@knickish knickish commented Nov 2, 2022

edited

Rendered

Create new anonymous types in trait impl blocks to use as associated types. Short example:

pub trait MyTrait{
    type Fields;
}

pub struct MyStruct{
    field1: usize
}

impl MyTrait for MyStruct{
    type Fields = enum { Field1(usize) };
}
slanterns reacted with confused emoji All reactions

knickish

marked this pull request as ready for review

Nov 2, 2022

ehuss

added the T-lang Relevant to the language subteam, which will review and decide on the RFC. label

Nov 2, 2022

rodrimati1992 commented Nov 4, 2022

edited

Overall, I feel like the intended semantics are too unclear. Are these associated types supposed to be constructible outside of the impl block? Are they opaque outside of the impl block?

What does this RFC accomplish that associated type impl Trait does not:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=0b70bed624069465fc03fdcda950790f

#![feature(type_alias_impl_trait)]

pub trait Builder {
    type Fields;

    fn save_fields(&self) -> Vec<Self::Fields>;
}


pub struct MyStruct {
    field1: usize,
}

const _: () = {
    enum MyStructBuilderFields { 
        Field1(usize) 
    }

    impl Builder for MyStruct {
        type Fields = impl Sized;

        fn save_fields(&self) -> Vec<<Self as Builder>::Fields> {
            vec![MyStructBuilderFields::Field1(self.field1)]
        }
    }
};

The MyStructBuilderFields enum is private to that anonymous constant.

slanterns reacted with thumbs up emoji All reactions

Contributor

clarfonthey commented Nov 4, 2022

Hmm, at least from my gut reaction, this feature seems a little limited. I find it less common that I'll make an associated type that's just deriving things, and more likely that I'll be implementing an entire trait for it, and there's no real way to do that under this model.

Another important thing to note is whether we should limit this idea of anonymous types to other places in the code too. For example, people have mentioned anonymous type fields for parity with a lot of C-style struct definitions, where you could define a type just for a single field. I feel like whatever syntax and decisions we go with here should also be applicable to cases like that, and maybe there's definitely a lot of unexplored potential for anonymous types across the language.

imho enum { ... } and struct { ... } syntax should be reserved for structurally-typed types -- where the identity of the type is not its name (it doesn't have one), but its structure (list of named fields/variants/variant-fields, repr, generic parameters, etc.). These wouldn't be private types, but would be the same type everywhere the same named and typed fields/variants/etc. appear, just like (u8, f32) is the same type everywhere it appears.

so far, the only Rust types that behave like that are tuples, and arguably arrays, pointers, references, and slices.

Author

knickish commented Nov 5, 2022

Author

knickish commented Nov 5, 2022

Overall, I feel like the intended semantics are too unclear. Are these associated types supposed to be constructible outside of the impl block? Are they opaque outside of the impl block?

What does this RFC accomplish that associated type impl Trait does not: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=0b70bed624069465fc03fdcda950790f

#![feature(type_alias_impl_trait)]

pub trait Builder {
    type Fields;

    fn save_fields(&self) -> Vec<Self::Fields>;
}


pub struct MyStruct {
    field1: usize,
}

const _: () = {
    enum MyStructBuilderFields { 
        Field1(usize) 
    }

    impl Builder for MyStruct {
        type Fields = impl Sized;

        fn save_fields(&self) -> Vec<<Self as Builder>::Fields> {
            vec![MyStructBuilderFields::Field1(self.field1)]
        }
    }
};

The MyStructBuilderFields enum is private to that anonymous constant.

Sorry about that, hit close button by accident.

This does work very well for hiding the internals, but that feature has been on nightly since 2019 and has a long way to go it seems like, and is a much larger feature

Author

knickish commented Nov 5, 2022

Hmm, at least from my gut reaction, this feature seems a little limited. I find it less common that I'll make an associated type that's just deriving things, and more likely that I'll be implementing an entire trait for it, and there's no real way to do that under this model.

Another important thing to note is whether we should limit this idea of anonymous types to other places in the code too. For example, people have mentioned anonymous type fields for parity with a lot of C-style struct definitions, where you could define a type just for a single field. I feel like whatever syntax and decisions we go with here should also be applicable to cases like that, and maybe there's definitely a lot of unexplored potential for anonymous types across the language.

I'll try to add some better examples of ways to impl traits for the in trait structs. I agree that more anonymous structs would be nice, but I think that this would fit in fine with that regardless and is a good first step in that direction.

rodrimati1992 commented Nov 5, 2022

edited

This does work very well for hiding the internals, but that feature has been on nightly since 2019 and has a long way to go it seems like, and is a much larger feature

impl Trait in traits is also needed for async methods in traits, so I wouldn't be surprised if it's stabilized before Anonymous Associated Types are stabilized(assuming that this RFC is accepted).

ExpHP commented Nov 9, 2022

edited

This does work very well for hiding the internals, but that feature has been on nightly since 2019 and has a long way to go it seems like, and is a much larger feature

The feature is also unnecessary. If you just make the generated type pub and name it directly in the impl:

pub trait Builder {
    type Fields;

    fn save_fields(&self) -> Vec<Self::Fields>;
}


pub struct MyStruct {
    field1: usize,
}

const _: () = {
    pub enum MyStructBuilderFields {
        Field1(usize)
    }

    impl Builder for MyStruct {
        type Fields = MyStructBuilderFields;

        fn save_fields(&self) -> Vec<<Self as Builder>::Fields> {
            vec![MyStructBuilderFields::Field1(self.field1)]
        }
    }
};

then the anonymous const is still enough to solve all namespacing concerns mentioned in the RFC. The only downside is that this line will appear in the documentation

image

which may cause a user to wonder how they could import MyStructBuilderFields (which they cannot). But your suggestion does not address documentation either (and uses far too much extra syntax budget to justify fixing a simple docs flaw that will already eventually be fixed by impl Trait), so... shrug. In practice, calling the type something spooky and non-case-conforming like _GENERATED_Fields may help people get the idea faster.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK