Thinking_Out_Loud

《Go程序设计语言》读书笔记#3

2018-09-10

第四章 复合类型

< P83 >

  • 长度亦算数组类型的一部分因此两个长度不同的数组是不同类型;
  • 只有两个数组的元素类型可以比较时,这两个数组才可以比较;
  • 调用函数传值时,会将数组拷贝传递(传值引用),所以在函数内的修改对原数组无影响;

< P86 >

  • 数组和切片声明时可以显式声明index,当两者混用时,隐式声明的元素index随前方相邻的显式声明index自增;
  • 与数组不同,切片不能直接用==!=比较,只有与nil的比较是合法的,除了[]byte可以用bytes.Equal方法比较外,其它类型切片我们都要自行逐个元素比较;

    Go语言核心之美 3.2-slice切片
    为什么Go语言不支持slice的比较运算呢?

    • 第一个原因,slice是引用类型,一个slice甚至可以引用自身。虽然有很多解决办法,但是没有一个是简单有效的;
    • 第二个原因,因为slice是间接引用,因此一个slice在不同时间可能包含不同的元素-底层数组的元素可能被修改;
    • 只要一个数据类型可以做相等比较,那么就可以用来做mapkey,map这种数据结构对key的要求是:如果最开始时key是相等的,那在map的生命周期内,key要一直相等,因此这里key是不可变的。而对于指针或chan这类引用类型,==可以判断两个指针是否引用了想同的对象,是有用的,但是slice的相等测试充满了不确定性,因此,安全的做法是禁止slice之间的比较操作。
  • 可以存在lencap都为0但是non-nilslice,所以判定slice是否为空应该用len;

    1
    2
    3
    4
    var s []int    // len(s) == 0, s == nil
    s = nil // len(s) == 0, s == nil
    s = []int(nil) // len(s) == 0, s == nil
    s = []int{} // len(s) == 0, s != nil

< P94 >

  • map的键应该可以用==比较,不建议用浮点数作键;
  • map中的元素不是变量,因此不能被取地址;

    map可能会随着元素的增多重新分配更大的内存空间,旧值都会拷贝到新的内存空间,因此之前的地址就会失效。

  • 和切片一样,map间不能直接比较,只有和nil比较是合法的;

< P100 >

结构体的字段(field)可以取地址并通过指针访问:

1
2
3
4
5
6
func EmployeeByID(id int) *Employee { /* ... */ }

fmt.Println(EmployeeByID(dilbert.ManagerID).Position) // "Pointy-haired boss"

id := dilbert.ID
EmployeeByID(id).Salary = 0 // fired for... no real reason

最后那条语句需要注意,它调用EmployeeByID生成了一个*Employee指针,然后直接更新该结构体中的一个字段。如果将EmployeeByID的返回值从*Employee换成Employee类型,那么编译将报错,因为编译器无法对返回的Employee进行寻址(不通过变量直接使用一个值,一般都无法进行寻址)。

< P101 >

对于结构体来说,字段顺序很重要,如果顺序不同或者有一些字段合并声明,即使字段名完全相同,那也算两个完全不同的结构体类型;

< P104 >

当结构体中所有字段都可以比较时,这个结构体类型才可以相比较;

< P106 >

  • 准确来说,结构体中的匿名字段并不是真正的匿名,有隐式的与类型名相同的名字;在格式化字符串中加入##代表用Go的语法来打印。对于struct类型来说,打印中将包含每个字段的信息;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    w = Wheel{
    Circle: Circle{
    Point: Point{X: 8, Y: 8},
    Radius: 5,
    },
    Spokes: 20, // NOTE: trailing comma necessary here (and at Radius)
    } // or w = Wheel{Circle{Point{8, 8}, 5}, 20}

    fmt.Printf("%#v\n", w)
    // Output:
    // Wheel{Circle:Circle{Point:Point{X:8, Y:8}, Radius:5}, Spokes:20}
  • 当匿名字段没有导出时,不能用长赋值形式;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //  mStruct.go
    package mStruct
    type point struct {
    X, Y int
    }
    type Cyc struct {
    point
    }
    // main.go
    package main

    func main() {
    var m = mStruct.Cyc{}
    m.X = 1
    m.point.Y = 2 // cannot refer to unexported field or method point
    fmt.Printf("%#v\n", m) // mStruct.Cyc{point:mStruct.point{X:1, Y:0}}
    }