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)) // {{}, } 这里面 {}是struct的初值,是指针类型的初值。
	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) //   
		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") }

你可能感兴趣的:(学习总结)