On Fri Oct 10, 2025 at 7:20 PM CEST, Yury Norov wrote:
> On Fri, Oct 10, 2025 at 06:19:17PM +0900, Alexandre Courbot wrote:
>> On Fri Oct 10, 2025 at 1:40 AM JST, Yury Norov wrote:
>     register!(NV_PDISP_VGA_WORKSPACE_BASE @ 0x00625f04 {
>         3:0     cnt => i8,
>         7:4     flags, // implied unsigned
>         31:8    addr,  // implied unsigned
>     });
>
> That answers my question. Can you please highlight this use case
> in commit message?
>
> And just to wrap up:
>
>  - all fields by default are unsigned integers;
>  - one may use '=>' to switch to signed integers, enums or booleans;
>  - all other types are not allowed.

We do allow other types.

In Rust we have the From [1] and TryFrom [2] traits (analogously Into and
TryInto).

This way, if some type T implements, for instance, From<u8> it means that we
can always derive an instance of T from any u8.

Similarly, if T implements TryFrom<u8> we can always derive a Result<T>, which
either contains a valid instance of T or some error that we can handle instead.

Hence, the following is valid is well:

        register!(NV_PFALCON_FALCON_HWCFG1 @ PFalconBase[0x0000012c] {
            3:0     core_rev ?=> FalconCoreRev, "Core revision";
            5:4     security_model ?=> FalconSecurityModel, "Security model";
            7:6     core_rev_subversion as ?=> FalconCoreRevSubversion, "Core 
revision subversion";
        });

The '?=>' notation means TryFrom, hence the generated accessor method - e.g.
security_model() - returns a Result<FalconCoreRevSubversion>.

In this exaple all three types are actually enums, but it would be the exact
same for structs.

In fact, enums in Rust are very different than enums in C anyways and can be
complex types, such as:

        enum Message {
            Quit,
            Move { x: i32, y: i32 },
            Write(String),
            ChangeColor(i32, i32, i32),
        }

[1] https://rust.docs.kernel.org/core/convert/trait.From.html
[2] https://rust.docs.kernel.org/core/convert/trait.TryFrom.html

> I still have a question regarding compile-time flavor of the setter.
> In C we've got a builtin_constant_p, and use it like:
>         
>    static inline int set_base(unsigned int base)
>    {
>         BUILD_BUG_ON_ZERO(const_true(base > MAX_BASE));
>
>         // Eliminated for compile-time 'base'
>         if (base > MAX_BASE)
>                 return -EOVERFLOW;
>
>         __set_base(base);
>
>         return 0;
>    }
>
> Can we do the same trick in rust? Would be nice to have a single
> setter for both compile and runtime cases.

Technically, we could, but it would be very inefficient: Even if the function /
method is called in an infallible way it would still return a Result<T> instead
of just T, which the caller would need to handle.

Analogously, for a setter the function / method would still return a Result,
which must be handled.

Ignoring a returned Result causes a compiler warning:

        warning: unused `core::result::Result` that must be used
          --> drivers/gpu/nova-core/driver.rs:64:9
           |
        64 |         foo();
           |         ^^^^^
           |
           = note: this `Result` may be an `Err` variant, which should be 
handled
           = note: `#[warn(unused_must_use)]` on by default
        help: use `let _ = ...` to ignore the resulting value
           |
        64 |         let _ = foo();
           |         +++++++
        
        warning: 1 warning emitted

This is intentional, users should not be able to ignore error conditions
willy-nilly:

It is a potential source for bugs if errors are ignored. For instance, a caller
might not check the returned Result initially because a compile time check is
expected. However, when changed later on to derive the value at runtime it is
easy to forget handling the Result instead.

Reply via email to