Regarding to how sink works: A `sink` annotated parameter `a` basically tells the compiler that, inside the procedure, you're planning to - in C++ terms - move `a` somewhere. This allows you to have only one `=sink` invocation with no intermediate temporaries at the 'final' move location even when passing a value down through multiple procedure calls (`sink` needs to be present on the respective parameters though). You're not required to actually move `a` on all code paths or even at all, the compiler just makes sure that you can do so without disturbing anything at the callsite by creating a temporary there if necessary.
Note that `sink` is, strictly speaking, not merely a hint since: proc a(x : var int, y : int) = x = move y Run does not compile, while: proc b(x : var int, y : sink int) = x = move y Run does. The following Nim code: type A = object x : int B = object a : A proc set(b : var B, a : sink A) = b.a = a # Compiler infers `=sink` (move) here. No need to use `move` # b.a = move a proc test1() = var a = A(x : 0) var b : B b.set(a) proc test2() = var a = A(x : 0) var b : B # A copy of `a` gets passed to `set` b.set(a) # modify a a.x = 1 proc test3() = let a = A(x : 0) # Using let here (roughly the same as const in C++) var b : B # No copy is created here. `a` gets moved from even though it's a let (constant) since `a` is last used here b.set(a) Run directly translated to C++20 (while keeping the same abstraction level): struct A { int x; }; struct B { A a; }; void set(B& b, A& a) { b.a = std::move(a); } void test1() { A a{.a = 0}; B b; set(b, a); } void test2() { A a{.a = 0}; B b; { A temp = a; // Could also use the copy-constructor here instead of copy-assignment set(b, temp); } a.x = 1; } void test3() { // const A a{.x = 0}; // No equivalent since you can't bind a const l-value to a l-value reference. You'd have to either make `a` non-const or introduce a copy } Run