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

Tracking issue for DST coercions (coerce_unsized, unsize) stabilization #27732

Open
aturon opened this issue Aug 12, 2015 · 39 comments
Open

Tracking issue for DST coercions (coerce_unsized, unsize) stabilization #27732

aturon opened this issue Aug 12, 2015 · 39 comments

Comments

@aturon
Copy link
Member

@aturon aturon commented Aug 12, 2015

DST coercions were the last part of DST to land, and are still undergoing some changes.

Before stabilization, at the very least the API surface here needs some scrutiny from the libs team for naming and other conventions issues.

cc @nrc @gankro

Things to consider prior to stabilization

@aturon
Copy link
Member Author

@aturon aturon commented Sep 23, 2015

@nrc, what's your sense of the status for these features? Do you feel ready to stabilize after the libs team takes a look?

Loading

@nrc
Copy link
Member

@nrc nrc commented Sep 23, 2015

My feeling (and it is really just a feeling, I have no data to back this up) is that DST coercions haven't yet got the use they need to prove themselves for stabilisation. I'd love to be wrong about this - if someone could show me a bunch of code doing stuff with DST coercions I'd be very happy. OTOH, I don't think there is anything fundamentally wrong with them, so it would not be a waste of time for the libs team to take a look.

Loading

@photino
Copy link

@photino photino commented Sep 24, 2015

Why is the marker named Unsize? Unsized sounds better, since we already have Sized.

Loading

@arielb1
Copy link
Contributor

@arielb1 arielb1 commented Sep 24, 2015

@photino

It goes the wrong way around - for example, we have [i8; 2]: Unsize<[u8]> (and also [i8; 2]: Unsize<fmt::Debug>). We can swap the order of parameters - U: Unsized<T> if U is thean unsized version of T.

Loading

@rphmeier
Copy link
Contributor

@rphmeier rphmeier commented Dec 18, 2015

Can we move to stabilize this? It's been unchanged for 5 months and I've heard no complaints about it.

Loading

@Gankra
Copy link
Contributor

@Gankra Gankra commented Dec 18, 2015

I don't recall having any issues with this in libstd. The correct way to use this is basically a witchcraft incantation (mostly because these are under-the-covers things one never interacts with in real code), but as far as I know, there's know way to use it dangerously wrong? The whole thing Just Works.

I guess one thing I've never really thought about is why one needs to talk about whether the pointed-to value is unsizeable or whatever. Why can't one simply declare that pointer X is an unsizable one, and have all that junk automatic? Is this desirable to have for generic consumers...?

Loading

@jnicholls
Copy link

@jnicholls jnicholls commented Jan 24, 2016

It's desirable for generic consumers of e.g. Arena allocators that allocate a Sized type that implements a trait unknown to the allocator, and the consumer wants to coerce the allocated result to an allocated trait object.

See https://gitlab.com/Bastacyclop/rust_arena/blob/master/src/lib.rs in the tests. It's a decent use case. How would you recommend doing this otherwise?

Loading

@stevenblenkinsop
Copy link

@stevenblenkinsop stevenblenkinsop commented Jan 29, 2016

Is the lack of support for enums deliberate, temporary, or merely overlooked?

Loading

@aturon
Copy link
Member Author

@aturon aturon commented Feb 1, 2016

I'm nominating this for discussion at the next lang team meeting.

Loading

@Gankra
Copy link
Contributor

@Gankra Gankra commented Feb 2, 2016

@jnicholls maybe I'm missing something, but just mark the one pointer field that will be Unsized as coercable: https://gitlab.com/Bastacyclop/rust_arena/blob/master/src/lib.rs#L41

It seems to me that all the impl<...> CoerceUnsized for ... is trivially derivable from the claim that that field should be coercable.

Loading

@jnicholls
Copy link

@jnicholls jnicholls commented Feb 2, 2016

@gankro Sorry I don't follow what you mean. Can you provide an example?

Loading

@Gankra
Copy link
Contributor

@Gankra Gankra commented Feb 2, 2016

Oops, something clobbered my selection, I meant to link to: https://gitlab.com/Bastacyclop/rust_arena/blob/master/src/lib.rs#L59

Basically instead of

pub struct Allocated<'a, 'b, T: 'b + ?Sized> {
    reference: &'b mut T,
    phantom: PhantomData<&'a ()>
}

impl<'a, 'b, T: ?Sized, U: ?Sized> CoerceUnsized<Allocated<'a, 'b, U>> for Allocated<'a, 'b, T>
    where U: 'b, T: 'b + Unsize<U> {}

You would just do something like:

#[coerce_unsized(T)]
pub struct Allocated<'a, 'b, T: 'b + ?Sized> {
    reference: &'b mut T,
    phantom: PhantomData<&'a ()>
}

Loading

@aturon aturon removed the T-libs-api label Feb 18, 2016
@aturon
Copy link
Member Author

@aturon aturon commented Feb 18, 2016

cc @eddyb

Loading

@aturon
Copy link
Member Author

@aturon aturon commented Feb 18, 2016

Removed T-libs tag, but note that we'd like to review the naming conventions here before stabilization.

Loading

@eddyb
Copy link
Member

@eddyb eddyb commented Feb 18, 2016

There's room for generalization and a good chance we might want Source: Coerce<Target>.
See this comment in response to wanting &mut T reborrows working through structs.

Loading

@apasel422
Copy link
Contributor

@apasel422 apasel422 commented Feb 22, 2016

Right now, because T does not implement Unsize<T>, one cannot use CoerceUnsized to support optional coercions in a generic context. (My use case is supporting DSTs with the placement API). Since &T can trivially be coerced to &T, would it make sense to add the identity impl?

Loading

@tomaka
Copy link
Contributor

@tomaka tomaka commented Feb 23, 2016

I'm not very familiar with how this works, but shouldn't the T of CoerceUnsized be T: ?Sized?
Right now T is required to be Sized, which doesn't make sense for me. Example.

Loading

@arielb1
Copy link
Contributor

@arielb1 arielb1 commented Feb 23, 2016

@tomaka

You implement CoerceUnsized on pointer types, not on DST types.

e.g.

impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}

Loading

@alexcrichton
Copy link
Member

@alexcrichton alexcrichton commented Mar 8, 2016

(adding T-libs as this has a library API associated with it which needs stabilization)

Loading

@nikomatsakis
Copy link
Contributor

@nikomatsakis nikomatsakis commented Mar 11, 2016

So at a recent @rust-lang/lang meeting we discussed this. I think everyone felt...somewhat comfortable with the idea of going forward, but we also wanted to do a thorough review of the current status of the code, along with a comparison against the existing specifications, to make sure everything is well aligned.

Loading

@eddyb
Copy link
Member

@eddyb eddyb commented Mar 11, 2016

@nikomatsakis The problem, though, is that the RFC is intentionally provisional, mostly to get Rc and Arc working with DSTs. I would like to see an attempt at generalization before stabilizing it (I believe the compiler code would "just work" with more than one field being coerced, and various coercion modes, not just Unsize, with minimal changes).

Maybe with specialization we can even encode Deref coercions in a general Coerce trait.

Loading

@Osspial
Copy link

@Osspial Osspial commented Apr 3, 2019

It's been over a year since anyone's really looked at this. What's stopping this from getting stabilized?

Loading

@eddyb
Copy link
Member

@eddyb eddyb commented Apr 4, 2019

The current design is perma-unstable. A new RFC is required for a design that we can consider stabilizing.

The Unsize trait can be ignored, as T: Unsize<U> and *mut T: CoerceUnsized<*mut U> are pretty much equivalent.

So what we should do is generalize CoerceUnsized (rename to Coerce and handle more/all kinds of coercions through it).

Then we can allow even impl<T: Coerce<U>, U> Coerce<Option<U>> for Option<T>, which has been requested for a long while, too.

IMO "custom DSTs" was a red herring, as stabilizing Unsize is unnecessary for making coercions work.

Loading

@eddyb
Copy link
Member

@eddyb eddyb commented Apr 4, 2019

Nominating for discussion, regarding splitting this issue into:

  • Unsize trait, reflects unsizing, may want to wait on "custom DSTs", irrelevant for "opt into unsizing coercions", can stay unstable forever (or even removed)
  • CoerceUnsized, can be generalized to an "opt into structural coercion" Coerce trait, would cover all the cases "CoerceUnsized with Unsize bounds" cover today (and more), can be stabilized more readily

Loading

@AzureMarker
Copy link
Member

@AzureMarker AzureMarker commented May 18, 2019

I just wanted to note (if it hasn't been noted already) that this feature makes it possible to write a struct with a generic trait parameter. This was useful for me because I could then require the other generics to implement the generic trait.

Related internals thread I found helpful:
https://internals.rust-lang.org/t/traits-as-generic-type-parameters/4950

Example (a mockable Rocket request guard):

pub enum Service<'r, Trait, Impl, Mock>
where
    Trait: ?Sized + 'r,
    Impl: Unsize<Trait> + FromRequest<'a, 'r>,
    Mock: Deref + FromRequest<'a, 'r>,
    <Mock as Deref>::Target: Unsize<Trait>
{
    Prod(Impl, PhantomData<&'r Trait>),
    Test(Mock)
}

pub type MyServiceGuard<'r> = Service<
    'r,
    MyServiceTrait,
    MyServiceImpl<'r>,
    rocket::State<'r, MyServiceMock>
>;

Loading

@mathstuf
Copy link
Contributor

@mathstuf mathstuf commented Jun 7, 2019

This feature is how I'm currently performing arc_of_t as arc_of_u given T: U (the type needs extra traits in the application, but a library doesn't know about that trait and just requires a "base" trait). Is there some other way of doing this on stable?

Loading

@eddyb
Copy link
Member

@eddyb eddyb commented May 13, 2020

Came across this again and... how did #27732 (comment) result in nothing? Not even a comment?

Re-nominating (low priority, but should still not be unnominated before a decision).

Loading

@nikomatsakis
Copy link
Contributor

@nikomatsakis nikomatsakis commented May 18, 2020

@eddyb we discussed in @rust-lang/lang meeting today -- splitting off into two feature gates makes sense, provided we have a good description of what the two gates cover and the interaction between them. (e.g., as the issue comments)

Loading

@matthieu-m
Copy link

@matthieu-m matthieu-m commented Feb 7, 2021

Has any consideration been given to having T: Unsize<T> for Sized types?

When attempting to use Unsize in generic contexts, such as in this example, the inability to abstract over sized and unsized types requires splitting APIs in 2, with the same code in each, just because T: !Unsize<T>.

Loading

@Chronos-Sk
Copy link

@Chronos-Sk Chronos-Sk commented May 21, 2021

@matthieu-m That playground example is currently throwing an ICE, in case that's new. Error gist.

Loading

@matthieu-m
Copy link

@matthieu-m matthieu-m commented May 22, 2021

@matthieu-m That playground example is currently throwing an ICE, in case that's new. Error gist.

I think this is new, but as I mentioned because T: !Unsize<T> it didn't compile in the first place.

Loading

@eggyal
Copy link
Contributor

@eggyal eggyal commented Sep 29, 2021

Currently, there's no std/core type for a "sized str" (by which I mean an array of UTF-8 bytes). Of course, one can create a custom version:

use core::str;

#[repr(transparent)]
pub struct Str<const N: usize>([u8; N]);

impl<const N: usize> Str<N> {
    pub fn new(s: [u8; N]) -> Result<Self, str::Utf8Error> {
        str::from_utf8(&s)?;
        Ok(Self(s))
    }

    pub unsafe fn new_unchecked(s: [u8; N]) -> Self {
        Self(s)
    }
}

But such a custom type cannot be "unsized" to str. Shouldn't there be a library type like this, but that implements Unsize<str>?

Loading

@bjorn3
Copy link
Contributor

@bjorn3 bjorn3 commented Sep 29, 2021

impl<const N: usize> Deref for Str<N> {
    type Target = str;
    
    fn deref(&self) -> &str {
        unsafe {
            str::from_utf8_unchecked(&self.0)
        }
    }
}

allows you to "unsize" &Str<N> to &str using &*s.

Loading

@eggyal
Copy link
Contributor

@eggyal eggyal commented Sep 29, 2021

@bjorn3 Of course, but that's not a coercion (the point of this feature, no?). In particular, given s: Str<N>, one cannot write &s as &str—and hence when coercing generic T: Unsize<U> with &t as &U we cannot provide a Str<N> for t.

Loading

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