Description
C-DEREF unambiguously says that Deref is only for smart pointers. However I've noticed (via this tweet) that there's another case where I reach out for Deref. It's not immediately clear to me who is wrong, the API guidelines or my code, so I am raising this issue to figure this out!
EDIT: #249 (comment) gives much better examples
I often implement Deref for newtype struct pattern -- when the struct has a single field, and exists to enforce some static invariant. A good example here is MainfistPath type from rust-analyzer, which is a wrapper around Path
which guarantees that parent
is not-None. I implement Deref
here because ManifestPath
is a Path
, and I want to get all the methods for free.
Another case is somewhat more obscure, and relates to this code. There, I have a hashbrown hash map of Nodes, but I use the raw API to supply my own, custom Hash. The code currently has a bug where, in one place, default hash impl is used, because Node: Hash
. I want to do the following:
struct NoHash<T>(T);
// impl<T> !Hash for NoHash<T> {}
impl<T> Deref for NoHash<T> {}
Again, the reasoning here is that I wrap the type as is, and it would be convenient to get access to all the methods for free.
I suggest focusing on the first case, at it seems more representative to me:
// Current implementation:
pub struct ManifestPath { file: AbsPathBuf }
impl ops::Deref for ManifestPath {
type Target = AbsPath;
fn deref(&self) -> &Self::Target {
&*self.file
}
}
impl ManifestPath {
// Shadow `parent` from `Deref`.
pub fn parent(&self) -> &AbsPath {
self.file.parent().unwrap()
}
}
// Implementation endorses(?) by the guidelines:
pub struct ManifestPath { file: AbsPathBuf }
impl ManifestPath {
pub fn file(&self) -> AbsPath {
&*self.file
}
pub fn dir(&self) -> &AbsPath {
self.file.parent().unwrap()
}
}
Some specific questions:
- Does the "current version" violate the guideline? To me, it seems that it clearly does, as
ManifestPath
is not a smart pointer, but my definition of smart pointer might be wrong. - What are specific problems caused by the
Deref
impl? I personally don't see any (not to say that there aren't any) - What's the best guideline-compliant way to write ManifestPath?
Quick googling showed one post wich says you can implement Deref for newtypes (cc @JWorthe): https://www.worthe-it.co.za/blog/2020-10-31-newtype-pattern-in-rust.html