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

Type conversions #401

Merged
merged 2 commits into from Oct 30, 2014

Conversation

Projects
None yet
@nrc
Copy link
Member

nrc commented Oct 16, 2014

Casting and coercions. Adds custom DST coercions for smart pointers. Otherwise, mostly descriptive, with a few bits of tidying up.

rfc text/

Type conversions
Casting and coercions. Adds custom DST coercions for smart pointers. Otherwise, mostly descriptive, with a few bits of tidying up.
@Gankro

This comment has been minimized.

Copy link
Contributor

Gankro commented Oct 16, 2014


* `&mut T` to `&T`;

* `*mut T` to `*const T`;

This comment has been minimized.

@Gankro

Gankro Oct 16, 2014

Contributor

Finally! ❤️


* coerce_inner(`(..., T)`) = `(..., coerce_inner(T))`.

Note that coercing from sub-trait to a super-trait it a new coercion and is non-

This comment has been minimized.

@Gankro

Gankro Oct 16, 2014

Contributor

"is a new coercion"

Here is an example implementation of `CoerceUnsized` for `Rc`:

```
impl<Sized? T, Sized? U> CoerceUnsized<Rc<T>> for Rc<U> {

This comment has been minimized.

@Gankro

Gankro Oct 16, 2014

Contributor

Does this need some kind of where clause for U: CoerceUnsized<T>?

This comment has been minimized.

@nrc

nrc Oct 16, 2014

Author Member

No, I don't think so. U is just the pointed-to data, it does not need to be coercible. The RcBox is unsized using the fat_pointer_convert intrinsic and automatically has the required Unsize bound.

This comment has been minimized.

@comex

comex Oct 16, 2014

How does that work? As written, it seems like you could [manually] call coerce() to convert any Rc<T> to any other.

This comment has been minimized.

@nrc

nrc Oct 16, 2014

Author Member

Yeah, actually I guess you do need a where clause on the impl - where U: Unsize<T>. Given that where clause the compiler can derive that RcBox<U>: Unsize<RcBox<T>> an thus that we can call fat_pointer_convert.

## Casts

Casting is indicated by the `as` keyword. A cast `e as U` is valid if one of the
following holds:

This comment has been minimized.

@ben0x539

ben0x539 Oct 16, 2014

I don't see it spelled out here (but I guess neither are numeric casts), *T -> *U is gonna remain a cast, right?

This comment has been minimized.

@Gankro

Gankro Oct 16, 2014

Contributor

No casting from u8 to u32 or u32 to u8?

This comment has been minimized.

@nrc

nrc Oct 16, 2014

Author Member

Numeric casts are an omission, I'll add them in.

@ben0x539 what are T and U here? Do you mean an arbitrary cast from one raw pointer type to another?

This comment has been minimized.

@ben0x539

ben0x539 Oct 16, 2014

Yeah, *T -> *U for any T and U with any combination of mutability seems to be accepted right now but I'm not seeing it listed here. Would be sad to see it go away!

it is embedded in the search for candidate methods, but from the point of view
of type conversions, that is not relevant).

Alternatively, a recevier coercion may be thought of as a two stage process.

This comment has been minimized.

@ben0x539

ben0x539 Oct 16, 2014

typo, "recevier"/"receiver"

given by the `Deref` trait), one or zero applications of `coerce_inner` or use
of the `CoerceUnsized` trait (as defined above, note that this requires we are
at a type which has neither references nor dereferences at the top level), and
up to two address-of operations (i.e., `T` to `&T`, `&mut T`, `*const T`, or

This comment has been minimized.

@ben0x539

ben0x539 Oct 16, 2014

When is a second address-of operation necessary?

This comment has been minimized.

@nrc

nrc Oct 16, 2014

Author Member

When the impl is for &T (for some T) and the method takes self by reference (&self). This was common pre-DST, but even with DST it still happens enough to be required.

# Summary

Describe the various kinds of type conversions available in Rust and suggest
some tweeks.

This comment has been minimized.

@michaelsproul

* `T == u8` and `U == char`;

* `T == &[V]` or `T == &[V, ..n]` and `U == *V`.

This comment has been minimized.

@Gankro

Gankro Oct 16, 2014

Contributor

*V?

This comment has been minimized.

@ben0x539

ben0x539 Oct 16, 2014

This is new, isn't it? Can we get &str -> *u8 too?

This comment has been minimized.

@nrc

nrc Oct 16, 2014

Author Member

I guess it should be *const V

This comment has been minimized.

@nrc

nrc Oct 16, 2014

Author Member

This is not new (afaik, I'm sure I pulled this out of the compiler, but it was a while back).

I don't see a reason not to allow the corresponding string cast. Anyone object?

This comment has been minimized.

@ben0x539

ben0x539 Oct 16, 2014

&[V] -> *const V doesn't seem to work, only &[V, .. n] -> *const V.

Also is the mut equivalent intentionally omitted?

This comment has been minimized.

@nrc

nrc Oct 23, 2014

Author Member

Hmm, I probably shouldn't allow casts from &[V] since that is a fat pointer, better to use a function for that. And I guess the same goes for &str to *u8.


* `&T` to `*const T`;

* `&mut T` to `*mut T`;

This comment has been minimized.

@ghost

ghost Oct 16, 2014

How does this interact with raw pointers that denote arrays, like *mut u8 almost always does?

This comment has been minimized.

@nrc

nrc Oct 16, 2014

Author Member

This only allows casts TO *mut T, so I don't think there is an interaction - unless I am missing something?

This comment has been minimized.

@ghost

ghost Oct 16, 2014

If I pass a &mut u8 to copy_memory() that is not an array, will it silently cause a buffer overflow?

This comment has been minimized.

@huonw

huonw Oct 16, 2014

Member

It won't be a buffer overrun if the length is less than or equal to 1; it's trivial to get a buffer overrun with copy_memory even with a real array, by just passing a length greater than the length of the array.

Also, note that we currently support this &mut to *mut coercion; I don't think it has caused too many problems in practice.

@bstrie

This comment has been minimized.

Copy link
Contributor

bstrie commented Oct 16, 2014

Is this the opportunity to discuss possibly allowing numeric types to implicitly coerce when there's no loss of precision, e.g. u8 -> u32? I'm not necessarily advocating for it myself, but it has been requested in the past.

@nrc

This comment has been minimized.

Copy link
Member Author

nrc commented Oct 16, 2014

@bstrie I think that is a big enough change that it deserves its own RFC

```

The `Unsize` trait is a marker trait and a lang item. It should not be
implemented by users and user implementations will be ignored. The compiler will

This comment has been minimized.

@comex

comex Oct 16, 2014

I think it would be better to ban such implementations than ignore them.

This comment has been minimized.

@nrc

nrc Oct 16, 2014

Author Member

That is a reasonable alternative. It would require an ad-hoc check in the type checker though

```
intrinsic fn fat_pointer_convert<Sized? T, Sized? U>(t: *const T) -> *const U
where T : Unsize<U>;
```

This comment has been minimized.

@comex

comex Oct 16, 2014

What is the difference between this and t as *const U?

Alternately, can this be part of the Unsize trait?

This comment has been minimized.

@nrc

nrc Oct 16, 2014

Author Member

t as *const U would allow more conversions than the intrinsic would. E.g., numeric conversions.

@Kimundi

This comment has been minimized.

Copy link
Member

Kimundi commented Oct 16, 2014

Does this make coercion an special case of casts? Eg, is is true that with the changes every implicit coercion can also be written as an explicit cast?

@nrc nrc self-assigned this Oct 16, 2014

@nodakai

This comment has been minimized.

Copy link

nodakai commented Oct 17, 2014

Will generic integer literals be covered by this RFC? (Perhaps they aren't technically coercion...) I have a little frustration about generic literals with minus sign:

fn main() {
    for x in std::iter::range_step(3u32, 0, -1) {
        println!("{}", x);
    }
}

I don't like -1 silently evaluates to some 4 billion of type u32.

@ben0x539

This comment has been minimized.

Copy link

ben0x539 commented Oct 17, 2014

@nodakai fwiw that's unrelated to generics, a plain let x: u32 = -1; already silently evaluates to upwards of 4 billion. I don't think it's a coercion, it's just a legal value of unsigned integer literals.

@nodakai

This comment has been minimized.

Copy link

nodakai commented Oct 18, 2014

@ben0x539 When you say "generics," you mean the <T> things, right? I'm talking about those literals which rustc call "generic integer":

fn main() {
    0.f();
}
<anon>:2:7: 2:10 error: type `<generic integer #0>` does not implement any method in scope named `f`
@ben0x539

This comment has been minimized.

Copy link

ben0x539 commented Oct 18, 2014

@nodakai Yeah, I thought you were talking about the <T> in fn range_step<T: ...>. Disregard that, I must have been confused. :)

@abonander

This comment has been minimized.

Copy link

abonander commented Oct 18, 2014

I would like &T to coerce to T where T is a primitive. It would eliminate so many unnecessary explicit derefs.

@nrc nrc referenced this pull request Oct 27, 2014

Closed

Raw pointer coercion #361

@alexcrichton alexcrichton force-pushed the rust-lang:master branch from b9e2b8c to 5020131 Oct 29, 2014

@aturon aturon merged commit d65c19d into rust-lang:master Oct 30, 2014

@aturon

This comment has been minimized.

Copy link
Member

aturon commented Oct 30, 2014

Merged. Discussion. Tracking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.