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