Add `map` for `NonZero` types?

Hi, I thought it may be worthy to add a map function for NonZero<T> types...

I imagine such function would be more or less like this:

pub fn map<U, F>(self, f: F) -> Option<NonZero<U>>
    where
        F: FnOnce(T) -> U,
{
    NonZero::<U>::new(f(self.get()))
}

This would allow writing code like:

let res = NonZeroU64::new(5)?
    .map(|x| x * 10)?
    .map(|x| some_fn_that_does_some_calculations(x))?
    .map(|x| x - 50)?;

I've tried it in the Rust Playground here: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=9eb3ea1ceb79b91b228ddc59fa7d1455

#![feature(try_blocks)]
#![feature(nonzero_internals)]
use std::num::NonZero;
use std::num::NonZeroU64;
use std::num::ZeroablePrimitive;

trait MappableNonZero<T>
where
    T: ZeroablePrimitive,
{
    fn map<U, F>(self, f: F) -> Option<NonZero<U>>
    where
        F: FnOnce(T) -> U,
        U: ZeroablePrimitive;
}

impl<T> MappableNonZero<T> for NonZero<T>
where
    T: ZeroablePrimitive,
{
    fn map<U, F>(self, f: F) -> Option<NonZero<U>>
    where
        F: FnOnce(T) -> U,
        U: ZeroablePrimitive,
    {
        NonZero::<U>::new(f(self.get()))
    }
}

fn some_fn_that_does_some_calculations(x: u64) -> u64 {
    // With 50u64, `res` becomes None
    51u64
}

fn main() {
    let res: Option<u64> = try {
        NonZeroU64::new(5)?
            .map(|x| x * 10)?
            .map(|x| some_fn_that_does_some_calculations(x))?
            .map(|x| x - 50)?
            .get()
    };

    println!("{:?}", res);
}

Quibble: Since the function is fallible, I wouldn't call it "map" (if I read a function called "map", I assume it's more closely analogous to Option::map, array::map, etc.).

The function itself seems reasonable if there's a good name for it, but I'm not sure what it's useful for? It would be helpful to see a motivating example from actual code. I haven't thought of a situation where I would actually want to do a chain of methods and require every intermediate result to also be nonzero.

1 Like