Pre-RFC: returning automatically generating impl Trait

I would expect this to instead be

match condition {
    | true => create_iterator() as AutoEnum<impl Iterator, impl Iterator>,
    | false => generate_iterator(),
}

as after the match does not influence Box coercions currently, so I don't think it would influence any stack box. Then this is getting very close to something that can be implemented by user code (other than needing variadic generics to be able to define it for an arbitrary arity).

1 Like

@Nemo157 oh sure, I was hinting at what the most aesthetic (because symmetrical) situation would be; but if "back-tracking" type inference can only go so far (IIRC, it does work for things such as &mut dyn Iterator...), then so be it.

Aside

Oh indeed, that's kind of what I had in mind, but we do hit the issue for both an automated way to opt into delegation for custom traits, and the lack of variadic generics, although the latter is not a real problem, provided we are given enough impls, such as your Z Y X ... (nit: I would rename those to _25 ... _2 _1 _0, since I find sum_types::_2 to be more readable than sum_types::C :wink:)

But maybe you can publish that crate, "now" that proc-macros are stable?

1 Like

It's always* sound to have an incorrect implementation of a trait which is not unsafe. Thus it makes sense that "auto enum dyn" be limited to safe traits, since the generated implementation may be incorrect w.r.t. unsafe invariants on the trait.

* modulo traits (ab)using privacy barriers to require a closed set of implementations. So, add another requirement: it must be possible to implement the trait at the point where the "auto enum dyn" is constructed. This is even more complicated a property than the "constructability" constraint for the Safer Transmute RFC.

Given that:

  • anyway an explicit marker seems to be needed
  • it's already possible to create this construction using user code

Then I also think that experimenting with a crate is the right way to advance forward (a bit like try! that evolved into ?).

1 Like

Even if proc-macro implementations, can make it work without explicit markers in many cases by analyzing ast.

#[auto_enum]
fn foo(x: i32) -> impl Iterator<Item = i32> {
    match x {
        0 => 1..10,
        _ => vec![5, 10].into_iter(),
    }
}

(The linked example uses the Iterator argument, but it can be detected automatically by analyzing the return type.)

1 Like

I followed the same train of thought. And since non-unsafe traits that rely on the closed set of implementors do exist, we generally cannot go and implement traits for these things.

Well, the two "hypothetical" suggestions I made, either the attribute on the trait definition site, or the impl for that AutoEnum wrapper type, require both that such things be written in the crate that defines the trait, so we wouldn't have to tackle that issue.


In that case, the #[auto_enum] proc-macro is the marker.


I think we all agree that provided some marker, either built-in or through a (proc-)macro, that auto-generates an ad-hoc instance of (a potentially ad-hoc) enum definition, this can "easily" be achieved, provided we target a trait we already know how to make it delegate to each variant.


But I am still concerned about other traits; we should focus on a way to generalize delegations, otherwise this "feature" will be of limited usability.

  • That being said, I like your approach, @taiki-e, of relying on defining custom-derives for a at least some form of extensionability :ok_hand:

And besides unsafe-ty of the trait, visibility / implementability of the trait, there is also the question of associated items that wouldn't be methods.

For instance, given

trait IntoConstLenIterator : IntoIterator {
    const LEN: usize;
}
impl<T, const N: usize> Into... for [T; N] { const LEN: usize = N; }

Then what do we do with the following?

fn foo (cond: bool) -> impl IntoConstLenIterator<IntoIter = ...>
{
    enum match cond { true => [42], false => [42, 27] }
}

There is no sensible delegation for this trait.

More generally, we should restrict ourselves to traits:

  • not having associated functions without a self receiver (otherwise we cannot read the enum discriminant to dispatch),

    • But contrary to our current dyn Trait, self: Self and, more generally, Sized-ness, wouldn't be an issue. Neither would be generic paramers.
  • whose non-function associated items (types and consts) would have to be fixed, similar to dyn Trait.

2 Likes

Ah, I thought "explicit makers" mean "markers to indicate which branch needs to be wrapped by an enum". But, if "explicit makers" mean "any annotation to the code", it's certainly a marker.

It may work well in combination with the crate does delegation.