From f34e6ff084ec6b7260e2014cbe09f4f44993ea63 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Fri, 27 Nov 2015 17:10:50 -0800 Subject: [PATCH 1/4] Copy in some documentation about which casts are legal --- src/doc/book/casting-between-types.md | 64 ++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/src/doc/book/casting-between-types.md b/src/doc/book/casting-between-types.md index 148c55e4b9732..4c520e3d6a80f 100644 --- a/src/doc/book/casting-between-types.md +++ b/src/doc/book/casting-between-types.md @@ -7,7 +7,7 @@ most dangerous features of Rust! # `as` -The `as` keyword does basic casting: +The `as` keyword does safe casting: ```rust let x: i32 = 5; @@ -15,7 +15,63 @@ let x: i32 = 5; let y = x as i64; ``` -It only allows certain kinds of casting, however: +There are three major categories of safe cast: explicit coercions, casts +between numeric types, and pointer casts. + +Casting is not transitive: even if `e as U1 as U2` is a valid +expression, `e as U2` is not necessarily so (in fact it will only be valid if +`U1` coerces to `U2`). + + +## Explicit coercions + +A cast `e as U` is valid if `e` has type `T` and `T` *coerces* to `U`. + +For example: + +```rust +let a = "hello"; +let b = a as String +``` + +Coercions always occur implicitly so this form is only for clarity. + +## Numeric casts + +A cast `e as U` is also valid in any of the following cases: + + * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* + * `e` is a C-like enum and `U` is an integer type; *enum-cast* + * `e` has type `bool` or `char` and `U` is an integer; *prim-int-cast* + * `e` has type `u8` and `U` is `char`; *u8-char-cast* + +For example + +```rust +let one = true as u8; +let at_sign = 64 as char; +``` + +## Pointer casts + +Perhaps surprisingly, it is safe to cast pointers to and from integers, and +to cast between pointers to different types subject to some constraints. It +is only unsafe to dereference the pointer. + +* `e` has type `*T`, `U` is a pointer to `*U_0`, and either `U_0: Sized` or + unsize_kind(`T`) = unsize_kind(`U_0`); a *ptr-ptr-cast* +* `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* +* `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* +* `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast* +* `e` is a function pointer type and `U` has type `*T`, + while `T: Sized`; *fptr-ptr-cast* +* `e` is a function pointer type and `U` is an integer; *fptr-addr-cast* + + +# `transmute` + +`as` only allows safe casting, and will for example reject an attempt to +cast four bytes into a `u32`: ```rust,ignore let a = [0u8, 0u8, 0u8, 0u8]; @@ -31,13 +87,11 @@ let b = a as u32; // four eights makes 32 ^~~~~~~~ ``` -It’s a ‘non-scalar cast’ because we have multiple values here: the four +This is a ‘non-scalar cast’ because we have multiple values here: the four elements of the array. These kinds of casts are very dangerous, because they make assumptions about the way that multiple underlying structures are implemented. For this, we need something more dangerous. -# `transmute` - The `transmute` function is provided by a [compiler intrinsic][intrinsics], and what it does is very simple, but very scary. It tells Rust to treat a value of one type as though it were another type. It does this regardless of the From d6952121e0a4654c7d94bfe679c97afac8f66ce9 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Fri, 27 Nov 2015 17:35:12 -0800 Subject: [PATCH 2/4] Attempted documentation of coercions Trying to summarize here only the cases that will make sense at the level of the rust book --- src/doc/book/casting-between-types.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/doc/book/casting-between-types.md b/src/doc/book/casting-between-types.md index 4c520e3d6a80f..a5849e2f05113 100644 --- a/src/doc/book/casting-between-types.md +++ b/src/doc/book/casting-between-types.md @@ -5,6 +5,22 @@ different types between each other. The first, `as`, is for safe casts. In contrast, `transmute` allows for arbitrary casting, and is one of the most dangerous features of Rust! +# Coercion + +Coercion between types is implicit and has no explicit syntax. Coercion occurs +in `let`, `const`, and `static` statements; in function call arguments; in +field values in struct initialization; and in a function result. + +The main cases of coercion are: + + * `&mut T` to `&T` + + * `*mut T` to `*const T` + + * `&T` to `*const T` + + * `&mut T` to `*mut T` + # `as` The `as` keyword does safe casting: @@ -31,10 +47,10 @@ For example: ```rust let a = "hello"; -let b = a as String +let b = a as String; ``` -Coercions always occur implicitly so this form is only for clarity. +All coercions will be made implicitly when necessary and unambiguous. ## Numeric casts @@ -58,7 +74,7 @@ Perhaps surprisingly, it is safe to cast pointers to and from integers, and to cast between pointers to different types subject to some constraints. It is only unsafe to dereference the pointer. -* `e` has type `*T`, `U` is a pointer to `*U_0`, and either `U_0: Sized` or +* `e` has type `*T`, `U` has type `*U_0`, and either `U_0: Sized` or unsize_kind(`T`) = unsize_kind(`U_0`); a *ptr-ptr-cast* * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* From 34e6995aa3cec561595d52ca28471d9a6c8dd437 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Sun, 29 Nov 2015 11:54:29 -0800 Subject: [PATCH 3/4] Add information about numeric casts, from the nomicon --- src/doc/book/casting-between-types.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/doc/book/casting-between-types.md b/src/doc/book/casting-between-types.md index a5849e2f05113..ae7c1c661a2c3 100644 --- a/src/doc/book/casting-between-types.md +++ b/src/doc/book/casting-between-types.md @@ -67,6 +67,32 @@ For example let one = true as u8; let at_sign = 64 as char; ``` + +For numeric casts, there are quite a few cases to consider: + +* Casting between two integers of the same size (e.g. i32 -> u32) is a no-op +* Casting from a larger integer to a smaller integer (e.g. u32 -> u8) will + truncate +* Casting from a smaller integer to a larger integer (e.g. u8 -> u32) will + * zero-extend if the source is unsigned + * sign-extend if the source is signed +* Casting from a float to an integer will round the float towards zero + * **[NOTE: currently this will cause Undefined Behavior if the rounded + value cannot be represented by the target integer type][float-int]**. + This includes Inf and NaN. This is a bug and will be fixed. +* Casting from an integer to float will produce the floating point + representation of the integer, rounded if necessary (rounding strategy + unspecified) +* Casting from an f32 to an f64 is perfect and lossless +* Casting from an f64 to an f32 will produce the closest possible value + (rounding strategy unspecified) + * **[NOTE: currently this will cause Undefined Behavior if the value + is finite but larger or smaller than the largest or smallest finite + value representable by f32][float-float]**. This is a bug and will + be fixed. + +[float-int]: https://github.com/rust-lang/rust/issues/10184 +[float-float]: https://github.com/rust-lang/rust/issues/15536 ## Pointer casts From 797d5433c973dadbfe5f9dfbd4de7d0930338847 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Sun, 29 Nov 2015 12:00:07 -0800 Subject: [PATCH 4/4] Rephrased description of casting --- src/doc/book/casting-between-types.md | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/doc/book/casting-between-types.md b/src/doc/book/casting-between-types.md index ae7c1c661a2c3..e2b114b7e54b4 100644 --- a/src/doc/book/casting-between-types.md +++ b/src/doc/book/casting-between-types.md @@ -7,9 +7,12 @@ most dangerous features of Rust! # Coercion -Coercion between types is implicit and has no explicit syntax. Coercion occurs -in `let`, `const`, and `static` statements; in function call arguments; in -field values in struct initialization; and in a function result. +Coercion between types is implicit and has no syntax of its own, but can +be spelled out with [`as`](#explicit-coercions). + +Coercion occurs in `let`, `const`, and `static` statements; in +function call arguments; in field values in struct initialization; and in a +function result. The main cases of coercion are: @@ -21,6 +24,9 @@ The main cases of coercion are: * `&mut T` to `*mut T` + * A custom coercion using [`Deref`](deref-coercions.md) + + # `as` The `as` keyword does safe casting: @@ -50,15 +56,14 @@ let a = "hello"; let b = a as String; ``` -All coercions will be made implicitly when necessary and unambiguous. - ## Numeric casts A cast `e as U` is also valid in any of the following cases: * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* - * `e` is a C-like enum and `U` is an integer type; *enum-cast* - * `e` has type `bool` or `char` and `U` is an integer; *prim-int-cast* + * `e` is a C-like enum (with no data attached to the variants), + and `U` is an integer type; *enum-cast* + * `e` has type `bool` or `char` and `U` is an integer type; *prim-int-cast* * `e` has type `u8` and `U` is `char`; *u8-char-cast* For example @@ -68,7 +73,7 @@ let one = true as u8; let at_sign = 64 as char; ``` -For numeric casts, there are quite a few cases to consider: +The semantics of numeric casts are: * Casting between two integers of the same size (e.g. i32 -> u32) is a no-op * Casting from a larger integer to a smaller integer (e.g. u32 -> u8) will @@ -100,13 +105,20 @@ Perhaps surprisingly, it is safe to cast pointers to and from integers, and to cast between pointers to different types subject to some constraints. It is only unsafe to dereference the pointer. +`e as U` is a valid pointer cast in any of the following cases: + * `e` has type `*T`, `U` has type `*U_0`, and either `U_0: Sized` or - unsize_kind(`T`) = unsize_kind(`U_0`); a *ptr-ptr-cast* + `unsize_kind(T) == unsize_kind(U_0)`; a *ptr-ptr-cast* + * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* + * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* + * `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast* + * `e` is a function pointer type and `U` has type `*T`, while `T: Sized`; *fptr-ptr-cast* + * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast*