Macro kernel::pin_init

source ·
macro_rules! pin_init {
    ($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
        $($fields:tt)*
    }) => { ... };
}
Expand description

Construct an in-place, pinned initializer for structs.

This macro defaults the error to Infallible. If you need Error, then use try_pin_init!.

The syntax is almost identical to that of a normal struct initializer:

#[pin_data]
struct Foo {
    a: usize,
    b: Bar,
}

#[pin_data]
struct Bar {
    x: u32,
}

let a = 42;

let initializer = pin_init!(Foo {
    a,
    b: Bar {
        x: 64,
    },
});

Arbitrary Rust expressions can be used to set the value of a variable.

The fields are initialized in the order that they appear in the initializer. So it is possible to read already initialized fields using raw pointers.

IMPORTANT: You are not allowed to create references to fields of the struct inside of the initializer.

Init-functions

When working with this API it is often desired to let others construct your types without giving access to all fields. This is where you would normally write a plain function new that would return a new instance of your type. With this API that is also possible. However, there are a few extra things to keep in mind.

To create an initializer function, simply declare it like this:

impl Foo {
    fn new() -> impl PinInit<Self> {
        pin_init!(Self {
            a: 42,
            b: Bar {
                x: 64,
            },
        })
    }
}

Users of Foo can now create it like this:

let foo = Box::pin_init(Foo::new());

They can also easily embed it into their own structs:

#[pin_data]
struct FooContainer {
    #[pin]
    foo1: Foo,
    #[pin]
    foo2: Foo,
    other: u32,
}

impl FooContainer {
    fn new(other: u32) -> impl PinInit<Self> {
        pin_init!(Self {
            foo1 <- Foo::new(),
            foo2 <- Foo::new(),
            other,
        })
    }
}

Here we see that when using pin_init! with PinInit, one needs to write <- instead of :. This signifies that the given field is initialized in-place. As with struct initializers, just writing the field (in this case other) without : or <- means other: other,.

Syntax

As already mentioned in the examples above, inside of pin_init! a struct initializer with the following modifications is expected:

  • Fields that you want to initialize in-place have to use <- instead of :.
  • In front of the initializer you can write &this in to have access to a NonNull<Self> pointer named this inside of the initializer.
  • Using struct update syntax one can place ..Zeroable::zeroed() at the very end of the struct, this initializes every field with 0 and then runs all initializers specified in the body. This can only be done if Zeroable is implemented for the struct.

For instance:

#[pin_data]
#[derive(Zeroable)]
struct Buf {
    // `ptr` points into `buf`.
    ptr: *mut u8,
    buf: [u8; 64],
    #[pin]
    pin: PhantomPinned,
}
pin_init!(&this in Buf {
    buf: [0; 64],
    ptr: unsafe { addr_of_mut!((*this.as_ptr()).buf).cast() },
    pin: PhantomPinned,
});
pin_init!(Buf {
    buf: [1; 64],
    ..Zeroable::zeroed()
});
This documentation is an old archive. Please see https://rust.docs.kernel.org instead.