If you were to remove the derive(Copy)
from Square
, the issue turns into an error message.
error[E0382]: borrow of moved value: `s`
--> src/main.rs:64:20
|
61 | let mut s = Square::from(SquareColor::Black, 'a', 1);
| ----- move occurs because `s` has type `Square`, which does not implement the `Copy` trait
62 | println!("{}", s.is_there_piece);
63 | let k = King::new(s, PieceColor::White);
| - value moved here
64 | println!("{}", s.is_there_piece);
| ^^^^^^^^^^^^^^^^ value borrowed here after move
|
note: consider changing this parameter type in method `new` to borrow instead if owning the value isn't necessary
--> src/main.rs:44:32
|
44 | pub fn new(mut square: Square, color: PieceColor) -> Self {
| --- ^^^^^^ this parameter takes ownership of the value
| |
| in this method
= note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable
|
63 | let k = King::new(s.clone(), PieceColor::White);
| ++++++++
You are placing the square into the kind, but also hold onto the square. In Rust, this doesn’t work, all sharing of values is explicit using references or smart pointers, and without those, there’ll only ever be one place where the – in this case Square
– can be, and be accessed from.
The compiler even suggests two general approaches to resolve this kind of error. The second approach, creating a new Square
via s.clone()
and putting that square into the king, is essentially equivalent to what your original code was doing implicitly via the Copy
trait.
Why is the square part of the king, anyways? Wouldn’t usually the king be placed into/onto the square? Rust is different from many popular contemporary programming languages in how you design data structures, in that you have to actually have a proper design. You can not simply assume every value (or maybe you like to say “object” in those languages) exists, somewhere, somehow, for some time, but long enough, belonging no-where and everywhere, and being mutated from wherever and whenever in the code you feel like it.
I.e. in other languages, “equivalent”-looking code would probably behave by making a Square
an object, always living on its own on the heap, with an object identity, and many places can observe this object, make changes to it, see changes made by others, reference it from other places and have it reference other places to your hears content … I think some critical people like to speak of this as a pile of spaghetti.
In Rust, especially if there is a Copy
implementation, a Square
struct is nothing but the list of its field’s values. Like a number. If you write
let n = 1;
let king = King::new(n);
you probably wouldn’t expect that n
could become 2
afterwards, either.
Making your code “work” is probably futile, because I cannot see how the design could fit into a total design for representing a chess board state (which I would assume is the goal here?) unless I’m overlooking something.
Making the code “work as you probably expected” though is possible. @jofas demonstrated a “solution”, though that approach will probably run into lots of compilation errors very quickly once you do any of the extensions to your code you likely planned. (Putting references into fields is hard to work with anyways, even more so if, and it looks like this is the case, the struct containing the reference is not supposed to be super short-lived; and having a method downgrade a mutable reference to a shared one, like King::new
does in that “solution”, will commonly run into surprisingly limiting (especially surprising to beginners) restrictions imposed by the borrow-checker.
The other way to make the code “work as you probably expected” is by using Rust’s “(almost) garbage collected” reference smart pointer type Rc
. Then Square
can gain something akin to an object identity, by means of putting it into an Rc
once, then copying that reference smart pointer, and … well … we’ll then have to use another helping primitive to re-gain the permission to mutate any of the data, but the end result may look something like this. This solution uses primitives that can be used to turn Rust data also in a bit more unstructured pile of spaghetti, with some limitations, e.g. you’ll still have to avoid cycles, or resort to Weak
pointers to break them; shared-mutability primitives like Cell
are more verbose to work with; the most straightforward approaches will make your data structures no longer thread-safe, or you’d need to use thread-safe approaches like atomics or mutexes.
There is a third way to make the code “work”, which hints at how the proper solution might look like, without actually suggesting a proper solution (which would be a clear hierarchy of who-owns-what between your data structures making up your game stare): You can avoid the need to share by accessing the value through its proper owner. The change for this is actually very minimal:
use piece::{King, PieceColor};
use square::{Square, SquareColor};
fn main() {
let mut s = Square::from(SquareColor::Black, 'a', 1);
println!("{}", s.is_there_piece);
let k = King::new(s, PieceColor::White);
- println!("{}", s.is_there_piece);
+ println!("{}", k.square.is_there_piece);
}
Rust Playground
and just like that it already prints true
properly
Hello World
false
true