Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
RFC for directly creating a pointer to a place

Summary

There is currently no way to create a raw pointer to a place. This pre-RFC adds support for it to the language.

Motivation

Verbosity of FFI code dealing with raw pointers

A lot of Rust FFI code reads:

let x = 0_u8;
unsafe { foo_ffi(&x as *const _) }

This code is unnecessarily verbose because currently there is no way to create a pointer to a place.

FFI calls on fields of packed structs

FFI code that interfaces with packed structs in Rust reads:

#[derive(Default)] struct A(u8, i32);

let mut a: A = Default::default();
let mut local = a.1; // copy struct field to stack
unsafe { ffi_mod(&mut local as *mut _) }; // pass pointer to local to FFI
a.1 = local; // copy local to struct back

There is no way to pass a pointer to a struct field to an FFI API directly. One always needs to copy the field to the stack, modify it there, and copy it back, even if the ffi_api is designed to handle unaligned memory.

Compute field offsets

There is no general way to compute the offset of a struct field in Rust. This means that naive approaches break, particularly when used as part of code generation (e.g. in rust-bindgen), or behind macros like offset_of!. For example, a naive implementation of a struct field offset would be:

let offset = (&value.field as *const _ as usize) - (&value as *const _ as usize);

However, if value is repr(packed), the behavior of that operation is undefined.

It turns out that it is impossible to write correct code to compute the offset of a repr(Rust, packed) struct field.

User-level / guide-level explanation

Two new operations are added to the language: a way to create a *const T to a place, and a way to create a *mut T to a place, without having to create a reference first.

The strawman syntax used is: *const PLACE and *mut PLACE.

This solves all the problems mentioned above, by constructing a pointer to a place directly without having to construct a reference first. For example, to compute a struct field offset:

let offset = (*const value.field as usize) - (*const value as usize);

the code not only becomes shorter, but more importantly it becomes correct for all structs. It can be put behind a procedural or declarative macro, and there are no corner cases that this code cannot handle.

Grammar

Probably *const PLACE and *mut PLACE can't be added to the language because they clash with the already existing grammar somehow. If this can't be easily fixed, we could use a contextual keyword &raw const foo.x, a sigil @const foo.x, or something else.

Drawbacks

Complicates the language and the grammar.

Alternatives

Many, probably. We could relax the validity of references, to not require them to be dereferenceable or properly aligned. We could make &T as *const _ special, although the RFC that proposes that appears to be coming to the conclusion that this pattern is not worth making especial. We could add different features to tackle the different issues, e.g., an offset_of! intrinsic that magically works, some magic that makes fields of packed structs special, etc. We could also do nothing, and tell people that if they want to compute field offsets, they should make their structs repr(C) and pass the struct to some C code that is actually able to compute the offsets correctly, etc.

Unresolved questions

Many, probably.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.