golang 學習筆記 - struct操作/切片/nil接口值

在看了文章Go 語言的優點,缺點和令人厭惡的設計之後,受益匪淺,覺得需要總結聯繫一下強化一下記憶。

struct操作

  • 複製

struct可以通過賦值語句來複制,如下:
雖然y是複製的x,但是修改y內array內容時候,會把x的也改變了。

import "fmt"

func main() {
	x := TestFo{Name: "foo1", Childs: []string{"foo1-2"}}

	y := x
	y.Name = "boo1"
	y.Childs[0] = "boo1-2"

	fmt.Printf("x is %v\n", x) //x is {foo1 [boo1-2]}
	fmt.Printf("y is %v\n", y) //y is {boo1 [boo1-2]}
}

type TestFo struct {
	Name   string
	Childs []string
}

這個例子就很清晰的描述了爲什麼會發上上面的情況,slice,map,array都是指針,複製struct時候,如果是指針只會複製指針,不會複製指針指向的值,可以看到,打印出來的兩個指針是相同的,所以複製後的struct修改指針指向的值會影響被複制的struct。

func main() {
	x := TestFo{Name: "foo1", Child: &Child{Name: "foo1-2"}, Child2: Child{Name: "foo1-3"}}

	y := x
	y.Name = "boo1"
	y.Child.Name = "boo1-2"
	y.Child2.Name = "boo1-3"

	fmt.Printf("x is %v  x.Child is %v\n", x, x.Child) // x is {boo1 0xc42007a1b0 {foo1-3}}  y.Child is &{boo1-2}
	fmt.Printf("y is %v  y.Child is %v\n", y, y.Child) // y is {boo1 0xc42007a1b0 {boo1-3}}  y.Child is &{boo1-2}
}

type TestFo struct {
	Name  string
	Child *Child
	Child2 Child
}

type Child struct {
	Name string
}
  • 匿名struct
  1. 聲明一個struct包含另一個struct時,可以匿名聲明,匿名聲明的好處是,可以隱式調用被包含的struct方法以及屬性,如:
package main
type Car struct {
	Name string
}

func (c Car) Print() {
	println("this is "+c.Name)
}

type City struct {
	Car
}

func main() {
	c := City{}
	c.Name = "bmw"
	c.Print()
}
  1. 如果被包含的struct中屬性有重名的,那麼就沒法隱式調用了,隱式調用其實應該是一塊go的語法糖。
package main
type Car struct {
	Name string
}

type People struct {
	Name string
}

type City struct {
	Car
	People
}

func main() {
	c := City{}
	// c.Name = "car" // 報錯:ambiguous selector c.Name
	c.Car.Name = "car"
	c.People.Name = "people"
}
  1. struct中包含指針型struct,那麼就算是隱式調用,也得先初始化。
package main

type Car struct {
	Name string
}

type Build struct {
	BuildName string
}

type City struct {
	Build
	*Car
}

func main() {
	c := City{}
	// c.Name = "car" // 編譯是可以通過的,但是運行時會在這裏中斷:bad
	println(fmt.Sprintln(c)) // {{}, <nil>} 這裏面 {}是struct的初值,<nil>是指針類型的初值。
	c.Car = &Car{}
	c.Name = "car"
	c.BuildName = "big wall"
}

切片陷阱

對於[:]或[x:]這種切片進行append操作之後,修改切片內的值,要小心,如果原數組的cap(array) > len(array),而你append之後的長度有小於等於原數組的cap,那修改切片會影響到原數組,否則不會。
原因在於,array2 = append(array1, …)操作如果cap(array1) < len(array2),那麼append會重新分配內存用以拼接,那array2的地址就是新地址,修改array2不會影響到array1,如果cap(array1) >= len(array2),那麼array2的地址是包含在array1裏的,修改array2[:len(array1)-1]的值,會影響到array1。具體例子如下:

package main

import "fmt"

func main() {
	s1 := []int{1, 2, 3, 4, 5}
	s2 := s1[3:]
	s3 := append(s1[3:], 6)
	s3[0] = 7
	fmt.Printf("s1=%v  s2=%v  s3=%v\n", s1, s2, s3)  // s1=[1 2 3 4 5]  s2=[4 5]  s3=[7 5 6]

	ss1 := make([]int, 5, 10)
	for i := 1; i < 6; i++ {
		ss1[i-1] = i
	}
	ss2 := ss1[3:]
	ss3 := append(ss2, 6)
	ss3[0] = 7
	fmt.Printf("ss1=%v  ss2=%v  ss3=%v\n", ss1, ss2, ss3) // ss1=[1 2 3 7 5]  ss2=[7 5]  ss3=[7 5 6] ss1[3]和ss2[0]被改變了!
}

NIL接口值

package main

import "fmt"

func main() {
	var t *Test
	var f Foo = t
	if f != nil {
		println(t, f)                // 0x0 (0x114f580,0x0)
		fmt.Printf("%v  %v\n", t, f) // <nil>  <nil>
		f.Can()                      // Test Can
		f.Cannot()                   // panic: value method main.Test.Cannot called using nil *Test pointer
	}
}

type Foo interface {
	Can()
	Cannot()
}

type Test struct{}

func (*Test) Can()   { fmt.Println("Test Can") }
func (Test) Cannot() { fmt.Println("Test Cannot") }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章