Skip to content

[ER] NonZero const generic constructor #73121

Open
@leonardo-m

Description

@leonardo-m

It could be nice to have std::iter::Step for the NonZeroX numbers, so in this code instead of having about 1000 unwrap:

fn main() {
    use std::num::NonZeroUsize;
    let nz = |x| NonZeroUsize::new(x).unwrap();
    for i in 1 .. 1_000 {
        println!("{:?}", nz(i));
    }
}

You only need two:

fn main() {
    use std::num::NonZeroUsize;
    let nz = |x| NonZeroUsize::new(x).unwrap();
    for i in nz(1) .. nz(1_000) {
        println!("{:?}", i);
    }
}

If also a const-generics-based constructor is added to stdlib then the number of run-time unwraps goes to zero (the two panics become compile-time):

fn main() {
    use std::num::nonzero_usize;
    for i in nonzero_usize::<1>() .. nonzero_usize::<1_000>() {
        println!("{:?}", i);
    }
}

A further improvement should come from const generics of arbitrary type (currently not allowed):

fn main() {
    use std::num::nonzero;
    for i in nonzero::<1_usize>() .. nonzero::<1_000>() {
        println!("{:?}", i);
    }
}

Activity

jonas-schievink

jonas-schievink commented on Jun 8, 2020

@jonas-schievink
Contributor

There's not really a point in calling .unwrap() less, the optimizer will get rid of all the calls anyways

leonardo-m

leonardo-m commented on Jun 8, 2020

@leonardo-m
Author

The less unwraps you have, the less you have to think (or in more reliable coding to prove) about possible ways for them to fire at run-time and ruin your execution.

added
C-enhancementCategory: An issue proposing an enhancement or a PR with one.
T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.
on Jun 10, 2020
leonardo-m

leonardo-m commented on Jan 1, 2021

@leonardo-m
Author

I'd like also the .step_by(usize).

the optimizer will get rid of all the calls anyways

I think this isn't always true (I've seen such cases in my code, where the inliner wasn't able to get rid of all the unwrap calls), if you have iterators chains, like (where pred computes a division by the input argument):

(1 .. 1000)
.map(...)
.filter(pred)
...

The compiler is not always able (or willing) to inline everything, so predicates and other lambdas get called by values that are nonzero, but the functions lose the information about the input argument being nonzero.

But if you have a range of NonZero values the type system can't lose this information and new improvements like #79134 give you some extra performance in non-inlined functions:

const NZ1: NonZeroU32 = NonZeroU32::new(1).unwrap();
const NZ1000: NonZeroU32 = NonZeroU32::new(1000).unwrap();

(NZ1 .. NZ1000)
.map(...)
.filter(pred)
...
added
C-feature-requestCategory: A feature request, i.e: not implemented / a PR.
and removed
C-enhancementCategory: An issue proposing an enhancement or a PR with one.
on Jan 1, 2021
jalil-salame

jalil-salame commented on Apr 24, 2023

@jalil-salame
Contributor

This is also being discussed in the libs-team: rust-lang/libs-team#130.

SOF3

SOF3 commented on Sep 6, 2023

@SOF3
Contributor

@leonardo-m it's reasonable that the compiler cannot optimize after you have mapped the integer, right?

added
S-waiting-on-ACPStatus: PR has an ACP and is waiting for the ACP to complete.
on Jan 11, 2024
leonardo-m

leonardo-m commented on Feb 16, 2025

@leonardo-m
Author

Calling nonzero::<1_usize>() is nicer than leaving a unwrap in the code that needs inspection and that the compiler will optimize away. But currently you can't write generic functions like that, and you need specialized ones like nonzero_usize::<1>(), that while still better than using the normal constructor + unwrap, look a bit less appealing. Now Issue #127534 seems to address the range iteration, so I leave this issue open for now just for the unwrap-less constructor.

changed the title [-][ER] NonZeroX Step and better constructors[/-] [+][ER] NonZero const generic constructor[/+] on Feb 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-feature-requestCategory: A feature request, i.e: not implemented / a PR.S-waiting-on-ACPStatus: PR has an ACP and is waiting for the ACP to complete.T-libs-apiRelevant to the library API team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @crlf0710@jonas-schievink@SOF3@leonardo-m@jalil-salame

        Issue actions

          [ER] NonZero const generic constructor · Issue #73121 · rust-lang/rust