Skip to content

Combining explicit T: Sized and T: ?Sized bounds is silently accepted and results in T: Sized. #25776

Closed
@eddyb

Description

@eddyb
fn foo<T: Sized + ?Sized>() {}

The above results in no warning or error in 1.0 and 1.1.

While this may seem harmless, the Sized bound can be propagated from a trait:

trait NotObjectSafe: Sized {...}
fn foo<T: NotObjectSafe + ?Sized>() {
    is_sized::<T>();
}
fn is_sized<T: Sized>() {}

This is a backwards-compatibility hazard, as NotObjectSafe cannot be made object-safe (by adding where Self: Sized to each method which requires it) without causing NotObjectSafe + ?Sized to start allowing unsized types for T, where it previously wouldn't have.

A concrete example is trait Clone: Sized which I believe could be object-safe if the Sized bound was moved to its two methods.
Clone: Sized prevents, e.g. [T]: Copy where T: Copy which could be used to write constructors for Rc<[T]> (and perhaps even for Rc<Trait+Copy>).

It's also arguably unintuitive, as ?Sized has no effect but it may show up in documentation, advertising an usecase which isn't actually supported.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-DSTsArea: Dynamically-sized types (DSTs)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions