Clippy is warning me about a "deref on an immutable reference" in my code, an example being &*s
where s
is a &String
.
Clippy suggests,
if you would like to reborrow, try removing
&*
or
if you would like to deref, try using
&**
What is the difference between these two suggestions?
The first thing to understand is that Deref
is a trait. If T: Deref<Target=U>
, it means that T
can be seen as a pointer to U
(roughly). This entails that Rust will automatically transform a &T
into a &U
if it needs to (ie. if you are calling a method of U
on a value of type T
). This also triggers when you try to deference T
: Rust will deref
it into U
.
However, this is not the case here: you are dereferencing &String
, which will give a String
(without even thinking about String
as a pointer to str
, because String: Deref<Target=str>
), then you borrow that thing again. If your objective was to end up with a &str
, you failed. Which is why Rust proposes to to deference once again: &** &String -> &* String -> &* &str -> & str -> &str
.
Or, maybe you wanted to do something called reborrow, which has nothing to do with dereferencing. Reborrow is needed when you have a certain kind of borrow, and you need an other kind of borrow. For instance, if you have a &mut T
and wanted a &T
, you could reborrow. Note that a reborrow always specifically means dereferencing a borrow, just to borrow it again: it will not trigger a deref or anything like that, it's just a type-checking step (in practice, a reborrow won't produce any code, it's just for the compiler to reason about; whereas a deref will produce some code).
An example of why you would want to do that is to change the implicit lifetime bounded to a borrow: when you reborrow, you are authorized to have a give smaller lifetime to the new borrow, which allows you to reuse the original borrow once you're done with the reborrowed one. See an example provided by @Cerberus on the playground:
use core::fmt::Debug;
fn debug(val: impl Debug) {
dbg!(val);
}
fn debug_ref(val: &impl Debug) {
dbg!(val);
}
fn main() {
let mut x = 42;
let x_ref = &mut x;
// fails to compile - `x_ref` is moved into `debug`,
// but later we use it again
// debug(x_ref);
// compiles successfully
debug(&mut *x_ref);
// also compiles successfully, since the target type is reference,
// so `x_ref` is implicitly reborrowed
debug_ref(x_ref);
*x_ref = 84;
debug(x_ref);
}
However, in your case, you would transform a regular borrow (that Clippy calls "immutable borrow") into a regular borrow: you're doing nothing! Which is why Clippy suggests that you remove that altogether.