Skip to content

Option::is_none_or #212

Closed
Closed
@WaffleLapkin

Description

@WaffleLapkin

Proposal

Problem statement

It seems like there is a common use-case to check if an option is None or some condition holds for its value.

Motivation, use-cases

Similarly to how we have Option::is_some_and which is basically .map_or(false, ...), sometimes it is desirable to have the opposite "default" value, i.e. sometimes a better name for .map_or(true, ...) is wanted. See for example comments on the Option::is_some_and tracking issue:

See also 45 occurrences of .map_or(true, ...) in the rustc itself:

list

:~/rust-lib (rust-lib); rg ".map_or\(true" ./compiler --stats -q | rg "\d+ matches"
45 matches
:~/rust-lib (rust-lib); rg ".map_or\(true" ./compiler
./compiler/rustc_middle/src/values.rs
180:                let check_params = def_id.as_local().map_or(true, |def_id| {

./compiler/rustc_middle/src/ty/instance.rs
236:            return ty.ty_adt_def().map_or(true, |adt_def| {

./compiler/rustc_session/src/parse.rs
58:        self.spans.borrow().get(&feature).map_or(true, |spans| spans.is_empty())

./compiler/rustc_passes/src/dead.rs
682:                .map_or(true, |layout| layout.is_zst())

./compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
152:        .map_or(true, |overlap| {

./compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
72:            if file.extension().map_or(true, |ext| ext.to_str().unwrap() != "o") {

./compiler/rustc_hir_analysis/src/check_unused.rs
79:            tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| {

./compiler/rustc_attr/src/builtin.rs
1092:                if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) {

./compiler/rustc_resolve/src/macros.rs
840:            if kind != NonMacroAttrKind::Tool && binding.map_or(true, |b| b.is_import()) {

./compiler/rustc_resolve/src/imports.rs
249:                || max_vis.get().map_or(true, |max_vis| vis.is_at_least(max_vis, self))

./compiler/rustc_parse/src/parser/attr.rs
428:        attr.ident().map_or(true, |ident| {

./compiler/rustc_resolve/src/lib.rs
1066:        if def_id.map_or(true, |def_id| def_id.is_local()) {

./compiler/rustc_resolve/src/diagnostics.rs
285:            self.extern_prelude.get(&ident).map_or(true, |entry| entry.introduced_by_item);
512:                if filter_fn(res) && ctxt.map_or(true, |ctxt| ctxt == key.ident.span.ctxt()) {

./compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
1463:                    .map_or(true, |def_id| self.tcx.object_safety_violations(def_id).is_empty())
1499:                        && last_ty.map_or(true, |last_ty| {

./compiler/rustc_infer/src/infer/mod.rs
1474:            value.as_ref().map_or(true, |value| !value.needs_infer()),

./compiler/rustc_mir_dataflow/src/framework/graphviz.rs
416:        assert!(befores.as_ref().map_or(true, ExactSizeIterator::is_empty));

./compiler/rustc_mir_dataflow/src/framework/direction.rs
290:                    if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
505:                    if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
537:                    if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
563:                    if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {

./compiler/rustc_hir_typeck/src/_match.rs
155:                                && prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty))

./compiler/rustc_hir_typeck/src/lib.rs
490:    trait_did.map_or(true, |trait_did| {

./compiler/rustc_hir_typeck/src/expr.rs
1458:        if count.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) {

./compiler/rustc_hir_typeck/src/coercion.rs
947:                    .map_or(true, |u| u.is_empty()) =>

./compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
992:                .map_or(true, |ty| expected.peel_refs() != ty.peel_refs())

./compiler/rustc_hir_typeck/src/generator_interior/mod.rs
396:            || ty.map_or(true, |ty| {

./compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
2190:                .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx))

./compiler/rustc_hir/src/def.rs
729:        self.ns().map_or(true, |actual_ns| actual_ns == ns)

./compiler/rustc_index/src/interval.rs
234:        current.map_or(true, |x| x < self.domain as u32)

./compiler/rustc_expand/src/config.rs
466:        parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| {
474:        if !self.features.map_or(true, |features| features.stmt_expr_attributes) {

./compiler/rustc_const_eval/src/interpret/util.rs
44:                        let is_used = unused_params.contains(index).map_or(true, |unused| !unused);

./compiler/rustc_const_eval/src/interpret/intern.rs
117:        let frozen = ty.map_or(true, |ty| ty.is_freeze(*ecx.tcx, ecx.param_env));

./compiler/rustc_const_eval/src/interpret/memory.rs
431:                if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {

./compiler/rustc_const_eval/src/interpret/projection.rs
295:            if from.checked_add(to).map_or(true, |to| to > len) {

./compiler/rustc_mir_transform/src/const_prop_lint.rs
722:                            .map_or(true, |layout| layout.is_zst())

./compiler/rustc_mir_transform/src/coverage/graph.rs
387:            if !self.counter_kind.as_ref().map_or(true, |c| c.is_expression()) {

./compiler/rustc_mir_transform/src/const_prop.rs
1142:                            .map_or(true, |layout| layout.is_zst())

./compiler/rustc_mir_transform/src/coverage/spans.rs
484:            if self.prev_expn_span.map_or(true, |prev_expn_span| {

./compiler/rustc_mir_build/src/check_unsafety.rs
172:            ref kind if ExprCategory::of(kind).map_or(true, |cat| cat == ExprCategory::Place) => {

./compiler/rustc_borrowck/src/type_check/mod.rs
1892:                if len.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) {

./compiler/rustc_lint/src/expect.rs
29:                && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))

./compiler/rustc_ast_passes/src/feature_gate.rs
100:                if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) {

Solution sketches

impl<T> Option<T> {
    pub fn is_none_or(self, f: impl FnOnce(T) -> bool) -> bool {
        match self {
            None => true,
            Some(x) => f(x),
        }
    }
}

Links and related work

There is an open PR implementing this, that was untouched for 8 moths: rust-lang/rust#100602

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions