Macro element_ptr

Source
element_ptr!() { /* proc-macro */ }
Expand description

Returns the address of an inner element without creating unneeded intermediate references.

The general syntax is

element_ptr!(base_ptr => /* element accesses */ )

where base_ptr may be any expression that evaluates to a value of the following types:

All accesses (besides a dereference) will maintain that pointer type of the input pointer. This is especially nice with NonNull<T> because it makes everything involving it much more ergonomic.

§Element accesses

The following a table describes each of the possible accesses that can be inside the macro. These can all be chained by simply putting one after another.

Access KindSyntaxEquivalent Pointer Expression
Field.fieldaddr_of!((*ptr).field)
Index[index]ptr.cast::<T>().add(index)
Add Offset+ count1ptr.add(count)
Sub Offset- count1ptr.sub(count)
Byte Add Offsetu8+ bytes1ptr.byte_add(bytes)
Byte Sub Offsetu8- bytes1ptr.byte_sub(bytes)
Castas T =>2ptr.cast::<T>()
Dereference.*3ptr.read()
Grouping( ... )Just groups the inner accesses for clarity.
  1. `count`/`bytes` may either be an integer literal or an expression wrapped in parentheses.
  2. The `=>` may be omitted if the cast is the last access in a group.
  3. A dereference may return a value that is not a pointer only if it is the final access in the macro. In general it is encouraged to not do this and only use deferencing for inner pointers.

§Safety

  • All of the requirements for offset() must be upheld. This is relevant for every access except for dereferencing, grouping, and casting.
  • The derefence access (.*) unconditionally reads from the pointer, and must not violate any requirements related to that.

§Examples

The following example should give you a general sense of what the macro is capable of, as well as a pretty good reference for how to use it.

use element_ptr::element_ptr;
 
use std::{
    alloc::{alloc, dealloc, Layout, handle_alloc_error},
    ptr,
};
 
struct Example {
    field_one: u32,
    uninit: u32,
    child_struct: ChildStruct,
    another: *mut Example,
}
 
struct ChildStruct {
    data: [&'static str; 6],
}
 
let example = unsafe {
    // allocate two `Example`s on the heap, and then initialize them part by part.
    let layout = Layout::new::<Example>();
     
    let example = alloc(layout).cast::<Example>();
    if example.is_null() { handle_alloc_error(layout) };
 
    let other_example = alloc(layout).cast::<Example>();
    if other_example.is_null() { handle_alloc_error(layout) };
     
    // Get the pointer to `field_one` and initialize it.
    element_ptr!(example => .field_one).write(100u32);
    // But the `uninit` field isn't initialized.
    // We can't take a reference to the struct without causing UB!
     
    // Now initialize the child struct.
    let string = "It is normally such a pain to manipulate raw pointers, isn't it?";
     
    // Get each word from the sentence
    for (index, word) in string.split(' ').enumerate() {
        // and push alternating words to each child struct.
        if index % 2 == 0 {
            // The index can be any arbitrary expression that evaluates to an usize.
            element_ptr!(example => .child_struct.data[index / 2]).write(word);
        } else {
            element_ptr!(other_example => .child_struct.data[index / 2]).write(word);
        }
    }
     
    element_ptr!(example => .another).write(other_example);
     
    example
};
 
 
// Now that the data is initialized, we can read data from the structs.
 
unsafe {
    // The `element_ptr!` macro will get a raw pointer to the data.
    let field_one_ptr: *mut u32 = element_ptr!(example => .field_one);
     
    // This means you can even get a pointer to a field that is not initialized.
    let uninit_field_ptr: *mut u32 = element_ptr!(example => .uninit);
     
    assert_eq!(*field_one_ptr, 100);
 
    let seventh_word = element_ptr!(example => .child_struct.data[3]);
     
    assert_eq!(*seventh_word, "to");
     
    // The `.*` access is used here to go into the pointer to `other_example`.
    // Note that this requires the field `another` to be initialized, but not any
    // of the other fields in `example`.
    // As long as you don't use `.*`, you can be confident that no data will ever
    // be dereferenced.
     
    let second_word = element_ptr!(
        example => .another.*.child_struct.data[0]
    );
 
    assert_eq!(*second_word, "is");
 
    // Now lets deallocate everything so MIRI doesn't yell at me for leaking memory.
    let layout = Layout::new::<Example>();
     
    // Here as a convenience, we can cast the pointer to another type using `as T`.
    dealloc(element_ptr!(example => .another.* as u8), layout);
    // Of course this is simply the same as using `as *mut T`
    dealloc(example as *mut u8, layout);
}