问题
I'm trying to create a shallow copy of a struct Board (a chessboard). Before saving a move to the board, I need to check if that move puts the mover in check.
To do so, within the Move method (method of a pointer), I dereference the pointer, update and check this possible board for Check. When I change the value of a single value of the Board type (such as possible.headers = "Possible Varient"
) the original b
Board is not changed.
But here when I call a method updateBoard() it updates both boards. I still receive the error (cannot move into check), but the main thread thinks b.board
(the board position) has been changed.
func (b *Board) Move(orig, dest int) error {
// validation
...
// Update
possible := *b // A 'shallow copy'?
possible.updateBoard(orig, dest, val, isEmpassant, isCastle)
king := possible.findKingPositionOfThePlayerWhoMoved()
isCheck := possible.isInCheck(king) // bool takes the king to check for
if isCheck {
return errors.New("Cannot move into Check")
}
b.updateBoard(orig, dest, val, empassant, isCastle)
return nil
Strangely, not all the the values updated by updateBoard()
change. So the b.toMove
value doesn't change, but the b.board
value does (the position of the pieces). This means if I pass possible := b
instead, the game will only ever be white's move (toMove is alternated in the updateBoard() method). With possible := *b
, turn alternation works until one moves into check. Then the move is applied to b.board
, but the error is thrown back and it remains the checked-players turn (meaning possible.updateBoard()
didn't update b.toMove.
Edit
As abhink pointed out, in Go Slices usage and internals,
Slicing does not copy the slice's data. It creates a new slice value that points to the original array.
b.board
, a []byte
, always points to its original value (even when the struct which holds it is dereferenced. abhink's answer uses the Go func copy(dst, src []Type) int
, https://golang.org/pkg/builtin/#copy , a shortcut for copying the values of the pointers.
回答1:
Since b.board
is a slice type, it is a reference type (https://blog.golang.org/go-slices-usage-and-internals) and behaves like a pointer. So any changes made to possible.board
will show up in b
. You can try making a copy of b.board
like so:
func (b *Board) Move(orig, dest int) error {
// validation
...
// Update
possible := *b // A 'shallow copy'?
boardCopy := make([]byte, len(b.board))
copy(boardCopy, b.board)
possible.board = boardCopy
possible.updateBoard(orig, dest, val, isEmpassant, isCastle)
// ...
Note that you'll have to do something like this for all reference types.
回答2:
Dereferencing does NOT make a copy. It returns the original value your pointer points to.
You get a copy because you are assigning that value to a new variable. In go every assignment makes a copy as does every pass to a function. If you assign or pass a reference, that reference is copied.
In your case you copy the value b points to. In that struct there are pointers like the b.board slice (slices have a pointer to an underlying array). So go is creating a copy of the slice. The copy still points to the same array as the slice in the original b variable. If you change that array, it is changed for both your boards.
You will need to implement a copy function to your Board struct that correctly creates a copy of your struct handling each variable depending on its type and returns that new board.
Something like:
func (b *Board) copy() *Board {
boardCopy := make([]byte, len(b.board))
copy(boardCopy, b.board)
return &Board{
moveTo: b.moveTo,
board: boardCopy
...
}
}
Hope that helps and my explanation wasn't confusing :)
来源:https://stackoverflow.com/questions/38263511/go-modify-dereferenced-struct-pointer-changes-most-struct-values-but-not-slic