gpt4 book ai didi

generics - 通用可克隆/可移动参数作为函数参数

转载 作者:行者123 更新时间:2023-11-29 08:07:44 24 4
gpt4 key购买 nike

对于实现 Clone 的任意结构,我想要一个通用函数,它采用以下任一方法:

  • a &MyStruct 在这种情况下它可以被函数有条件地克隆
  • 一个 MyStruct 在这种情况下不需要克隆,因为它可以移动

我自己实现了这个:

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

并且输出符合预期:

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

此功能是否已通过实现Clone 以某种方式派生?如果没有,std::borrow::ToOwned听起来像我想要的,但我无法让它工作:

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

编译器输出:

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`

通过更改 test 执行编译器建议的操作:

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

以及由此产生的错误:

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

如果我尝试为 &MyStruct 实现 ToOwned

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

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

我收到以下错误:

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`

最佳答案

正如@Shepmaster 指出的那样,有 Cow ;但您需要手动创建 Cow::Borrowed(&a)Cow::Owned(a)实例,并且包装 ( Owned ) 类型必须始终实现 Clone (对于 T: ToOwned<Owned=T> )。

(CloneToOwned::Owned 对于自定义 ToOwned 实现可能不是严格必需的;但 .borrow().to_owned() 的行为类似于 .clone(),因此没有理由隐藏它。)

尽管您应该使用通用实现,但您自己的特征是替代方案的良好开端。这样你就不需要类型来实现 Clone只要您不传递引用:

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

可悲的是,现在似乎不可能建立基础 CloneOrNoOpToOwned (而不是 Clone )像这样:

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

编译器看到“CloneOrNoOp<&_> 类型 &_ ”的冲突实现(疯狂的人可能会实现 ToOwned for Foo { type Owned = &'static Foo; ... } ;特征无法根据生命周期差异区分实现)。

但类似于ToOwned您可以实现特定的自定义,例如:

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

现在你可以通过任何&str , &StringString如果你想得到 String :

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

关于generics - 通用可克隆/可移动参数作为函数参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47253965/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com