Can't change the variable inside in the struct

I've got a bool checker in my Square struct and I want to make it true in another struct's impl

square.rs

#[derive(Debug, Copy, Clone)]
pub enum SquareColor {
    Black,
    White,
}

#[derive(Debug, Copy, Clone)]
pub struct Square {
    square_color: SquareColor,
    colon: char,
    row: u16,
    pub is_there_piece: bool,
}

impl Square {
    pub fn from(square_color: SquareColor, colon: char, row: u16) -> Self {
        println!("Hello World");
        Self {
            square_color: square_color,
            colon: colon,
            row: row,
            is_there_piece: false,
        }
    }
}

piece.rs

pub enum PieceColor {
    Black,
    White,
}
  
pub struct King {
    square: Square,
    color: PieceColor,
}
impl King {
    pub fn new(mut square: Square, color: PieceColor) -> Self {
        if !square.is_there_piece {
            square.is_there_piece = true;
        } else {
            panic!("Error");
        }
        Self {
            square: square,
            color: color,
        }
    }
}

main.rs

use chess::board::square::{Square, SquareColor};
use chess::board::Board;
use chess::piece::{King, PieceColor};
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);
}

Terminal Output is

false
false

How can I be change in this

You are moving/copying self into the methods. You need to take a mutable reference, &mut self, in methods where you want changes to be reflected in the original value.

You pass s by value, so it is moved and will be owned by your King struct that is bound to your variable k.

One possibility to prevent the move so that you can continue to use s is passing it by reference to King::new. Additionally, since you want to mutate it inside King::new, you'd need to pass it as a mutable reference. Here a possible solution.

I'm struggling to hold back the jokes. I'm sure you meant "column" instead of "colon."

2 Likes

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
6 Likes

Yeah I am aware of it in Turkey we call it kolon it's hybrid of it.

Merhaba,

  • I don't think a piece should own a square. A piece is only associated with a square when it is part of a position, and in this case, the position is basically a collection of pieces, and then the position would know which squares they are on. The operations involving pieces and squares are probably the following, which will be associated with the position:

    • Query the position, whether there is a piece on a given field, in which case you already have the field.
    • Iterate over pieces, in which case the position can iterate over piece/square pairs
  • There is no need for a struct King, because a king is just one type of chess piece. Just have a struct for Piece

  • The color of a piece is just reflective of the two parties we have. Each piece is owned by a party

  • For coordinates, I would just use internally numbers from 0 to 7 and then convert them for display into a number from 1 to 8 for the row and a character from a to h for the column (or reverse if you plan to parse)

  • The color of a square can easily be calculated from the coordinates, so no need to store it.

Something like this:

enum Party { White, Black }
struct Square { col: u8, row: u8 }

enum PieceType { King, Queen, Rook, Bishop, Knight, Pawn }

struct Piece {
  piece_type: PieceType,
  owner: Party
}

struct Position {
  pieces: [[Option<Piece>; 8]; 8],
  next_to_move: Party,
  ...
}

Of course, if you wanted to make a serious chess app (which would be quite a bit of work), then you would also need to keep some historical information to determine whether any pawn can be taken en passant, which castling options may still be available, and how close we are to tie due to position repetition or 50 moves without moving a pawn or taking a piece.

4 Likes

Just to play devil's advocate a bit - It would/could make sense to include the position in the Piece, since this can potentially speed up the move generator (you only need to iterate over all pieces that are still in the game rather than over all squares) and the move generator is probably the part of the code that is going to be called most often. But actually -- it could be better to use @curoli's suggestion and not do so, but instead somehow make sure to be able to quickly find which squares have a piece.
@kamilutkumavi0 - You might get more ideas by looking at source code of one or more well-designed open source engines. For instance, the crafty code by Robert Hyatt. That code also has a very interesting way of generating moves by using 64-bit bitmaps. See: GitHub - lazydroid/crafty-chess: All versions of the great Crafty chess engine by Dr. Robert Hyatt

1 Like

I actualy wanted to do it because of the possible moves. My plan was I could access all possible moves for that piece in that piece struct so when you select the piece program check the possibles when you move, all possible moves will change. Now I am trying to solve that another way. I will looking at the github link thank you.

For generating moves, if you want to iterate over all pieces of a party, the position can give you an Iterator<Item=(Square, Piece)>, then you would have the square, too. Once you try to find all possible moves for a given piece, you would query for every square that potentially move to or pass over, whether there is a piece on it. In other words, checking what's on a given square is the most frequent operation, so you want to make that fast.

And if you want to be really fast, you can pack the entire field into 4*8*8 bits.

Yes, you can!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.