For an arbitrary struct that implements Clone
, I would like to have a single generic function that takes either:
&MyStruct
in which case it can be conditionally cloned by the functionMyStruct
in which case the clone is unnecessary since it can be movedI've implemented this on my own:
use std::clone::Clone;
#[derive(Debug)]
struct MyStruct {
value: u64,
}
impl Clone for MyStruct {
fn clone(&self) -> Self {
println!("cloning {:?}", self);
MyStruct { value: self.value }
}
}
trait TraitInQuestion<T> {
fn clone_or_no_op(self) -> T;
}
impl TraitInQuestion<MyStruct> for MyStruct {
fn clone_or_no_op(self) -> MyStruct {
self
}
}
impl<'a> TraitInQuestion<MyStruct> for &'a MyStruct {
fn clone_or_no_op(self) -> MyStruct {
self.clone()
}
}
fn test<T: TraitInQuestion<MyStruct>>(t: T) {
let owned = t.clone_or_no_op();
}
fn main() {
let a = MyStruct { value: 8675309 };
println!("borrowing to be cloned");
test(&a);
println!("moving");
test(a);
}
and the output is as expected:
borrowing to be cloned
cloning MyStruct { value: 8675309 }
moving
Is this functionality already derived somehow by implementing Clone
? If not, std::borrow::ToOwned
sounds like what I want, but I can't get it to work:
use std::clone::Clone;
use std::borrow::Borrow;
#[derive(Debug)]
struct MyStruct {
value: u64,
}
impl Clone for MyStruct {
fn clone(&self) -> Self {
println!("cloning {:?}", self);
MyStruct { value: self.value }
}
}
fn test<T: ToOwned<Owned = MyStruct>>(a: T) {
let owned = a.to_owned();
}
fn main() {
let a = MyStruct { value: 8675309 };
println!("borrowing to be cloned");
test(&a);
println!("moving");
test(a);
}
Compiler output:
error[E0277]: the trait bound `MyStruct: std::borrow::Borrow<T>` is not satisfied
--> src/main.rs:16:1
|
16 | / fn test<T: ToOwned<Owned = MyStruct>>(a: T) {
17 | | let owned = a.to_owned();
18 | | }
| |_^ the trait `std::borrow::Borrow<T>` is not implemented for `MyStruct`
|
= help: consider adding a `where MyStruct: std::borrow::Borrow<T>` bound
= note: required by `std::borrow::ToOwned`
Doing what the compiler suggests by changing test
:
fn test<T: ToOwned<Owned = MyStruct>>(a: T) -> ()
where
MyStruct: Borrow<T>,
{
let owned = a.to_owned();
}
And the resulting error:
error[E0308]: mismatched types
--> src/main.rs:27:10
|
27 | test(&a);
| ^^ expected struct `MyStruct`, found &MyStruct
|
= note: expected type `MyStruct`
found type `&MyStruct`
If I try to implement ToOwned
for &MyStruct
impl<'a> ToOwned for &'a MyStruct {
type Owned = MyStruct;
fn to_owned(&self) -> Self::Owned {
self.clone()
}
}
I get the following error:
error[E0119]: conflicting implementations of trait `std::borrow::ToOwned` for type `&MyStruct`:
--> src/main.rs:16:1
|
16 | / impl<'a> ToOwned for &'a MyStruct {
17 | | type Owned = MyStruct;
18 | |
19 | | fn to_owned(&self) -> Self::Owned {
20 | | self.clone()
21 | | }
22 | | }
| |_^
|
= note: conflicting implementation in crate `alloc`
As @Shepmaster pointed out there is Cow
; but you'd need to manually create the Cow::Borrowed(&a)
or Cow::Owned(a)
instances, and the wrapped (Owned
) type must always implement Clone
(for T: ToOwned<Owned=T>
).
(The Clone
for ToOwned::Owned
might not be strictly necessary with custom ToOwned
implementations; but .borrow().to_owned()
acts like a .clone()
, so there is no reason to hide it.)
Your own trait is a nice start for an alternative, although you should use generic implementations. That way you don't require a type to implement Clone
as long as you don't pass a reference:
Playground
trait CloneOrNoOp<T> {
fn clone_or_no_op(self) -> T;
}
impl<T> CloneOrNoOp<T> for T {
fn clone_or_no_op(self) -> T {
self
}
}
impl<'a, T: Clone> CloneOrNoOp<T> for &'a T {
fn clone_or_no_op(self) -> T {
self.clone()
}
}
struct MyStructNoClone;
#[derive(Debug)]
struct MyStruct {
value: u64,
}
impl Clone for MyStruct {
fn clone(&self) -> Self {
println!("cloning {:?}", self);
MyStruct { value: self.value }
}
}
fn test<T: CloneOrNoOp<MyStruct>>(t: T) {
let _owned = t.clone_or_no_op();
}
// if `I` implement `Clone` this takes either `&I` or `I`; if `I` doesn't
// implement `Clone` it still will accept `I` (but not `&I`).
fn test2<I, T: CloneOrNoOp<I>>(t: T) {
let _owned: I = t.clone_or_no_op();
}
fn main() {
let a = MyStruct { value: 8675309 };
println!("borrowing to be cloned");
test(&a);
// cannot infer `I`, could be `&MyStruct` or `MyStruct`:
// test2(&a);
test2::<MyStruct,_>(&a);
test2::<&MyStruct,_>(&a);
println!("moving");
test(a);
let a = MyStructNoClone;
test2(&a);
// the previous line is inferred as ("cloning" the reference):
test2::<&MyStructNoClone,_>(&a);
// not going to work (because it can't clone):
// test2::<MyStructNoClone,_>(&a);
test2(a);
}
Sadly it seems impossible for now to base CloneOrNoOp
on ToOwned
(instead of Clone
) like this:
impl<'a, B> CloneOrNoOp<B::Owned> for &'a B
where
B: ToOwned,
{
fn clone_or_no_op(self) -> B::Owned {
self.to_owned()
}
}
The compiler sees conflicting implementations of "CloneOrNoOp<&_>
for type &_
" (someone crazy might implement ToOwned for Foo { type Owned = &'static Foo; ... }
; traits can't distinguish implementations based on lifetime differences).
But similar to ToOwned
you can implement specific customizations, like:
impl<'a> CloneOrNoOp<String> for &'a str {
fn clone_or_no_op(self) -> String {
self.to_owned()
}
}
Now you can pass any of &str
, &String
or String
if you want to get a String
:
test2::<String,_>("abc");
test2::<String,_>(&String::from("abc"));
test2::<String,_>(String::from("abc"));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With