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

Time:02-27

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 a c/c dev rust is complex to me for the moment :)

CodePudding user response:

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.

  • Related