Option::is_none_or (original) (raw)

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

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.