What is more idiomatic: to call .deref(), * or T::m()?

I tend to prefer calling x.deref().clone() (except for Arc::clone(&x)), but it is also possible to use *x.clone() where x is Arc<T>, but it feels less readable to me.
Are there any pros/cons and is it more idiomatic in Rust to always call T::clone(&x) in this case?

UPD: I forgot it is also possible to do x.as_ref().clone().

I've never had to clone the underlying value, but I would probably prefer T::clone(&x)

These seem like five different things and I don't understand the question.

  • x.deref().clone() is a compile error unless you have used std::ops::Deref, and then it does the same thing as (*x).clone()
  • Arc::clone(&x) clones x if x is an Arc and is a compile error otherwise
  • *x.clone() clones x and then dereferences it
  • x.as_ref().clone() converts x to a reference to some other type, and then clones that other type

Are there any pros/cons and is it more idiomatic in Rust to always call T::clone(&x) in this case?

In which case? None of these are exactly equivalent to T::clone(&x) (although the first one is pretty close.)

3 Likes

My use case was to call .clone() method on underlying type wrapped with Arc. Even though it is not actually a real one, since the type I had turned out to be both Sync and Send, the question is basically how to nicely extract reference to underlying type for things like Arc wrapper when rustc can't do that automagically.

Somewhat similar example is to have y of type MutexGuard<Option<T>> and then if let Some(x) = y {}, which wouldn't compile like that.
There are again a few ways to achieve this, replacing y with y.as_ref() or y.deref() both work, but I'm not sure which is the better one and both look like there should be a nicer way to achieve the goal. Maybe there is a proposal somewhere to make it work automagically too?

Oh, ok.

I wouldn't use .deref(), because you have to use std::ops::Deref for it and that seems just a little bit weird. For the same reason, I wouldn't use std::ops::Add; foo.add(bar) when I can write foo + bar. It's extraneous.

Also noteworthy: when x is Arc<T>, (*x).foo() will always find T::foo if it exists, whereas x.deref().foo() will try <&T>::foo first (this doesn't cause a problem for clone, because auto-deref tries to match receiver types exactly before taking a reference; but it could go wrong when you are trying to call a method that takes self and is available on &T).

Between T::clone(&x) and (*x).clone() I don't have a strong opinion, but I'd lean toward the second one. It seems like situations where (*x).clone() would be confusing are probably rare. But that's just my opinion.

AsRef is a generic trait and usually you would use that in the context of a generic function that takes something that is only known to implement AsRef<U>. It's overkill for just dereferencing a smart pointer.

Depending on the semantics you probably want either &*y or y.take().

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.