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

Unsized marker for AsRef #28

Open
mexus opened this issue Sep 6, 2018 · 9 comments
Open

Unsized marker for AsRef #28

mexus opened this issue Sep 6, 2018 · 9 comments
Labels
potential-2.0 potential changes for `either` 2.0

Comments

@mexus
Copy link
Contributor

mexus commented Sep 6, 2018

Apparently AsRef implementation of Either lacks an "unsized" marker ?Sized on Target. It prevents of implementing AsRef<str> for the Either, for instance.

While default Either doesn't compile (without the marker on playground) ...

extern crate either; // 1.5.0
use either::Either;

fn get_default(s: Option<impl AsRef<str>>) -> impl AsRef<str> {
    s.map(Either::Left).unwrap_or_else(|| Either::Right("default"))
}

fn main() {
    println!("{}", get_default(None::<String>).as_ref());
    println!("{}", get_default(Some("not default")).as_ref());
}

... an example with the marker compiles just ok :) with the marker on playground

enum Either2<L, R> {
    Left(L),
    Right(R),
}

impl<L, R, T: ?Sized> AsRef<T> for Either2<L, R>
where
    L: AsRef<T>,
    R: AsRef<T>,
{
    fn as_ref(&self) -> &T {
        match self {
            Either2::Left(x) => x.as_ref(),
            Either2::Right(x) => x.as_ref(),
        }
    }
}

fn get_default(s: Option<impl AsRef<str>>) -> impl AsRef<str> {
    s.map(Either2::Left)
        .unwrap_or_else(|| Either2::Right("default"))
}

fn main() {
    println!("{}", get_default(None::<String>).as_ref());
    println!("{}", get_default(Some("not default")).as_ref());
}

So, my question is: was the ?Sized marker omitted on purpose, or should I make a PR which adds the marker? :)

@cuviper
Copy link
Member

cuviper commented Sep 6, 2018

In principle I think it would be fine to have Target: ?Sized on our AsRef, and on AsMut too.

My only hesitation is whether this constitutes a breaking change to expand the scope of the impl. That is, if someone has their own unsized type Foo, can they currently write impl AsRef<Foo> for Either? If so, we'd be adding a conflicting implementation when we make ours unsized.

@mexus
Copy link
Contributor Author

mexus commented Sep 6, 2018

I believe it's impossible: playground

extern crate either; // 1.5.0
use either::Either;

struct Unsized {
    data: [u8],
}

impl<L, R> AsRef<Unsized> for Either<L, R>
where
    L: AsRef<Unsized>,
    R: AsRef<Unsized>,
{
    fn as_ref(&self) -> &Unsized {
        match self {
            Either::Left(x) => x.as_ref(),
            Either::Right(x) => x.as_ref(),
        }
    }
}

produces

error[E0210]: type parameter `L` must be used as the type parameter for some local type (e.g. `MyStruct<L>`)
  --> src/lib.rs:8:1
   |
8  | / impl<L, R> AsRef<Unsized> for Either<L, R>
9  | | where
10 | |     L: AsRef<Unsized>,
11 | |     R: AsRef<Unsized>,
...  |
18 | |     }
19 | | }
   | |_^ type parameter `L` must be used as the type parameter for some local type
   |
   = note: only traits defined in the current crate can be implemented for a type parameter

I've made an unsized type just to be more specific, but I believe it doesn't matter in this case.

@cuviper
Copy link
Member

cuviper commented Sep 6, 2018

Your example works with concrete types though:

impl AsRef<Unsized> for Either<Box<Unsized>, Box<Unsized>> {
    fn as_ref(&self) -> &Unsized {
        match self {
            Either::Left(x) => x.as_ref(),
            Either::Right(x) => x.as_ref(),
        }
    }
}

@mexus
Copy link
Contributor Author

mexus commented Sep 6, 2018

Ah, true! I didn't look that far :( If we allow unsized types in AsRef it will rise a conflict: playground.

So it's a breaking change potentially 😢

@cuviper
Copy link
Member

cuviper commented Sep 6, 2018

I think we could still implement AsRef<str> and AsRef<[T]> though.

@mexus
Copy link
Contributor Author

mexus commented Sep 6, 2018

Okay, sounds good! Will send a PR

@mexus
Copy link
Contributor Author

mexus commented Jul 4, 2019

Btw, isn't it a potential-2.0 change? (i mean adding the ?Sized marker)

@cuviper cuviper added the potential-2.0 potential changes for `either` 2.0 label Jul 4, 2019
@cuviper
Copy link
Member

cuviper commented Jul 4, 2019

Sure, I've added the label.

@nvzqz
Copy link
Contributor

nvzqz commented Jul 28, 2019

It's a shame that expanding the blanket impl is a breaking change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
potential-2.0 potential changes for `either` 2.0
Projects
None yet
Development

No branches or pull requests

3 participants