Skip to content

Commit 0afa52a

Browse files
committed
Respond to review feedback
1 parent e23240e commit 0afa52a

File tree

1 file changed

+83
-27
lines changed

1 file changed

+83
-27
lines changed

text/0000-offset_of.md

Lines changed: 83 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,16 @@ particular, the implementation should not allocate space for an instance of
229229
time) would otherwise be hard-coded, so in some cases it may reduce the risk
230230
of a compatibility hazard.
231231

232-
2. This is a feature most code won't need to use, and it may be confusing to
232+
2. Similarly, this reduces the amount of dynamism that a Rust implementation
233+
could use for `repr(Rust)` types.
234+
235+
For example, it forbids a Rust implementation from varying field offsets of
236+
`repr(Rust)` types between executions of the same compiled program (for
237+
example, by way of interpretation or code modification), unless it also
238+
performs modifications to adjust the result of `offset_of!` (and recompute
239+
the values of derived constants, and regenerate relevant code, ...).
240+
241+
3. This is a feature most code won't need to use, and it may be confusing to
233242
users unfamiliar with low level programming.
234243

235244
# Rationale and alternatives
@@ -255,6 +264,12 @@ considered:
255264
implementation from `memoffset`, or the implementation they could implement
256265
if they computed it manually.
257266

267+
Needing the offset of fields on `#[repr(Rust)]` is not as common, but still
268+
useful in code which needs to describe precise details of type layout to
269+
some other system, including GPU APIs which accept configurable vertex
270+
formats or binary serialization formats that contain descriptions of the
271+
field offsets for the record types they contain, etc.
272+
258273
3. Require that all fields of `$Container` be visible at the invocation site,
259274
rather than just requiring that `$field` is.
260275

@@ -409,6 +424,28 @@ functionality.
409424
This proposal is intentionally minimal, so there are a number of future
410425
possibilities.
411426

427+
## Nested Field Access
428+
429+
In C, expressions like `offsetof(struct some_struct, foo.bar.baz[3].quux)` are
430+
allowed, where `foo.bar.baz[3].quux` denotes a path to a derived field. This can
431+
be of somewhat arbitrary complexity, accessing fields of nested structs,
432+
performing array indexing (often this is used to access past the end of the
433+
array even), and so on. Similar functionality is offered by
434+
`MemoryLayout.offset` in Swift, where more complex language features are used to
435+
achieve it.
436+
437+
This was omitted from this proposal because it is not commonly used, and can
438+
generally be replaced (at the cost of convenience) by multiple invocations of
439+
the macro.
440+
441+
Additionally, in the future similar functionality could be added in a
442+
backwards-compatible way, either by directly allowing usage like
443+
`offset_of!(SomeStruct, foo.bar.baz[3].quux)`, or by requiring each field be
444+
comma-separated, as in `offset_of!(SomeStruct, foo, bar, baz, [3], quux)`.
445+
446+
Note that while this example shows a combination that supports array indexing,
447+
it's unclear if this is actually desirable for Rust.
448+
412449
## Enum support (`offset_of!(SomeEnum::StructVariant, field_on_variant)`)
413450

414451
Eventually, it may be desirable to allow `offset_of!` to access the fields
@@ -417,7 +454,7 @@ with a primitive integer representation, such as `#[repr(C)]`, `#[repr(int)]`,
417454
or `#[repr(C, int)]` -- where `int` is one of Rust's primitive integer types —
418455
u8, isize, u128, etc).
419456

420-
For example, in the future somthing like the following could be allowed:
457+
For example, in the future something like the following could be allowed:
421458

422459
```rs
423460
use core::mem::offset_of;
@@ -440,27 +477,9 @@ While there are use-cases for this in low level FFI code (similar to the use
440477
cases for `#[repr(int)]` and `#[repr(C, int)]` enums), this may need further
441478
design work, and is left to the future.
442479

443-
## Nested Field Access
444-
445-
In C, expressions like `offsetof(struct some_struct, foo.bar.baz[3].quux)` are
446-
allowed, where `foo.bar.baz[3].quux` denotes a path to a derived field. This can
447-
be of somewhat arbitrary complexity, accessing fields of nested structs,
448-
performing array indexing (often this is used to access past the end of the
449-
array even), and so on. Similar functionality is offered by
450-
`MemoryLayout.offset` in Swift, where more complex language features are used to
451-
achieve it.
452-
453-
This was omitted from this proposal because it is not commonly used, and can
454-
generally be replaced (at the cost of convenience) by multiple invocations of
455-
the macro.
456-
457-
Additionally, in the future similar functionality could be added in a fully
458-
backwards-compatible way, either by directly allowing usage like
459-
`offset_of!(SomeStruct, foo.bar.baz[3].quux)`, or by requiring each field be
460-
comma-separated, as in `offset_of!(SomeStruct, foo, bar, baz, [3], quux)`.
461-
462-
Note that while this example shows a combination that supports array indexing,
463-
it's unclear if this is actually desirable for Rust.
480+
A drawback is that it is unclear how to support these types in the "Nested Field
481+
Access" proposed above, so in the future should we decide to support one of
482+
these, a decision may need to be made about the other.
464483

465484
## `memoffset::span_of!` Functionality
466485

@@ -474,9 +493,46 @@ would be simple to add a similar macro to `core::mem` in the future.
474493

475494
[spanof]: https://docs.rs/memoffset/0.6.5/memoffset/macro.span_of.html
476495

477-
## Support for types with `?Sized` fields.
496+
## Support for types with unsized fields
497+
498+
### ... via `offset_of_val!`
499+
500+
Currently, we don't support use with unsized types. That is, `(A, B, ... [T])`
501+
and/or `(A, B, ..., dyn Foo)`, or their equivalent in structs.
502+
503+
The reason for this is that the offset of the unsized field is not always known,
504+
such as in the case of the last field in `(Foo, dyn SomeTrait)`, where the
505+
offset depends on what the concrete type is. Notably, the compiler must read the
506+
alignment out of the vtable when you access such a field.
507+
508+
This is equivalent to not being able to determine the the size and/or alignment
509+
of `?Sized` types, where we solve it by making the user provide the instance
510+
they're interested in, as in `core::mem::{size_of_val, align_of_val}`, so we
511+
could provide an analogous `core::mem::offset_of_val!($val, $Type, $field)` to
512+
support this case.
513+
514+
It would be reasonable to add this in the future, but is left out for now.
515+
516+
### ... by only forbidding the edge case
517+
518+
The only case where we currently do *not* know the offset of a field statically
519+
is when the user has requested the offset of the unsized field, and the unsized
520+
field is a trait object.
521+
522+
There are valid reasons to want to get the offset of:
523+
1. The fields before the unsized field, as in `offset_of!((i32, dyn Send), 0)`.
524+
2. The unsized field if it's a `[T]`, `str`, or other case where the offset does
525+
not depend on reading the metadata, as in `offset_of!((i32, [u16]), 1)`.
526+
527+
Allowing these is somewhat inconsistent with `align_of`, which could provide the
528+
alignment in some cases, but forbids it for all `?Sized` types (admittedly,
529+
allowing `align_of::<[T]>()` is not particularly compelling, as it's always the
530+
same as `align_of::<T>()`).
478531

479-
Currently, we don't support `offset_of!((u8, [i32]), 1)`, as `(u8, [i32])` does
480-
not implement `Sized`.
532+
Either way, it's trivially backwards compatible for us to eventually start
533+
allowing these, and for the trailing slice/str case, it seems difficult to pin
534+
down the cases where it's allowed without risk of complicating potential future
535+
features (like custom DSTs, extern types, or whatever other new unsized types we
536+
might want to add).
481537

482-
This is a mostly artificial restriction, and could be relaxed in the future.
538+
As such, it's left for future work.

0 commit comments

Comments
 (0)