Refer to an already created member variable during construction

I want to avoid:

let expensive_to_clone = SomeType::new();

MyType {
    a: expensive_to_clone.clone(),
    b: expensive_to_clone.some_usize,
}

and write instead:

MyType {
    a:  SomeType::new(),
    b: a.some_usize,
}

This gives a compilation error. So how to achieve something like this ?

I don't think there's a way to do that, but you don't need to clone. I usually have

let obj = SomeType::new();
let obj_val = obj.some_usize;

MyType {
    a: obj,
    b: obj_val,
}
3 Likes

There isn't any way to do this, @phaylon's code is idiomatic. You can just store both a and b before creating MyType, and avoid the .clone() as well.

1 Like

Ya what happened there I guess is "move" operation. Being from C++ background, I see move as something worthwhile if there are pointers to data on heap. Then only the pointers get aliased and the actual data is not duplicated thus move being much faster than copy/clone. However if there is no data on heap and all are on stack (eg., huge arrays etc) then move is practically just an illusion, it will be equivalent to copy (of-course barring compiler optimizations which takes us to magic land and as a programmer i do not have control over it or can't reason about it all the time).

Is it the same in Rust ? I mean we usually say that a struct is by default moved etc but i guess the concept is same as in C++ right ? Or in Rust is there something like - even if it is on stack, move operation will not duplicate data, but will point to same data in the stack by altering the offset from stack-pointer etc (I do see a lot of corner cases here but never really investigated, so just asking) ?

eg.,

struct A { a: SomethinOnStack, }

fn f() {
  let obj_0 = A { a: SomethingOnStack::new(), };
  let obj_1 = obj_0; // obj_0 is moved - what does this mean at stack level.
                               // Within the scope of f() do I see only one
                               // copy of A which both obj_0/1 refer to or two
                               // copies of A, obj_0 referring to an
                               // unusable-dead copy of A ?
}

If "move" is same as "copy" for data on stack then @phaylon 's code is equally expensive as a clone operation, no?

The great thing about the Rust compiler is that it uses LLVM for codegen, just like clang. With some exceptions, Rust gets all of the optimizations traditionally afforded to C++ compilers for free.

Assuming reasonable compiler optimizations, there is no wasted stack space and no copying done at all between moves - obj_1 is elided to obj_0. There are probably more complex use cases where this optimization doesn't apply, though.

Rust playpen has a disassemble feature. If you ASM with Release mode, you'll see that moves are optimized out: Rust Playground
String internally stores a heap pointer and length/capacity (24-ish bytes on x64), so the moves are pretty cheap.

2 Likes

In a direct answer to this, rust does generate a copy there initially, but LLVM will almost certainly optimize it out and remove any cost associated with it.