Immutable borrow of NonZero types as underlying base type

There doesn't seem to be a way to (immutably) borrow a NonZero type (e.g. NonZeroU32) as underlying base type (e.g. u32).

I understand that these are trivially copyable types, so most of the time the .get() method is just fine, but if you need to implement a foreign trait on your newtype there might not be a way around this.

To illustrate the issue:
I'm trying to implement a ToSql trait from the rusqlite crate for my simple newtype wraping NonZeroU32. Normally it should be a simple delegation to the impl for u32, like this:

pub struct NewTypeName(NonZeroU32);

impl rusqlite::ToSql for NewTypeName {
    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
        self.0.as_ref().to_sql()
    }
}

Unfortunately there doesn't seem to be AsRef<u32> implementation for NonZeroU32 or any similar method. Since the traits impl needs to borrow the data I don't see a simple way around this (without leaking).

In my mind it should be completely safe to hand out immutable borrows as the underlying type. Is there a reason there is no AsRef implementation or something similar? Looks like an omission to me.

As a workaround, you could use the bytemuck crate which offers a bunch of safe casts including the one in question:

use std::num::NonZeroU32;
fn demo(r: &NonZeroU32) -> &u32 {
    bytemuck::cast_ref(r)
}

Rust Playground

Thanks.

Looks like my particular use case was actually already merged-in in May:

But since last release is from March ...

Anyway, still interested in the big-picture reasoning behind this. Maybe this could be improved upon?

You can also use the From<i32> for ToSqlOutput<'_> implementation here, which accepts an i32 by-value instead of by-reference.

I’d assume that the only reason it doesn’t exists might be that either no-one has been wanting to add it so far, or it seems not useful enough (after all, users that really need it can always get the functionality via unsafe code, or via third-party crates offering safely wrapped unsafe code). And looking at e.g. the niche that bytemuck fills, there is a question to be asked as to to what end we want such implementations. After all, things like &bool -> &u8 or &u32 -> &i32 aren’t offered in the standard library either; and even &&NonZeroI32 -> &&i32 is sound, something not even bytemuck offers.

On the other hand, &NonZeroI32 -> &i32 does seem more natural than a bunch of the more obscure ones I listed, so (unless someone has tried before and it was rejected), it seems not unlikely to me that something like the AsRef you have in mind (or a method for it) could be accepted for inclusion to the standard library.

1 Like