package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
)

func main() {
	var w io.Writer
	fmt.Printf("(%T, %[1]v)\n", w)
	w = os.Stdout
	w = io.Writer(os.Stdout)
	fmt.Printf("(%T, %[1]v)\n", w)
	w.Write([]byte("hello, writer\n")) // 向屏幕输出了hello, writer
	fmt.Printf("(%T, %[1]v)\n", w)
	w = new(bytes.Buffer)
	fmt.Printf("(%T, %[1]v)\n", w)
	w.Write([]byte("hello")) // 向缓冲区写入了hello
	fmt.Printf("(%T, %[1]v)\n", w)

	w = nil
	fmt.Printf("(%T, %[1]v)\n", w)

	// 这里按照我的理解，w是一个接口，接口的值是一个指针，指向一个实现了Write()方法的结构体
	// os.Stdout是一个变量，保存了 os.NewFile()这个函数的返回值，这个函数返回的是一个File结构体指针
	// 这个File结构体实现了Write()方法
	// 按照接口的约束条件, w可以使用Write()方法
	// 但不能使用File结构体里实现的其他方法，比如Close()方法

	// 这里不管是w = os.Stdout还是w = new(bytes.Buffer),
	// 都是将w的值改变了，但是w的类型是不变的，都是io.Writer
	// 所以接口的类型是不变的，接口的约束仍然存在

	// 接口的值在我看来很像是一个指针，指向一个实现了接口的某一个结构体
	// 这个结构体可以是一个File结构体，也可以是一个Buffer结构体
	// 实现的方法也不一样，所以调用的结果也不一样
	type name struct {
		firstName string
		lastName  string
	}
	var x, y interface{}
	x = name{"John", "Doe"}
	y = name{"John", "Doe"}
	fmt.Println(x == y) // true
	// 这里的x和y是两个接口，接口的值是两个结构体
	// 两个结构体的值是一样的，所以x和y是相等的

	x = &name{"John", "Doe"}
	y = &name{"John", "Doe"}
	fmt.Println(x == y) // false
	// 这里的x和y是两个接口，接口的值是两个结构体指针
	// 两个结构体指针的值是不一样的，所以x和y是不相等的

	// Go中的接口的nil值和指针类型的nil值是不一样的
	type Empty struct{}
	var z interface{}
	var AEmpty Empty
	var p *int
	fmt.Printf("%T, %[1]v\n", AEmpty)
	fmt.Println(z == AEmpty) // false
	fmt.Println(z == nil)    // true
	fmt.Println(p == nil)    // true
	fmt.Println(z == p)      // false
	// 在实际开发中, 要尽量用nil代替空指针去返回
	// see : 【golang 踩坑之：Shit box】 https://www.bilibili.com/video/BV1G8411j7Q6
}
