2017-11-12 1 views
2

Für eine beliebige Struktur, die Clone implementiert, würde Ich mag eine einzelne generische Funktion haben, nimmt entweder:Generisches klonbar/beweglichen Parameter als Funktionsargument

  • a &MyStruct in welchem ​​Fall es kann bedingt durch die geklont werden Funktion
  • ein MyStruct in diesem Fall ist der Klon nicht notwendig ist, da es
  • bewegt werden kann

ich habe implementiert das alleine:

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); 
} 

und der Ausgang ist wie erwartet:

borrowing to be cloned 
cloning MyStruct { value: 8675309 } 
moving 

Ist diese Funktionalität schon irgendwie Clone durch die Implementierung abgeleitet? Wenn nicht, klingt std::borrow::ToOwned wie das, was ich will, aber ich kann es nicht funktionieren:

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 Ausgabe:

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` 

Tun, was der Compiler durch test Wechsel schlägt vor:

fn test<T: ToOwned<Owned = MyStruct>>(a: T) ->() 
where 
    MyStruct: Borrow<T>, 
{ 
    let owned = a.to_owned(); 
} 

Und der resultierende Fehler:

error[E0308]: mismatched types 
    --> src/main.rs:27:10 
    | 
27 |  test(&a); 
    |   ^^ expected struct `MyStruct`, found &MyStruct 
    | 
    = note: expected type `MyStruct` 
       found type `&MyStruct` 

Wenn ich versuche, ToOwned für &MyStruct

impl<'a> ToOwned for &'a MyStruct { 
    type Owned = MyStruct; 

    fn to_owned(&self) -> Self::Owned { 
     self.clone() 
    } 
} 

ich die folgende Fehlermeldung erhalten zu implementieren:

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` 
+2

Sind Sie Kennen Sie schon Kuh? – Shepmaster

+1

In hohem Grade verwandt: [Gibt es eine Weise, eine Funktion zu schreiben, die eine Referenz, eine Bewegung oder eine Umwandlung annimmt, die nur Eigenschaften von der Standardbibliothek verwendet?] (Https://stackoverflow.com/q/47225805/155423) – Shepmaster

+0

Oh Ich war mir nicht bewusst, Kuh. Das ist ziemlich genau das, was ich gesucht habe, muss es verpasst haben. Obwohl es in einigen Fällen unnötig erscheint, eine Eingabe in eine Enum zu schreiben (d. H. "Cow" ist eine dynamische Ausgabe, da meine "TraitInQuestion" eine statische Ausgabe ist, wenn diese Analogie sinnvoll ist) – 10101

Antwort

2

Wie @Shepmaster wies darauf hin, ist es Cow; aber Sie müssten die Instanzen Cow::Borrowed(&a) oder Cow::Owned(a) manuell erstellen, und der umschlossene (Owned) Typ muss immer Clone (für T: ToOwned<Owned=T>) implementieren.

(Die Clone für ToOwned::Owned vielleicht nicht streng mit benutzerdefinierten ToOwned Implementierungen notwendig sein, aber .borrow().to_owned() wirkt wie ein .clone(), so gibt es keinen Grund, es zu verbergen.)

Ihr eigenes Merkmal ist ein schöner Start für eine alternative , obwohl Sie generische Implementierungen verwenden sollten.Auf diese Weise Sie benötigen kein Typ Clone so lange zu implementieren, wie Sie keinen Verweis übergeben Sie:

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); 
} 

Leider scheint es nun unmöglich zu CloneOrNoOp auf ToOwned Basis (statt Clone) wie folgt aus:

impl<'a, B> CloneOrNoOp<B::Owned> for &'a B 
where 
    B: ToOwned, 
{ 
    fn clone_or_no_op(self) -> B::Owned { 
     self.to_owned() 
    } 
} 

der Compiler "für Typen CloneOrNoOp<&_>&_" widersprüchlichen Implementierungen sieht (jemand verrückt könnte implementieren ToOwned for Foo { type Owned = &'static Foo; ... }; Merkmale können Implementierungen aufgrund von Unterschieden in der Lebensdauer nicht unterscheiden.

Aber ähnlich wie ToOwned können Sie bestimmte Anpassungen implementieren, wie:

impl<'a> CloneOrNoOp<String> for &'a str { 
    fn clone_or_no_op(self) -> String { 
     self.to_owned() 
    } 
} 

Jetzt können Sie alle &str, &String oder String, wenn Sie ein String bekommen passieren:

test2::<String,_>("abc"); 
test2::<String,_>(&String::from("abc")); 
test2::<String,_>(String::from("abc")); 
Verwandte Themen