Re: [PATCH v7 05/10] rust: io: add IoLoc and IoWrite types

From: Gary Guo

Date: Fri Mar 06 2026 - 10:37:30 EST


On Fri Mar 6, 2026 at 2:32 PM GMT, Alexandre Courbot wrote:
> On Fri Mar 6, 2026 at 10:20 PM JST, Gary Guo wrote:
>> I mean not sure `at` gives me that impression at all. It would just let me know
>> that I am accessing it at a different location. If you omit the `MyRegArray`
>> part then there's no real indication that this is an array to me.
>
> `at` is a function name, we can change it - I picked it because it is
> short and reasonably descriptive. The point being: we have a unique
> function that indicates unambigously that we are using a location for
> an array of registers.

Okay, maybe `at` is not the biggest issue. I just instinctively feel the usage
example being awkward.

Perhaps the fact that `Reg` exist itself is awkward to me. It looks like a type
that exists only for things to typecheck, and not itself represent a meaningful
concept.

>
> You seem to reject this design because the syntax isn't obvious and
> natural to you at first read. We are trying to build something new, so
> of course if will look a bit alien at the first encounter. That's why we
> have documentation, and I think it is not very difficult to wrap your
> head around it after seeing a few examples.
>
>>
>> If `at` is only for array, how would you represent the case where the same type
>> is being used in multiple registers?
>
> That's not something that is supported by the register macro currently
> (probably not a big change, but not something I will do in this series).
>
> But to try and answer your question, such register types would not have
> an `IoLoc` implementation of their own and would need to have their
> location constructed explicitly. In accordance, the construction of
> their value would not bear any location information; thus there would be
> no redundancy.
>
> We do have a case that is pretty close to this with relative registers.
> These are accessed like this (real examples):
>
> bar.write(Reg::of::<Gsp>(regs::NV_PFALCON_FALCON_DMACTL::zeroed()));
>
> and
>
> bar.write(Reg::of::<Sec2>(regs::NV_PFALCON_FALCON_DMACTL::zeroed()));

Relative registers are something that on my list to eliminate and replace with
projections, so I don't particular care about how it looks like.

>
> But for register types that can be used at several arbitrary locations,
> I think this would be even simpler. The different locations would just
> need to implement `IoLoc<T>`, where `T` is the shared register type.
> Then, considering that the two-arguments version is called `write_at`,
> you can simply do:
>
> bar.write_at(REG_LOCATION, reg_value);

Having `write_at` as the name of the two-argument version is okay to me.

>
> ... but this design also makes this possible:
>
> bar.write((REG_LOCATION, reg_value));

I considered about this, but IMO this looks awkward.

>
> Tuples of (location, value) do implement `IoLoc` themselves, so we can
> use this little trick to support a 2-arguments syntax with a single
> method.
>
>>
>>>
>>>>
>>>> If you want to make things more explicit you could also have
>>>> `bar.write(at_array(10), ...)` or something similar.
>>>
>>> Is it possible to generate an `IoLoc<T>` without having `T` mentioned
>>> anywhere in the call to `at_array`?
>>
>> Exactly same as the `impl IoLoc<REG> for usize`:
>>
>> struct AtArray(usize);
>>
>> impl IoLoc<REG> for AtArray {
>> ...
>> }
>
> Right, but can the correct `REG` be inferred when the call to `at_array`
> doesn't bear that information? The type inferred by the second argument
> would have to be propagated to the first. Guess I'll try and see.

RE: inference and bounds checking issue that you mentioned in another email, I
think you can have

fn at_array(i: usize) -> Result<AtArray<T>> { .. }

and

impl IoReg<REG> for AtArray<REG> {}

The type inference here is no different to `Reg::at`.

>
>>
>>>
>>>>
>>>> For the array case I really think trying to shove everything into a single
>>>> argument is a footgun. The type of value in this case *doesn't* tell us the
>>>> location, and the location needs to be explicit.
>>>
>>> bar.write(Reg::at(10, regs::MyRegArray::foo()))
>>>
>>> "write the constructed value at the 10th position of the `MyRegArray`
>>> register array"
>>>
>>> What is missing here?
>>
>> This is completely un-natural if I try to read it with fresh mind (try to forget
>> about implementation details for a second).
>
> That's what documentation is for. Please give it a fair chance and ask
> yourself: would it still look unnatural after working with it for
> 20 minutes?
>
>>
>> `MyRegArray` here is a type name that is a bitfield and not an array. `foo` returns a
>> single value and not an array. "at" here is saying that the register is at a
>> specific location and doesn't really indicate the array nature.
>>
>> This is why I insist that I would prefer an explicit location
>>
>> bar.write(REG_ARRAY.at(10), Reg::foo())
>>
>> would have no ambiguity whatsoever about user's intent.
>
> IIUC `REG_ARRAY` would be a const ZST and `at` a method returning an
> `AtArray(usize)`? I still have doubts that its generic type could be
> inferred automatically but it's worth giving it a go.
>
> If that works, then I assume fixed register writes would look like
>
> bar.write(FIXED, Reg::foo());
>
> Unless we have a specialized `write` variant for them.

That, or `()` as I mentioned.

Best,
Gary