0
0

goのfor文を用いて構造体に変数代入しようとしたら頭を抱えた話

Posted at

問題

User構造体のintroductionIntroduction構造体に代入しようとしたら、全て一番最後のプロパティの値が代入されてしまった

ソースコード

package main

import "fmt"

type User struct {
	name         string
	age          int
	introduction string
}

type Introduction struct {
	introduction *string
}

func main() {
	users := []User{
		{"Alice", 25, "Hello, I'm Alice!"},
		{"Bob", 30, "Hi there, I'm Bob."},
		{"Charlie", 22, "Greetings! I'm Charlie."},
	}
	introductions := makeIntroductions(users)

	for _, introduction := range introductions {
		fmt.Println(*introduction.introduction)
	}
}

func makeIntroductions(us []User) []Introduction {
	var introductions []Introduction

	for _, user := range us {
		introductions = append(introductions, Introduction{
			introduction: &user.introduction,
		})
	}

	return introductions
}

期待した結果

Hello, I'm Alice!
Hi there, I'm Bob.
Greetings! I'm Charlie.

出力結果

Greetings! I'm Charlie.
Greetings! I'm Charlie.
Greetings! I'm Charlie.

原因

introductionsの要素全てのポインタ(参照先)が同じだった

問題の箇所

for _, user := range us {
		introductions = append(introductions, Introduction{
			introduction: &user.introduction,
		})
}

for rangeにおいて代入される要素の値は全て同じメモリ空間を使用します
そして今回introductionsにはそのポインタを代入しています

for _, introduction := range introductions {
		fmt.Println(introduction.introduction)
}
0x140000740d8
0x140000740d8
0x140000740d8

つまり、最終的に出力される値も参照元が全て同じ値であるためfor rangeで代入された一番最後値が結果として出力されたのです

対処法

for rangeの要素がすべて同じメモリ空間で使用されないようにするため、for rangeの中で代入される要素を新しい変数定義する

for _, user := range us {
		introduction := user.introduction
		introductions = append(introductions, Introduction{
			introduction: &introduction,
		})
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0