面试者与面试官

1、什么情况下会使用空struct,它会分配内存吗?

空结构体 struct{} 是一个特殊的结构体,它没有字段属性,一些常见的使用场景包括:集合、实现接口、信号传递。

Go语言没有内置的集合类型,但是我们可以使用map[T]struct{}来模拟集合,因为struct{}不占用空间,因此只需要存储键,能节省内存。

空结构体还可以用来实现没有方法的接口,这中接口通常用于占位符或标记的用途。

type NoMethodInterface interface {
}

type EmptyStruct struct{}

type AnotherEmptyStruct struct{}

func process(i NoMethodInterface) {
	// 什么也不干,占位,将来可以扩展
}

// 因为NoMethodInterface接口没有任何方法,所以任何类型都被认为是这个接口的实现,包括空结构体EmptyStruct
func main() {
	var empty EmptyStruct
	var another AnotherEmptyStruct
	// 同一接口的两种不同的实现方式
	process(empty)
	process(another)
}

在Go的并发编程中,使用 chan struct{} 来传递信号,由于 struct{} 不占用内存,因此可以高效的用于信号传递而无需携带任何额外的数据。

最后,空结构体 struct{} 在实例化的时候并不会分配内存,每个空结构体的实例在内存中占用0个字节,即使在一个集合中存储了多个struct{}实例,它们也不会占用额外的内存空间而只会占用集合本身存储键所需要的内存空间。

2、Go中接口有什么用,如何实现?

接口的精髓在于抽象和多态度,接口允许我们定义一个行为的集合,而不关心实现这些行为的具体类型,这样我们就可以编写更加抽象的代码,当我们需要改变具体实现时,不需要修改使用接口的代码,对调用者做到了透明。接口使得不同类型可以实现相同的方法,从而通过接口类型的变量来引用不同的具体实现,这种特性大大增加了代码的灵活性和可扩展性。此外,接口还有利于代码解耦,它定义的是一组行为而不是具体实现,使得不同实现可以通过接口进行通信,从而减少了模块之间的耦合。

Go接口的实现非常简单,只需要实现接口中定义的所有方法即可。

Go源码中最通俗易懂的实现之一就是标准库中的 io.Writer 接口:

package io

type Writer interface {
	Write(p []byte) (n int, err error)
}

io.Writer接口非常简洁,只有一个Write方法,任何实现了Write方法的类型都可以被视为io.Writer,例如标准库中的os.File类型就实现了Writer接口:

package os

func (f *Fule) Write(p []byte) (n int, err error) {
	// 具体实现细节
}

这样我们就可以用下面的代码来使用 io.Writer 接口:

func main() {
	var writer io.Writer
	
	f, err := os.Create("example.txt")
	if err != nil {
		return
	}
	defer f.Close()
	
	// 将打开的文件赋值给writer变量
	writer = f
	
	// 使用writer向文件中写入数据
	_, err := writer.Write([]byte("Hello World!"))
	if err != nil {
		return
	}
}