Skip to content

Implement Try and FromResidual<!> for () #187

Closed as not planned
Closed as not planned
@Jarcho

Description

@Jarcho

Proposal

Problem statement

Using ControlFlow<Self::T> in a trait where most implementors set T = !. (or Result<_, Self::T>) results in a lot of boilerplate for the implementer.

Motivation, use-cases

Visitors with an early return can be modeled as a trait such as the following (used in rustc):

trait Visitor {
    type BreakTy = !;
    fn visit_foo(&mut self, foo: &Foo) -> ControlFlow<Self::BreakTy> {
        walk_foo(self, foo)
    }
    // And more `visit_*` functions
}

fn walk_foo<V: Visitor>(v: &V, foo: &Foo) -> ControlFlow<V::BreakTy> {
    // walk foo using `?` when needed
    Continue(())
}

With non-branching implementations looking like (actual implementations can get worse):

impl Visitor for Bar {
    fn visit_foo(&mut self, foo: &Foo) -> ControlFlow<!> {
        if some_condition() {
            // do something
        } else if something_else() {
            // do something
        } else {
            walk_foo();
        }
        Continue(())
    }
}

Solution sketches

If Try and FromResidual<!> are implemented for (), then the previous example would instead be:

trait Visitor {
    type ResultTy: Try<Output = ()> = ();
    fn visit_foo(&mut self, foo: &Foo) -> Self::ResultTy {
        walk_foo(self, foo)
    }
    // And more `visit_*` functions
}

fn walk_foo<V: Visitor>(v: &V, foo: &Foo) -> V::ResultTy {
    // walk foo using `?` when needed
    V::ResultTy::from_output(())
}

impl Visitor for Bar {
    fn visit_foo(&mut self, foo: &Foo) {
        if some_condition() {
            // do something
        } else if something_else() {
            // do something
        } else {
            walk_foo();
        }
    }
}

Any impl which requires branching can then set ResultTy to ControlFlow<T> (or Result<(), T> if that makes more sense).


An alternative would be to use a custom ResultTy trait implemented for both () and ControlFlow<T> (and others if needed). This would allow effectively the same interface, except anything which interacts with generic Visitors wouldn't be able to use the ? operator. This would mainly affect the implementation of walk_* functions which would otherwise make heavy use of it.

Downsides would be allowing foo()????? when foo returns (). This is easily solved with a lint any any use of ? on an expression of the type ().

Links and related work

Came up on zulip while discussing using ControlFlow more.

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions