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

Hierarchy of Sized traits #3729

Open
wants to merge 61 commits into
base: master
Choose a base branch
from
Open
Changes from 5 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
45d9eac
hierarchy of sized traits
davidtwco Oct 4, 2024
dedc078
updates from EuroRust discussions
davidtwco Oct 14, 2024
2eaaddb
updates post niko-discussion
davidtwco Oct 16, 2024
20e7454
add reasoning about constness
davidtwco Oct 17, 2024
8a8c5a9
add diamond hierarchy
davidtwco Oct 17, 2024
592d192
using const traits
davidtwco Oct 21, 2024
9eecd57
improved diagram
davidtwco Oct 22, 2024
55b8779
address review comments
davidtwco Oct 30, 2024
7849df0
future possibility for externref
davidtwco Nov 12, 2024
f5a5cfc
dynsized -> valuesized
davidtwco Nov 14, 2024
d222516
"statically known at runtime" -> "runtime constant"
davidtwco Nov 14, 2024
337c2e3
elaborate on determining runtime-sized size
davidtwco Nov 14, 2024
aa2a39b
const-stable size_of_val + ed. migration detail
davidtwco Nov 14, 2024
226ca5f
remove const pointee
davidtwco Nov 14, 2024
a0f7bb7
elaborate on runtime-sized const prohibition
davidtwco Nov 14, 2024
2eae11e
add acknowledgements section
davidtwco Nov 14, 2024
dcf2b84
mention PhantomData
davidtwco Nov 14, 2024
487de4d
mention extension options
davidtwco Nov 14, 2024
26f0ace
move to correct filename
davidtwco Nov 15, 2024
a840d8b
correct Copy is a subtrait of Sized
davidtwco Nov 15, 2024
260c980
correct supertrait/subtrait
davidtwco Nov 16, 2024
95a1b7d
remove rust= from code blocks
davidtwco Nov 16, 2024
b670517
correct usage of const trait
davidtwco Nov 16, 2024
b8ea881
mention context for dynamic stack allocation
davidtwco Nov 16, 2024
a703feb
use current const trait syntax
davidtwco Nov 16, 2024
fad04b0
correct incorrect syntax
davidtwco Nov 16, 2024
2fb3aaa
list all alternate bounds
davidtwco Nov 16, 2024
23cb8a2
mention ?Trait being accepted for non-Sized
davidtwco Nov 16, 2024
0c2f6e6
use distinct Pointee trait
davidtwco Nov 16, 2024
397e474
correct bound in size_of_val description
davidtwco Nov 16, 2024
6c1482b
update info about size_of_val and align_of_val
davidtwco Nov 16, 2024
1f85898
correct extern types in structs
davidtwco Nov 16, 2024
5f4b8ff
elaborate on implicit relaxation
davidtwco Nov 18, 2024
ab8e7f6
further clarify ?Sized alternative
davidtwco Nov 19, 2024
b050c2b
reword references to relaxed bounds
davidtwco Nov 19, 2024
432a77a
strengthen drawback of implicit relaxation
davidtwco Nov 19, 2024
98af888
mention rfl want
davidtwco Nov 19, 2024
60eab1a
weaken language around externref
davidtwco Nov 19, 2024
6e124e5
further extend ?sized alternatives
davidtwco Nov 19, 2024
4faeb83
remove implicit relaxation terminology
davidtwco Nov 19, 2024
b4258c7
mention backwards incompatiblity with trait methods
davidtwco Nov 25, 2024
61f8d6d
changed aligned future possibility
davidtwco Nov 25, 2024
9158ebb
elaborate on implicit supertrait
davidtwco Nov 25, 2024
b71401c
elaborate further on implicit supertraits
davidtwco Nov 26, 2024
c9e71bd
add alternative about metasized/mutexes
davidtwco Nov 26, 2024
5573be7
further further elaboration on implicit supertraits
davidtwco Nov 27, 2024
2831cca
fix heading level of alternative
davidtwco Nov 27, 2024
ef2f5cd
elaborate on metasized and unsafecell
davidtwco Nov 29, 2024
145446d
clarify return types
davidtwco Nov 29, 2024
b620534
clarify that non-const sized is not nameable
davidtwco Dec 2, 2024
02d1759
clarify which pointee
davidtwco Dec 2, 2024
8cbaef9
clarify relaxed bounds tables
davidtwco Dec 2, 2024
72743b6
further clarifications around relaxed bounds
davidtwco Dec 2, 2024
9fd3dff
runtime-sized in const is unsound
davidtwco Dec 2, 2024
462d016
clarify wording of unresolved metasized question
davidtwco Dec 2, 2024
57b9a85
replace valuesized with metasized
davidtwco Dec 9, 2024
aa21450
elaborate on backwards incompat with methods
davidtwco Dec 9, 2024
c0ee97d
match indentation in code blocks
davidtwco Dec 9, 2024
7a11fe9
fix list of when traits are implemented
davidtwco Dec 12, 2024
762c6d8
add example of clone supertrait relaxation
davidtwco Dec 12, 2024
52b776f
rationale for delaying size_of bound relaxation
davidtwco Dec 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 107 additions & 56 deletions text/3729-sized-hierarchy.md
Original file line number Diff line number Diff line change
Expand Up @@ -541,13 +541,14 @@ edition, existing `Sized` bounds will be rewritten to `const Sized` bounds and n
bare `Sized` bounds will be non-const. `Sized` supertraits (such as that on `Clone`
or `Default`) will not be sugar for `const Sized`, these will remain bare `Sized`.
If traits with a `Sized` supertrait are later made const, then their supertrait
would be made `~const Sized`.
would be made `~const Sized`. In the current edition, due to this proposed migration, there
is no syntax for referring to non-const `Sized`.

As `ValueSized` and `Pointee` are not default bounds, there is no equivalent to `?Sized`
for these traits.

### Implicit `const ValueSized` supertraits
[implicit-const-valuesized-supertrait]: #implicit-const-valuesized-supertrait
[implicit-const-valuesized-supertraits]: #implicit-const-valuesized-supertraits

It is necessary to introduce a implicit default bound of `const ValueSized` on a trait's
`Self` type in order to maintain backwards compatibility (referred to as an implicit
Expand Down Expand Up @@ -865,14 +866,37 @@ could be made to the standard library:
As part of the implementation of this RFC, each `Sized`/`?Sized` bound in
the standard library would need to be reviewed and updated as appropriate.

## Summary of backwards (in)compatibilities
[summary-of-backwards-incompatibilities]: #summary-of-backwards-incompatibilities

In the above sections, this proposal argues that..

- ..adding bounds of new automatically implemented supertraits of a default bound..
- see [*Implementing `Sized`*][implementing-sized].
- ..relaxing a sizedness bound in a free function..
- see [*Implementing `Sized`*][implementing-sized].
- ..relaxing implicit sizedness supertraits..
- see [*Implicit `const ValueSized` supertraits*][implicit-const-valuesized-supertraits].

..is backwards compatible and that..

- ..relaxing a sizedness bound for a generic parameter used as a return type..
- see [*Implementing `Sized`*][implementing-sized].
- ..relaxing a sizedness bound in a trait method..
- see [*Implementing `Sized`*][implementing-sized].
- ..relaxing the bound on an associated type..
- see [*Implementing `Sized`*][implementing-sized].

..is backwards incompatible.

# Drawbacks
[drawbacks]: #drawbacks

- This is a fairly significant change to the `Sized` trait, which has been in
the language since 1.0 and is now well-understood.
- This RFC's proposal that adding a bound of `const Sized`, `const ValueSized`,
`ValueSized` or `Pointee` would remove the default `Sized` bound is a significant
change from the current `?Sized` mechanism.
change from the current `?Sized` mechanism and can be considered confusing.
- Typically adding a trait bound does not remove another trait bound, however
this RFC argues that this behaviour scales better to hierarchies of traits
with default bounds and constness.
Expand Down Expand Up @@ -960,55 +984,63 @@ section][size_of-and-size_of_val] for rationale). In the next edition, `?Sized`
rewritten to `?Sized + const ValueSized` (remove the `Sized` default and add a `const ValueSized`
bound) and bare `?Sized` would only remove the `Sized` default bound.

Prior to the edition migration, the default bound is `Sized`, which could be changed using
Prior to the edition migration, with positive bounds and keeping `?Sized`, the default bound is
`Sized` (interpreted as `const Sized` for backwards compatibility), which could be changed using
the following syntax:

| With positive bounds | Keeping `?Sized` |
| -------------------- | ---------------- |
| `const Sized` | `const Sized` |
| `Sized` | `Sized` |
| `const ValueSized` | `?Sized` |
| `ValueSized` | Not possible |
| `Pointee` | Not possible |

After the edition migration, the default bound is `const Sized`, which could be changed
using the following syntax:

| With positive bounds | Keeping `?Sized` |
| --------------------- | --------------------------------- |
| `const Sized` | `const Sized` |
| `Sized` | `?const Sized + Sized` |
| `const ValueSized` | `?const Sized + const ValueSized` |
| `ValueSized` | `?const Sized + ValueSized` |
| `Pointee` | `?const Sized` |
| Canonically | Syntax with positive bounds | Syntax keeping `?Sized` |
| ------------------ | ------------------------------------ | ------------------------------------ |
| `const Sized` | `T: const Sized`, `T: Sized`, or `T` | `T: const Sized`, `T: Sized`, or `T` |
| `Sized` | Not possible | Not possible |
| `const ValueSized` | `T: const ValueSized` | `T: ?Sized` |
| `ValueSized` | `T: ValueSized` | Not possible |
| `Pointee` | `T: Pointee` | Not possible |

After the edition migration, with positive bounds and keeping `?Sized`, the default bound is
`const Sized`, which could be changed using the following syntax:

| Canonically | Syntax with positive bounds | Syntax keeping `?Sized` |
| ------------------ | --------------------------- | -------------------------------------- |
| `const Sized` | `T: const Sized` or `T` | `T: const Sized` or `T` |
| `Sized` | `T: Sized` | `T: ?(const Sized) + Sized` |
| `const ValueSized` | `T: const ValueSized` | `T: ?(const Sized) + const ValueSized` |
| `ValueSized` | `T: ValueSized` | `T: ?(const Sized) + ValueSized` |
| `Pointee` | `T: Pointee` | `T: ?(const Sized)` |
davidtwco marked this conversation as resolved.
Show resolved Hide resolved

In other words, `?(const Sized)` fully opts out of all default bounds and then one has to
explicitly opt back in.

davidtwco marked this conversation as resolved.
Show resolved Hide resolved
### Adding `?ValueSized`
[adding-valuesized]: #adding-valuesized

Another alternative is to make `ValueSized` a default bound in addition to `Sized` and establish
that relaxing a supertrait bound also implies relaxing subtrait bounds:
that relaxing a supertrait bound also implies relaxing subtrait bounds (but that relaxing a
subtrait bound does not imply relaxing supertrait bounds):

Prior to the edition migration, the default bound is `ValueSized + const ValueSized + Sized`,
which could be changed using:
Prior to the edition migration, when adding `?ValueSized`, the default bound is
`ValueSized + const ValueSized + Sized + const Sized`, which could be changed using:

| With positive bounds | Adding `?ValueSized` |
| --------------------- | -------------------------------- |
| `const Sized` | `const Sized` |
| `Sized` | `Sized` |
| `const ValueSized` | `?Sized` |
| `ValueSized` | `?const ValueSized` |
| `Pointee` | `?ValueSized` |
| Canonically | Syntax with positive bounds | Syntax adding `?ValueSized` |
| ------------------ | ----------------------------------- | ----------------------------------- |
| `const Sized` | `T: const Sized`, `T: Sized` or `T` | `T: const Sized`, `T: Sized` or `T` |
| `Sized` | Not possible | Not possible |
| `const ValueSized` | `T: const ValueSized` | `T: ?(const Sized)` or `T: ?Sized` |
| `ValueSized` | `T: ValueSized` | `T: ?(const ValueSized)` |
| `Pointee` | `T: Pointee` | `T: ?ValueSized` |

After the edition migration, the default bound is
After the edition migration, when adding `?ValueSized`, the default bound remains
`ValueSized + const ValueSized + Sized + const Sized`, which could be changed using:

| With positive bounds | Adding `?ValueSized` |
| -------------------- | -------------------------------- |
| `const Sized` | `const Sized` |
| `Sized` | `?const Sized` |
| `const ValueSized` | `?Sized` |
| `ValueSized` | `?const ValueSized` |
| `Pointee` | `?ValueSized` |
| Canonically | Syntax with positive bounds | Syntax adding `?ValueSized` |
| ------------------ | --------------------------- | --------------------------- |
| `const Sized` | `T: const Sized` or `T` | `T: const Sized` or `T` |
| `Sized` | `T: Sized` | `T: ?(const Sized)` |
| `const ValueSized` | `T: const ValueSized` | `T: ?Sized` |
| `ValueSized` | `T: ValueSized` | `T: ?(const ValueSized)` |
| `Pointee` | `T: Pointee` | `T: ?ValueSized` |

In other words, when a less strict bound is desirable, it is achieved by opting out of the
next strictest bound.

## Why not re-use `std::ptr::Pointee`?
[why-not-re-use-stdptrpointee]: #why-not-re-use-stdptrpointee
Expand Down Expand Up @@ -1104,8 +1136,9 @@ with const functions, adding a lot of complexity to const code.
More importantly, the size of a runtime-sized type could differ between
the host and the target and if the size from a const context were to be used
at runtime with runtime-sized types, then that could result in incorrect
code. It is unintuitive that `const { size_of::<svint8_t>() }` would not
be equal to `size_of::<svint8_t>()`.
code. Not only is it unintuitive that `const { size_of::<svint8_t>() }` would
not be equal to `size_of::<svint8_t>()`, but the layout of types could differ
between const and runtime contexts which would be unsound.

Changing `size_of` and `size_of_val` to `~const Sized` bounds ensures that
`const { size_of:<svint8_t>() }` is not possible.
Expand Down Expand Up @@ -1379,11 +1412,13 @@ this RFC's `ValueSized` trait is inspired, just renamed.
implicit bounds, such as `DynSized`, `Move`, `Leak`, etc. Often rejected due to
the ergonomic cost of relaxed bounds.
- `?Trait` being a negative feature can be confusing to users.
- Depending on the specific trait being added as a default bound, downstream crates
need to re-evaluate every API to determine if adding `?Trait` makes sense, for
each `?Trait` added.
- `?Trait` isn't actually fully backwards compatible due to interactions with
associated types.
- Downstream crates need to re-evaluate every API to determine if adding `?Trait`
makes sense, for each `?Trait` added.
- This is also true of the traits added in this proposal, regardless of whether a
relaxed bound or positive bound syntax is used. However, this proposal argues
that adding supertraits of an existing default bound significantly lessens this
disadvantage (and moreso given the niche use cases of these particular
supertraits).
- This thread was largely motivated by the `Move` trait and that was
replaced by the `Pin` type, but there was an emerging consensus that `DynSized`
may be more feasible due to its relationship with `Sized`.
Expand All @@ -1409,8 +1444,6 @@ this RFC's `ValueSized` trait is inspired, just renamed.
[rfcs#1993][rfc_opaque_data_structs]/[rust#44469][pr_dynsized] but without
being an implicit bound and being able to be a relaxed bound (i.e. no
`?DynSized`).
- Adding new implicit bounds which can be relaxed has backwards
compatibility hazards, see [rfcs#2255][issue_more_implicit_bounds].
- The proposed `DynSized` trait in [rfcs#2310][rfc_dynsized_without_dynsized]
is really quite similar to the `ValueSized` trait proposed by this RFC except:
- It includes an `#[assume_dyn_sized]` attribute to be added to
Expand Down Expand Up @@ -1622,7 +1655,22 @@ longer relevant][zulip_issue_regions_too_simplistic].
# Unresolved questions
[unresolved-questions]: #unresolved-questions

None currently.
- Which syntax should be used for opting out of a default bound with const traits and
a trait hierarchy?
- This RFC is primarily written proposing the "positive bounds" approach, where
introducing a positive bound for a supertrait of the default bound will remove
the default bound.
- Alternatively, described in [*Adding `?ValueSized`*][adding-valuesized], existing
relaxed bounds syntax could be used, where a desired bound is written as opting out
of the next strictest.
- Should `MetaSized` be introduced?
davidtwco marked this conversation as resolved.
Show resolved Hide resolved
- All existing types currently determine their size based on metadata, so this
matches existing semantics.
- Motivations for `MetaSized` are described in the [*What about `MetaSized` instead
of or in addition to `ValueSized`?*][what-about-metasized-instead-of-or-in-addition-to-valuesized].
- What is the precedence for `?const Trait` - `(?const) Trait` or `?(const Trait)`?
- This isn't a question for this RFC to resolve but this RFC takes a conserative
approach and always adds explicit parentheses.

# Future possibilities
[future-possibilities]: #future-possibilities
Expand All @@ -1638,13 +1686,16 @@ None currently.
`trait Clone: Sized` and `T: Clone` is used as a bound of a function
and `Sized` is relied on in that function, then the supertrait of
`Clone` could no longer be relaxed as it can today.
- Consider allowing associated type bounds to be relaxed over an edition.
- i.e. `type Output: if_rust_2021(Sized) + NewAutoTrait` or something like that,
out of scope for this RFC.
- All existing associated types will have at least a `const ValueSized` bound
and relaxing these bounds is a semver-breaking change. It could be worth considering
introducing mechanisms to make this relaxation non-breaking and apply that
automatically over an edition.
- i.e. `type Output: if_rust_2021(Sized) + NewAutoTrait` or something like that,
out of scope for this RFC.
- Consider allowing traits to relax their bounds and having their implementor have
stricter bounds - this would enable traits and implementations to migrate towards
more relaxed bounds.
- This would be unintuitive to callers but would not break existing code.
- This would be unintuitive to callers but would not break existing code.

## externref
[externref]: #externref
Expand Down Expand Up @@ -1745,8 +1796,8 @@ There are various future changes to these traits which could be used to support
custom DSTs on top of this RFC. None of these have been considered thoroughly, and are
written here only to illustrate.

- Allow `Pointee` to be implemented manually on user types, which would replace
the compiler's implementation.
- Allow `std::ptr::Pointee` to be implemented manually on user types, which would
replace the compiler's implementation.
- Introduce a trait like [rfcs#2594][rfc_custom_dst_electric_boogaloo]'s `Contiguous`
which users can implement on their custom DSTs, or add methods to `ValueSized` and
allow it to be implemented by users.
Expand Down