Hello everyone. I'm studying some low level code used to access memory mapped I/O (specifically the PAC for the RP2040, but I've seen it happen for other MCUs as well). If I understand correctly a RegisterBlock
struct that allows to access the specific registers is defined for each device class (e.g. UART); it then is integrated into a struct for every instance of the device (e.g. UART0); finally a DevicePeripheral
is implemented on top of the device instance type.
All of this using zero cost abstractions to ensure that the ultimate register access is as efficient as possible.
I have yet to understand fully those abstraction and I need help with the first one: the UART0 device instance type, for example, is declared as:
pub struct UART0 {
_marker<PhantomData<* const ()>>;
}
See the exact loc.
Now, I was under the impression that PhantomData
is a zero-sized type useful only to make a type depends on another without needing to actually allocate memory for an instance. Here however the parameter is just *const ()
, which doesn't make UART0
depend on anything.
Why couldn't UART0
just be an empty struct? What does PhantomData
achieve here?