サーチ…
前書き
構造体は、一緒にパックされたさまざまな変数のセットです。構造体自体は変数を含むパッケージであり、簡単にアクセスできるようにしています。
C言語と違って、Goの構造体にはメソッドが付加されています。また、インタフェースを実装することもできます。これにより、Goの構造体はオブジェクトに似ていますが、継承のようなオブジェクト指向言語で知られている主要な機能が(おそらく意図的に)欠落しています。
基本的な宣言
基本構造体は次のように宣言されています:
type User struct {
FirstName, LastName string
Email string
Age int
}
各値はフィールドと呼ばれます。フィールドは通常、1行に1つずつ記述され、そのフィールド名はその型より前に置かれます。上記の例では、同じタイプの連続したフィールドをFirstName
とLastName
ように組み合わせることができます。
エクスポートされたフィールドと非エクスポートフィールド(非公開vs公開)
名前が大文字で始まる構造体フィールドがエクスポートされます。その他の名前はすべてアンエクスポートされます。
type Account struct {
UserID int // exported
accessToken string // unexported
}
非エクスポートフィールドは、同じパッケージ内のコードでのみアクセスできます。そのため、 別のパッケージからフィールドにアクセスする場合は、その名前を大文字で始める必要があります。
package main
import "bank"
func main() {
var x = &bank.Account{
UserID: 1, // this works fine
accessToken: "one", // this does not work, since accessToken is unexported
}
}
ただし、 bank
パッケージ内から、UserIdとaccessTokenの両方に問題なくアクセスできます。
パッケージbank
は次のように実装できます:
package bank
type Account struct {
UserID int
accessToken string
}
func ProcessUser(u *Account) {
u.accessToken = doSomething(u) // ProcessUser() can access u.accessToken because
// it's defined in the same package
}
合成と埋め込み
コンポジションは継承の代替案を提供します。構造体は、宣言に別の型を名前で含めることができます。
type Request struct {
Resource string
}
type AuthenticatedRequest struct {
Request
Username, Password string
}
上記の例では、 AuthenticatedRequest
は、 Resource
、 Request
、 Username
、およびPassword
4つのパブリック・メンバーが含まれています。
複合構造体は、通常の構造体と同じ方法でインスタンス化して使用できます。
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
fmt.Printf("%#v", ar)
}
埋め込み
前の例では、 Request
は埋め込みフィールドです。合成は、異なるタイプを埋め込むことによっても達成できます。これは、例えば、より多くの機能を持つStructを飾る場合などに便利です。たとえば、Resourceサンプルを続けると、Resourceフィールドの内容をフォーマットして、 http://
またはhttps://
接頭辞を付ける関数が必要になりhttps://
。 2つのオプションがあります:AuthenticatedRequestで新しいメソッドを作成するか、別の構造体から埋め込みます:
type ResourceFormatter struct {}
func(r *ResourceFormatter) FormatHTTP(resource string) string {
return fmt.Sprintf("http://%s", resource)
}
func(r *ResourceFormatter) FormatHTTPS(resource string) string {
return fmt.Sprintf("https://%s", resource)
}
type AuthenticatedRequest struct {
Request
Username, Password string
ResourceFormatter
}
そして今、主な機能は次のことを行うことができます:
func main() {
ar := new(AuthenticatedRequest)
ar.Resource = "www.example.com/request"
ar.Username = "bob"
ar.Password = "P@ssw0rd"
println(ar.FormatHTTP(ar.Resource))
println(ar.FormatHTTPS(ar.Resource))
fmt.Printf("%#v", ar)
}
ResourceFormatter
埋め込まれた構造体を持つAuthenticatedRequest
を探します。
しかし 、それの欠点は、あなたがあなたのコンポジションの外にあるオブジェクトにアクセスできないことです。したがって、 ResourceFormatter
はAuthenticatedRequest
メンバーにアクセスできません。
メソッド
Structメソッドは、関数と非常によく似ています。
type User struct {
name string
}
func (u User) Name() string {
return u.name
}
func (u *User) SetName(newName string) {
u.name = newName
}
唯一の違いは、メソッド受信者の追加です。それは、型のインスタンスまたは型のインスタンスへのポインタとして宣言されるかもしれません。 SetName()
はインスタンスを変更するため、受信側はインスタンス内で永続的な変更を行うためにポインタでなければなりません。
例えば:
package main
import "fmt"
type User struct {
name string
}
func (u User) Name() string {
return u.name
}
func (u *User) SetName(newName string) {
u.name = newName
}
func main() {
var me User
me.SetName("Slim Shady")
fmt.Println("My name is", me.Name())
}
匿名の構造体
匿名の構造体を作成することは可能です:
data := struct {
Number int
Text string
} {
42,
"Hello world!",
}
完全な例:
package main
import (
"fmt"
)
func main() {
data := struct {Number int; Text string}{42, "Hello world!"} // anonymous struct
fmt.Printf("%+v\n", data)
}
タグ
構造体フィールドには、それらに関連付けられたタグを付けることができます。これらのタグは、 reflect
パッケージによって読み取られ、開発者がフィールドについて指定したカスタム情報を取得できます。
struct Account {
Username string `json:"username"`
DisplayName string `json:"display_name"`
FavoriteColor string `json:"favorite_color,omitempty"`
}
上記の例では、JSONをマーシャリングまたはアンマーシャリングするときに、 encoding/json
パッケージで使用されるキー名を変更するためにタグが使用されています。
タグには任意の文字列値を指定できますが、スペース区切りkey:"value"
組み合わせを使用することをお勧めしkey:"value"
。
struct StructName {
FieldName int `package1:"customdata,moredata" package2:"info"`
}
encoding/xml
およびencoding/json
パッケージで使用されるstructタグは、標準のlibararyで使用されます。
構造体コピーを作る。
構造体は、代入を使用して簡単にコピーできます。
type T struct {
I int
S string
}
// initialize a struct
t := T{1, "one"}
// make struct copy
u := t // u has its field values equal to t
if u == t { // true
fmt.Println("u and t are equal") // Prints: "u and t are equal"
}
上記の場合、 't'
と 'u'は別々のオブジェクト(構造体の値)になりました。
T
はフィールドとして参照型(スライス、マップ、チャネル)を含んでいないので、相互に影響することなく上記のt
とu
変更することができます。
fmt.Printf("t.I = %d, u.I = %d\n", t.I, u.I) // t.I = 100, u.I = 1
ただし、 T
に参照型が含まれている場合は、次のようになります。
type T struct {
I int
S string
xs []int // a slice is a reference type
}
次に、割り当てによる簡単なコピーでは、スライスタイプフィールドの値も新しいオブジェクトにコピーされます。これにより、同じスライスオブジェクトを参照する2つの異なるオブジェクトが生成されます。
// initialize a struct
t := T{I: 1, S: "one", xs: []int{1, 2, 3}}
// make struct copy
u := t // u has its field values equal to t
uとtの両方がそのフィールドxsを介して同じスライスを参照しているため、あるオブジェクトのスライス内の値は、他のオブジェクトのスライスの値を反映します。
// update a slice field in u
u.xs[1] = 500
fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 500 3], u.xs = [1 500 3]
したがって、この参照型のプロパティが意図しない振る舞いをしないように注意する必要があります。
たとえば、上記のオブジェクトをコピーするには、スライスフィールドの明示的なコピーを実行できます。
// explicitly initialize u's slice field
u.xs = make([]int, len(t.xs))
// copy the slice values over from t
copy(u.xs, t.xs)
// updating slice value in u will not affect t
u.xs[1] = 500
fmt.Printf("t.xs = %d, u.xs = %d\n", t.xs, u.xs)
// t.xs = [1 2 3], u.xs = [1 500 3]
構造体リテラル
struct型の値は、そのフィールドの値を指定するstructリテラルを使用して記述できます。
type Point struct { X, Y int }
p := Point{1, 2}
上記の例では、すべてのフィールドを正しい順序で指定しています。これは有用ではありません。なぜなら、プログラマは正確なフィールドを順番に覚えていなければならないからです。しばしば、構造体は、フィールド名の一部またはすべて、および対応する値をリストすることによって初期化できます。
anim := gif.GIF{LoopCount: nframes}
省略されたフィールドは、そのタイプのゼロ値に設定されます。
注: 2つの書式を同じリテラルで混在させることはできません。
空の構造体
構造体は、フィールドと呼ばれる名前付き要素のシーケンスであり、それぞれが名前と型を持ちます。空の構造体には、匿名の空の構造体のようなフィールドはありません。
var s struct{}
または、空のstruct型という名前のように:
type T struct{}
空の構造体について興味深いのは、そのサイズがゼロ(try The Go Playground )です:
fmt.Println(unsafe.Sizeof(s))
これは0
出力するので、空の構造体自体はメモリを消費しません。だから、( The Playgroundを試してみてください) のように、チャンネルを終了すると良いオプションです:
package main
import (
"fmt"
"time"
)
func main() {
done := make(chan struct{})
go func() {
time.Sleep(1 * time.Second)
close(done)
}()
fmt.Println("Wait...")
<-done
fmt.Println("done.")
}