Repository navigation
Description
This is the tracking issue for the breaking changes made in #136776, #120248, and #136764. The goal of this page is describe why these changes were made and how you can fix code that is affected by them. It also provides a place to ask questions or register a complaint if you feel the changes should not have been made.
What is the error for?
As part of stabilizing the arbitrary_self_types and derive_coerce_pointee we needed to change what raw pointer casts are legal. Specifically:
- Casting
*const dyn Trait + 'ato*const dyn Trait + 'bnow requires that'aoutlives'b - Casting
*const dyn Traitto*const dyn Trait + AutoTraitrequiresTrait: AutoTrait - Casting
*const dyn Trait<'a, T, N>to*const dyn Trait<'b, U, M>requires'a == 'b,T == UandN == M, whereT/Uare type parameters, andN/Mare const parameters
Why was this change made?
Casting these parts of trait objects can invalidate the VTable for the trait object, allowing dispatching to methods that should not be callable.
For points 1 and 2 (and lifetimes from point 3) the trait may have a where Self: 'a or where Self: AutoTrait bound on some of its methods. Extending the lifetime of the trait object, or adding new auto traits would result in new methods being callable which may not be present in the VTable.
For point 3, Trait<T> and Trait<U> may have entirely different VTables even in methods which exist in both VTables. Though, this does include the possibility of new methods becoming callable.
Examples
Extending lifetimes of trait objects
#![forbid(unsafe_code)]
#![feature(arbitrary_self_types, derive_coerce_pointee)]
use std::any::TypeId;
use std::marker::{CoercePointee, PhantomData};
#[derive(CoercePointee)]
#[repr(transparent)]
struct SelfPtr<T: ?Sized>(*const T);
impl<T: ?Sized> std::ops::Deref for SelfPtr<T> {
type Target = T;
fn deref(&self) -> &T {
panic!("please don't call me, I just want the `Receiver` impl!");
}
}
trait GetTypeId {
fn get_type_id(self: SelfPtr<Self>) -> TypeId
where
Self: 'static;
}
impl<T: ?Sized> GetTypeId for PhantomData<T> {
fn get_type_id(self: SelfPtr<Self>) -> TypeId
where
Self: 'static,
{
TypeId::of::<T>()
}
}
// no `T: 'static` bound necessary
fn type_id_of<T: ?Sized>() -> TypeId {
let ptr = SelfPtr(
// This line no longer compiles
&PhantomData::<T> as *const (dyn GetTypeId + '_) as *const (dyn GetTypeId + 'static),
);
ptr.get_type_id()
}Introducing new auto-traits to a trait object
#![feature(arbitrary_self_types)]
trait Trait {
fn f(self: *const Self)
where
Self: Send;
}
impl Trait for *const () {
fn f(self: *const Self) {
unreachable!()
}
}
fn main() {
let unsend: *const () = &();
let unsend: *const dyn Trait = &unsend;
let send_bad: *const (dyn Trait + Send) = unsend as _;
send_bad.f(); // this crashes, since vtable for `*const ()` does not have an entry for `f`
//~^ warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on
}Migrations
Extending lifetimes of trait objects
Existing code can be migrated by replacing the offending raw pointer cast with a transmute. See metrics-rs/metrics#564 as an example of how such a migration can be accomplished. It's advised to only do so if actually sure that extending the lifetime of the trait object is sound.
Introducing new auto-traits to a trait object
If your usage is sound (e.g. because the trait doesn't have auto trait bounds), you can replace cast with a transmute to suppress the error:
trait Cat {}
impl Cat for *const () {}
fn main() {
let unsend: *const () = &();
let unsend: *const dyn Cat = &unsend;
let _send: *const (dyn Cat + Send) = unsafe {
// Safety:
// - Both types are pointers, to the same trait object (and thus have the same vtable)
// - `Cat` does not have methods with `Send` bounds
std::mem::transmute::<*const dyn Cat, *const (dyn Cat + Send)>(unsend)
};
// meow
}Related Links
- Forbid freely casting lifetime bounds of dyn-types #136776
- arbitrary_self_types + derive_coerce_pointee allows calling methods whose where clauses are violated #136702
- Reference updates for forbidding object lifetime changing pointer casts reference#1951
- Make casts of pointers to trait objects stricter #120248
- Pointer casts allow switching trait parameters for trait objects, which doesn’t interact soundly with trait upcasting #120222
- Pointer casts allow switching trait parameters for trait objects, which can be unsound with raw pointers as receiver types under
feature(arbitrary_self_types)#120217 - Make
ptr_cast_add_auto_to_objectlint into hard error #136764 - Tracking issue for future-incompatibility lint
ptr_cast_add_auto_to_object#127323 - Stabilize
feature(trait_upcasting)#134367
Activity
Yarwin commentedon Dec 12, 2025
Hello! I’m one of the godot-rust maintainers.
I think this change in itself is reasonable and acceptable!
There is only one issue, though—according to the crater report which you linked in #136776, we are one of the affected crates, failing with the following output: https://crater-reports.s3.amazonaws.com/pr-136776-2/try%237424ce9ff9baac3e9d9f5dd3c85828cddea0134d/reg/godot-core-0.4.3/log.txt. We were not acknowledged in the Breakage section and we learnt about this breaking change only after the latest nightly update (i.e., the moment when it affected us)—it might even have gone unnoticed up until the stable update if some of our features (web export) didn’t require nightly.
I would advise you to double-check the aforementioned crater report, and notify the maintainers of all the affected crates.
Thanks a lot in advance!
BoxyUwU commentedon Dec 12, 2025
Hmm :/ I don't understand how that got missed, thanks for letting me know
fix: patch spindle for Rust 1.94+ raw pointer lifetime cast soundness…
fix: Resolve trait object lifetime cast error on Rust 1.94+ (#2)
[-]Tracking Issue for breaking lifetime changing raw pointer casts of trait objects[/-][+]Tracking Issue for breaking raw pointer casts of trait objects[/+]fix: re-enable compilation on nightly
fix: re-enable compilation on nightly
11 remaining items
fix: bump tycho-indexer Docker tag from 0.83.4 to 0.153.0
fix(agate/immich): bump mountpoint-s3 1.17.0 -> 1.22.3
fix(agate/immich): bump mountpoint-s3 1.17.0 -> 1.22.3
Pin Rust toolchain to 1.91.0
chore(flake): update unstable and adapt to upstream breakage
Bump metrics to 0.23.1 to fix compiler lifetime regression
Bump metrics 0.24.1 -> 0.24.6 to fix build on newer Rust
s3torchconnectorclient: fix build with Python 3.14 and newer rustc
python3Packages.s3torchconnectorclient: init at 1.5.0
python3Packages.s3torchconnectorclient: init at 1.5.0
text-embeddings-inference: init at 1.9.3
feat(AI): add tabby catalog entry via temporary nixos-25.11 pin
Cargo: bump metrics
Cargo: bump metrics
fix(preproc): keep memory lifetime on ContextKnowledge pointer