简体   繁体   中英

Assign a parameter value to many struct members in a contructor similar function Rust

I have an issue with a simple struct creation with generic type, as follows:

struct Point3<T> {
    pub x: T,
    pub y: T,
    pub z: T,
}

impl<T> Point3<T>
{
    fn create3(vx: T, vy: T, vz: T) -> Point3<T> {
        Point3::<T> {
            x: vx,
            y: vy,
            z: vz,
        }
    }

    fn create1(v: T) -> Point3<T> {
        Point3::<T> {
            x: v,
            y: v,
            z: v,
        }
    }
}

but when I tried to compile it, I got an error:

fn create1(v: T) -> Point3<T> {
   |                - move occurs because `v` has type `T`, which does not implement the `Copy` trait
34 |         Point3::<T> {
35 |             x: v,
   |                - value moved here
36 |             y: v,
   |                ^ value used here after move

I understand than v is moved in x and so not available for y or z . so it seems I need to copy it, but I don't know how to do that, nor implement the Copy trait for T since its a generic type

If I pass v by ref I have another error

I'm sure it's pretty simple, but as ac/c++ dev rust is complex to me for the moment:)

When working with type parameters, the only thing that Rust assumes about the type is that it is Sized . Any other constraints on the type parameter must be explicitly written out.

Also, Rust's default move semantics mean that a value can have only a single owner, and that once a value is "moved out of" a place, it is no longer valid there. You can opt out of this behaviour by implementing Copy , though this is only valid for types that can be meaningfully memcpy -ed, so many things can't use this (anything with a Drop impl, &mut T , and more). TLDR, it's a fairly restrictive bound to put on an API.

The much more general case is Clone , which is a supertrait of Copy (which means: any T that implements Copy , also implements Clone ). The difference is that Clone is potentially an expensive operation, potentially requiring heap allocations, and must be explicitly called via Clone::clone , but the tradeoff is that it is far more widely applicable.

So I'd suggest rewriting your code as follows:

// this function doesn't need to impose any special bounds on T,
// since it never needs to be cloned
impl<T> Point3<T> {
  fn create3(x: T, y: T, z: T) -> Self {
    Self { x, y, z }
  }
}

// this function does require clone, so we have to add the bound
impl<T: Clone> Point3<T> {
  fn create1(v: T) -> Self {
    Self {
      x: v.clone(),
      y: v.clone(),
      z: v,
    }
  }
}

This allows your API to be used by significantly more types, but also doesn't add any performance overhead for Copy types, since the Clone implementation for a type that is also Copy is basically a standard memcpy .

Admittedly, with a struct called Point3 , it's probably only going to be used for numbers, so the Copy vs Clone distinction isn't very relevant, but in the general case, a Clone bound is more widely usable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM