So yesterday, I (as many probably shall soon) went across my codebase to take advantage of rust 1.26 features, one of which was the default binding modes feature. This basically lets you get rid of a lot of ref
s.
like these ref
s:
match (&self.clean, &self.dirty) {
- (&Some(ref clean), &Some(ref dirty)) => f(clean) != f(dirty),
- (&None, &Some(_)) => true,
- (&Some(_), &None) => false,
- (&None, &None) => unreachable!(),
+ (Some(clean), Some(dirty)) => f(clean) != f(dirty),
+ (None, Some(_)) => true,
+ (Some(_), None) => false,
+ (None, None) => unreachable!(),
}
and this ref
-{ iter.into_iter().enumerate().max_by_key(|&(_, ref v)| v.clone()).map(|(i,_)| i) }
+{ iter.into_iter().enumerate().max_by_key(|(_, v)| v.clone()).map(|(i,_)| i) }
and these refs
let GammaSystemAnalysis {
- ref ev_acousticness, ref ev_polarization,
- ref ev_frequencies, ref unfold_probs,
- ref ev_layer_acousticness,
+ ev_acousticness, ev_polarization,
+ ev_frequencies, unfold_probs,
+ ev_layer_acousticness,
ev_raman_tensors: _,
ev_classifications: _,
layer_sc_mats: _,
- } = *self;
+ } = self;
and these refs
- self.0.as_ref().map(|&(ref a, ref b)| (a,b))
+ self.0.as_ref().map(|(a, b)| (a, b))
and... uh, hold on a second. Did I really just write |(a, b)| (a, b)
? Yes I did, and it's not the identity function; it's turning a &(A, B)
into a (&A, &B)
. I'm probably going to get confused the next time I see this. I hope this doesn't become idiomatic.
This makes me wonder what other WTFs I introduced by changing existing code to use match default bindings. I wonder, what are the best practices here?
My thoughts are something along the following:
-
You should not deliberately change existing code to use default binding modes. Unless it's really sickening to look at like the
clean
/dirty
example. - Regardless, you will still benefit from the mere existence of the feature as you prototype and refactor code, requiring fewer check-edit cycles to get back to a runnable state. Notably, I find that it is common for local bindings of type
T
to become type&T
when extracting a function, and default binding modes will (hopefully) help a lot more code continue to compile even in the face of such type changes.
On the other hand, I would really like if let Some(x) = &self.x
to become the idiomatic way of borrowing fields with types like Option
(replacing if let Some(ref x) = self.x
). It just seems to me that, if that becomes idiomatic, then it's only a short step away from calling this idiomatic:
match &result {
Ok(x) => Ok(x),
Err(e) => Err(e),
}