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

impl Trait fails to resolve when returning ! #36375

Open
Cobrand opened this issue Sep 10, 2016 · 9 comments
Open

impl Trait fails to resolve when returning ! #36375

Cobrand opened this issue Sep 10, 2016 · 9 comments

Comments

@Cobrand
Copy link
Contributor

@Cobrand Cobrand commented Sep 10, 2016

I couldn't think of a better title.

This code doesn't compile on nightly :

#![feature(conservative_impl_trait)]
use std::ops::Add ;

fn test() -> impl Add<u32> {
    unimplemented!()
}

fn main() {}
rustc 1.13.0-nightly (378195665 2016-09-08)
error[E0277]: the trait bound `(): std::ops::Add<u32>` is not satisfied

while this does :

#![feature(conservative_impl_trait)]
use std::ops::Add ;

fn test() -> impl Add<u32> {
    if true {
        unimplemented!()
    } else {
        0
    }
}

fn main() {}
@cramertj
Copy link
Member

@cramertj cramertj commented Jan 5, 2018

Currently, ! does not implement Add<u32>. That's the underlying issue here. In order to fix this in general, IMO we should generate automatic ! impls of all traits containing only non-static methods.

@varkor
Copy link
Member

@varkor varkor commented Jan 12, 2018

Considering that ! can coerce to any other type, it seems strange that it can't coerce to any impl Trait (even if it doesn't explicitly implement that trait). It would seem like an inconsistency to only implement traits that contain non-static methods. As long as an object exists that implements that trait, ! could trivially coerce to that type, so you could do it by casting beforehand. I think this should "just work" (assuming there aren't any compatibility issues with doing it).

@hanna-kruppe
Copy link
Contributor

@hanna-kruppe hanna-kruppe commented Jan 12, 2018

The issue is which type you coerce to. There can be multiple, and the choice can be observed if there are static methods: https://play.rust-lang.org/?gist=f8679a05f75f3db559870ad2389713bb&version=nightly

@varkor
Copy link
Member

@varkor varkor commented Jan 12, 2018

@rkruppe: Ah, I see; that's more awkward. The "non-static implementations only" causes issues too, though, because you'd always be able to get around it with this unintuitive if true { ... } else { valid_value } construction. Tricky.

@hanna-kruppe
Copy link
Contributor

@hanna-kruppe hanna-kruppe commented Jan 12, 2018

if true { <diverges> } else { <something of type T> } unambiguously has type T. So there's no ambiguity. It's just a very ugly workaround.

@varkor
Copy link
Member

@varkor varkor commented Jan 12, 2018

Yeah, it's like explicitly casting the ! to any other type (that implements the trait) in the return position — it just feels very ugly, like you say (though casting is possibly somewhat nicer — and in the worst case, we could probably lint this case).

@kennytm
Copy link
Member

@kennytm kennytm commented Jan 12, 2018

@bddap
Copy link

@bddap bddap commented Mar 28, 2019

If () implements the return trait, compilation succeeds.

use std::fmt::Debug;

fn main() {
    println!("{:?}", frob());
}

fn frob() -> impl Debug {
    unimplemented!()
}
@obi1kenobi
Copy link
Contributor

@obi1kenobi obi1kenobi commented Mar 2, 2021

I recently got bitten by this while working with Iterator, which led me to this issue.

The impl Trait RFC names impl Iterator as a major use case for that syntax, since it's a place where exact types get very complex, but code frequently only cares about the impl Iterator part. With current limitations in place, one has to choose between two unappealing options:

  • don't use todo!() or unimplemented!(), which are awesome for prototyping, or
  • specify the exact type, and don't use impl Iterator, which the impl Trait RFC eloquently argues is very unergonomic.

I understand that returning ! in general for impl Trait is difficult to do — but given that impl Iterator is a primary use case of impl Trait, would it make sense to special-case support for returning ! in impl Iterator functions?

As one possible approach, ! could perhaps be made to implement Iterator and simply have a next() that always returns None. A code example is below, though it currently doesn't compile because neither ! nor Iterator are defined in the current crate:

#![feature(never_type)]

impl Iterator for ! {
    type Item = u32;

    fn next(&mut self) -> Option<u32> {
        None
    }
}

fn my_func() -> impl Iterator<Item = u32> {
    todo!()
}

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=c754a0e11b6bc3931d124e41681b2562

I have not figured out a way to implement Iterator<Item = T> for any T, since ! is not generic itself and I don't control its definition so I can't add a PhantomData<&T>. But I'm fairly new to Rust, so perhaps more experienced folks might have some ideas.

Thanks for all your hard work making Rust awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
9 participants