Skip to content

Commit facba6f

Browse files
committed
Add examples for pointer-to-pointer casts
1 parent b339880 commit facba6f

1 file changed

Lines changed: 163 additions & 6 deletions

File tree

src/expressions/operator-expr.md

Lines changed: 163 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -701,28 +701,185 @@ r[expr.as.pointer.behavior]
701701
r[expr.as.pointer.sized]
702702
- If `T` and `U` are both sized, the pointer is returned unchanged.
703703

704+
> [!EXAMPLE]
705+
> ```rust
706+
> let x: i32 = 42;
707+
> let p1: *const i32 = &x;
708+
> let p2: *const u8 = p1 as *const u8;
709+
> // The pointer address remains the same.
710+
> assert_eq!(p1 as usize, p2 as usize);
711+
> ```
712+
704713
r[expr.as.pointer.discard-metadata]
705714
- If `T` is unsized and `U` is sized, the cast discards all metadata that completes the wide pointer `T` and produces a thin pointer `U` consisting of the data part of the unsized pointer.
706715
707-
r[expr.as.pointer.unsized]
716+
> [!EXAMPLE]
717+
> ```rust
718+
> let slice: &[i32] = &[1, 2, 3];
719+
> let ptr: *const [i32] = slice as *const [i32];
720+
> // Cast from wide pointer (*const [i32]) to thin pointer (*const i32)
721+
> // discarding the length metadata.
722+
> let data_ptr: *const i32 = ptr as *const i32;
723+
> assert_eq!(unsafe { *data_ptr }, 1);
724+
> ```
725+
726+
r[expr.as.pointer.unsized.unchanged]
708727
- If `T` and `U` are both unsized, the pointer is also returned unchanged. In particular, the metadata is preserved exactly. The cast can only be performed if the metadata is compatible according to the below rules:
709728
710729
r[expr.as.pointer.unsized.slice]
711730
- When `T` and `U` are unsized with slice metadata, they are always compatible. The metadata of a slice is the number of elements, so casting `*[u16] -> *[u8]` is legal but will result in reducing the number of bytes by half.
712731
732+
> [!EXAMPLE]
733+
> ```rust
734+
> let slice: &[u16] = &[1, 2, 3];
735+
> let ptr: *const [u16] = slice as *const [u16];
736+
> let byte_ptr: *const [u8] = ptr as *const [u8];
737+
> assert_eq!(byte_ptr.len(), 3);
738+
> ```
739+
713740
r[expr.as.pointer.unsized.trait]
714741
- When `T` and `U` are unsized with trait object metadata, the metadata is compatible only when all of the following holds:
715-
1. The principal trait must be the same. (you can't cast from `dyn Foo` to `dyn Bar`)
716-
2. Auto traits may be removed. (you can cast `dyn Foo + Send` to `dyn Foo`)
717-
3. Auto traits may be added only if they are a super trait of the principal trait. (you can cast `dyn Foo` to `dyn Foo + Send` only if `Send` is a super trait of `Foo`)
718-
4. Trailing lifetimes may only be shortened. (you can cast `dyn Foo + 'long` to `dyn Foo + 'short`, but the opposite is not legal)
719-
5. Generics (including lifetimes) and associated types must match exactly. (`*dyn T<'a, A>` -> `*dyn T<'b, B>` requires `'a = 'b` and `A = B`)
742+
1. The principal trait must be the same.
743+
744+
> [!EXAMPLE]
745+
> ```rust,compile_fail,E0606
746+
> trait Foo {}
747+
> trait Bar {}
748+
> impl Foo for i32 {}
749+
> impl Bar for i32 {}
750+
>
751+
> let x: i32 = 42;
752+
> let ptr_foo: *const dyn Foo = &x as *const dyn Foo;
753+
> // You can't cast to a different principal trait.
754+
> let ptr_bar: *const dyn Bar = ptr_foo as *const dyn Bar; // ERROR
755+
> ```
756+
757+
758+
2. Auto traits may be removed.
759+
760+
> [!EXAMPLE]
761+
> ```rust
762+
> trait Foo {}
763+
> struct S;
764+
> impl Foo for S {}
765+
> unsafe impl Send for S {}
766+
>
767+
> let s = S;
768+
> let ptr_send: *const (dyn Foo + Send) = &s;
769+
> // Removing an auto trait.
770+
> let ptr_no_send: *const dyn Foo = ptr_send as *const dyn Foo;
771+
> ```
772+
773+
774+
3. Auto traits may be added only if they are a super trait of the principal trait.
775+
776+
> [!EXAMPLE]
777+
> ```rust
778+
> trait Foo: Send {}
779+
> struct S;
780+
> impl Foo for S {}
781+
> unsafe impl Send for S {}
782+
>
783+
> let s = S;
784+
> let ptr_no_send: *const dyn Foo = &s;
785+
> // Adding an auto trait.
786+
> let ptr_send: *const (dyn Foo + Send) = ptr_no_send as *const (dyn Foo + Send);
787+
> ```
788+
>
789+
> ```rust,compile_fail,E0804
790+
> trait Foo {}
791+
> # struct S;
792+
> # impl Foo for S {}
793+
> # unsafe impl Send for S {}
794+
> #
795+
> # let s = S;
796+
> # let ptr_no_send: *const dyn Foo = &s;
797+
> // Same as above, except trait Foo does not have Send as a super trait.
798+
> let ptr_send: *const (dyn Foo + Send) = ptr_no_send as *const (dyn Foo + Send); // ERROR
799+
> ```
800+
801+
802+
4. Trailing lifetimes may only be shortened.
803+
804+
> [!EXAMPLE]
805+
> ```rust
806+
> trait Foo {}
807+
>
808+
> fn shorten_lifetime<'long: 'short, 'short>(
809+
> ptr: *const (dyn Foo + 'long),
810+
> ) -> *const (dyn Foo + 'short) {
811+
> // Shortening the lifetime is allowed.
812+
> ptr as *const (dyn Foo + 'short)
813+
> }
814+
> ```
815+
>
816+
> ```rust,compile_fail
817+
> trait Foo {}
818+
>
819+
> fn lengthen_lifetime<'long: 'short, 'short>(
820+
> ptr: *const (dyn Foo + 'short),
821+
> ) -> *const (dyn Foo + 'long) {
822+
> // It is not allowed to cast to a longer lifetime.
823+
> ptr as *const (dyn Foo + 'long) // ERROR
824+
> }
825+
> ```
826+
827+
5. Generics (including lifetimes) and associated types must match exactly.
828+
829+
> [!EXAMPLE]
830+
> ```rust,compile_fail,E0606
831+
> trait Generic<T> {}
832+
> impl Generic<i32> for () {}
833+
> impl Generic<u32> for () {}
834+
>
835+
> let x = ();
836+
> let ptr_i32: *const dyn Generic<i32> = &x;
837+
> // You can't cast to a different generic parameter.
838+
> let ptr_u32: *const dyn Generic<u32> = ptr_i32 as *const dyn Generic<u32>; // ERROR
839+
> ```
840+
>
841+
> ```rust
842+
> trait HasType {
843+
> type Output;
844+
> }
845+
>
846+
> trait Generic<'x, T> {}
847+
>
848+
> fn cast_via_associated<'a, 'b, A, B>(
849+
> ptr: *const dyn Generic<'a, A::Output>,
850+
> ) -> *const dyn Generic<'b, B::Output>
851+
> where
852+
> 'a: 'b,
853+
> 'b: 'a,
854+
> A: HasType,
855+
> B: HasType<Output = A::Output>, // Forces equality
856+
> {
857+
> ptr as *const dyn Generic<'b, B::Output>
858+
> }
859+
> ```
860+
720861
721862
Note that [trait upcasting][coerce.unsize.trait-upcast] (including the addition of auto traits) requires a coercion and is not supported by `as` casts.
722863
723864
r[expr.as.pointer.unsized.compound]
724865
- When `T` or `U` is a struct or tuple type whose last field is unsized, it has the same metadata and compatibility rules as its last field.
725866
867+
> [!EXAMPLE]
868+
> ```rust
869+
> struct Wrapper(u32, [u8]);
870+
>
871+
> let slice: &[u8] = &[1, 2, 3];
872+
> let ptr: *const [u8] = slice;
873+
>
874+
> // The metadata (length 3) is preserved when casting to a struct
875+
> // where the last field is the unsized type `[u8]`.
876+
> let wrapper_ptr: *const Wrapper = ptr as *const Wrapper;
877+
>
878+
> // And preserved when casting back.
879+
> let ptr_back: *const [u8] = wrapper_ptr as *const [u8];
880+
> assert_eq!(ptr_back.len(), 3);
881+
> ```
882+
726883
r[expr.assign]
727884
## Assignment expressions
728885

0 commit comments

Comments
 (0)