rust-lang / rust-lang/rust-clippy

Suggest Option::as_deref{_mut} when calling Option::as_{ref,mut} on Option<Box<T>>

Open
#6,393 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint E-medium T-middle
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

What it does

Suggest replacing calls to Option::<Box<T>>::as_ref and Option::<Box<T>>::as_mut with calls to Option::<Box<T>>::as_deref and Option::<Box<T>>::as_deref_mut.

Calling as_ref and as_mut on an Option<Box<T>> results in &Box<T> or &mut Box<T>, which are references to a pointer, or a double indirection. Changing these Option::as_ref and Option::as_mut calls to Option::as_deref and Option::as_deref_mut forces resolving the underlying &T and &mut T once, which amortizes the cost of following the double pointer over multiple uses of the option field.

These suggestions could also be implemented for other smart pointers like Rc and Arc.

Categories (optional)
  • Kind: performance?

What is the advantage of the recommended code over the original code

The inner Box is derefed once.

Drawbacks

None.

Example
struct StateNotPresentError;

impl std::error::Error for StateNotPresentError {}

impl std::fmt::Display for StateNotPresentError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("State not present for extraction")
    }
}

struct State;

struct Interpreter {
    state: Option<Box<State>>,
}

fn heap_size(interp: &Interpreter) -> Result<String, Box<dyn std::error::Error> {
    let state = interp.state.as_ref().ok_or(StateNotPresentError)?;
    unimplemented!("Heap size not implemented");
    // access `state` multiple times
}

fn eval(interp: &mut Interpreter) -> Result<String, Box<dyn std::error::Error> {
    let state = interp.state.as_mut().ok_or(StateNotPresentError)?;
    unimplemented!("Eval not implemented");
    // access `state` multiple times
}

Could be written as:

struct StateNotPresentError;

impl std::error::Error for StateNotPresentError {}

impl std::fmt::Display for StateNotPresentError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("State not present for extraction")
    }
}

struct State;

struct Interpreter {
    state: Option<Box<State>>,
}

fn heap_size(interp: &Interpreter) -> Result<String, Box<dyn std::error::Error> {
    let state = interp.state.as_deref().ok_or(StateNotPresentError)?;
    unimplemented!("Heap size not implemented");
    // access `state` multiple times
}

fn eval(interp: &mut Interpreter) -> Result<String, Box<dyn std::error::Error> {
    let state = interp.state.as_deref_mut().ok_or(StateNotPresentError)?;
    unimplemented!("Eval not implemented");
    // access `state` multiple times
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start from the requested examples for Option<Box>::as_ref and as_mut, and review the proposed as_deref and as_deref_mut replacements. Determine the lint scope, including whether other smart pointers such as Rc and Arc are included. Done means the intended suggestions and their boundaries are defined well enough to implement and test.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
devtools
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.