一文掌握 Golang Empty Struct 的所有用法

在 Go 語言中,空結構體被用作佔位符,當我們想要創建一個不攜帶任何數據的類型時。這個概念經常被用來表示特定的行爲,或者將該類型用作類似集合的數據結構。

下面是個使用空結構體的例子:

package main

import (
 "fmt"
 "unsafe"
)

// EmptyStruct is an empty struct type
type EmptyStruct struct{}

func main() {
 // Create an instance of EmptyStruct
 var myEmptyStruct EmptyStruct

 // Print the size of EmptyStruct (it will be 0 bytes)
 fmt.Println("Size of EmptyStruct:", unsafe.Sizeof(myEmptyStruct))
}

在上面的例子中,EmptyStruct 沒有任何字段,因此它不佔用任何內存空間。它經常被用作只需要一個信號或標記而不攜帶任何額外數據的時候。

在 Go 語言中,我們可以使用一個映射 (map),其中將一個虛擬類型(比如一個空結構體)作爲值,來創建類似集合的數據結構。由於映射不能有重複的鍵,我們可以使用鍵來表示集合的元素。

下面是一個使用空結構體作爲類似集合的數據結構的示例:

package main

import "fmt"

// Set is a set-like data structure using an empty struct
type Set map[string]struct{}

func main() {
    // Create a set
    mySet := make(Set)

    // Add elements to the set
    mySet["apple"] = struct{}{}
    mySet["banana"] = struct{}{}
    mySet["orange"] = struct{}{}

    // Check if an element is in the set
    if _, exists := mySet["banana"]; exists {
        fmt.Println("Banana is in the set!")
    }

    // Print all elements in the set
    for key := range mySet {
        fmt.Println(key)
    }
}

在這個例子中,Set 類型是一個映射,其中鍵是字符串,值是空結構體。與鍵關聯的實際值並不重要;在映射中鍵的存在表示集合的成員資格。這是在 Go 語言中空結構體的常見用法。

在 Go 語言中,空結構體經常與接口結合使用,以創建簡潔而具有表現力的代碼。

下面的示例演示瞭如何使用空結構體與接口:

package main

import "fmt"

// Eater is an interface with an Eat method
type Eater interface {
 Eat()
}

// Dog is a type that implements the Eater interface
type Dog struct {
 Name string
}

// Implement the Eat method for Dog
func (d Dog) Eat() {
 fmt.Printf("%s is eating\n", d.Name)
}

// Cat is a type that implements the Eater interface
type Cat struct {
 Name string
}

// Implement the Eat method for Cat
func (c Cat) Eat() {
 fmt.Printf("%s is eating\n", c.Name)
}

func main() {
 // Create instances of Dog and Cat
 myDog := Dog{Name: "Buddy"}
 myCat := Cat{Name: "Whiskers"}

 // Use an empty struct to create a set of eaters
 eaters := make(map[Eater]struct{})
 eaters[myDog] = struct{}{}
 eaters[myCat] = struct{}{}

 // Iterate over the eaters and make them eat
 for eater := range eaters {
  eater.Eat()
 }
}

在這個例子中,Eater 接口定義了一個 Eat() 方法。Dog 和 Cat 類型都實現了這個接口。eatrs 映射使用空結構體作爲值,創建了一個實現了 Eater 接口的類型集合。

然後程序遍歷 eaters 並在每個上調用 Eat() 方法,演示瞭如何使用空結構體創建共享公共接口的類型集合。當開發者想要基於共享行爲將不同類型分組而不存儲額外數據時,這種用法就特別有用。

當一個接口沒有方法時,它被所有類型實現,包括空結構體。這對於創建不需要指定特定行爲的通用結構非常有用。

package main

import "fmt"

// Empty interface with no methods
type MyInterface interface{}

// Function that takes any type implementing MyInterface
func myFunction(value MyInterface) {
 fmt.Println("Value:", value)
}

func main() {
 // Using an empty struct to implement the empty interface
 emptyStructInstance := struct{}{}
 myFunction(emptyStructInstance)

 // Using other types to implement the empty interface
 intInstance := 42
 myFunction(intInstance)

 stringInstance := "Hello, Go!"
 myFunction(stringInstance)
}

MyInterface 是一個沒有方法的空接口,這意味着它可以被任何類型滿足,包括空結構體。函數 myFunction 接受一個類型爲 MyInterface 的參數,允許它接受任何實現空接口的類型的值。一個空結構體的實例(emptyStructInstance)被傳遞給 myFunction。

其他類型,比如 int 和 string,也被傳遞給了 myFunction,因爲它們自動滿足了空接口。

空結構體可以有效地用於創建輕量級的模擬實現。當開發者想要專注於行爲而不是實際數據時就非常有用。

考慮一個場景,我們有一個代表數據存儲的接口,就可以創建一個空結構體來模擬這個接口:

package main

// DataStore is an interface for storing and retrieving data
type DataStore interface {
 Save(data string) error
 Retrieve() (string, error)
}

// MockDataStore is an empty struct implementing the DataStore interface
type MockDataStore struct{}

// Save is a mock implementation of the Save method
func (m MockDataStore) Save(data string) error {
 // Mock save behavior
 return nil
}

// Retrieve is a mock implementation of the Retrieve method
func (m MockDataStore) Retrieve() (string, error) {
 // Mock retrieve behavior
 return "Mocked Data", nil
}

在這個例子中,MockDataStore 是 DataStore 接口的模擬實現。它不攜帶任何數據,但提供了 Save 和 Retrieve 方法的模擬實現。

在測試過程中,我們可以使用這個模擬實現來隔離並測試依賴於 DataStore 接口的組件的行爲。下面是在測試中使用模擬實現的示例:

package main

import (
 "fmt"
 "testing"
)

func TestSaveAndRetrieve(t *testing.T) {
 // Create an instance of the mock data store
 mockDataStore := MockDataStore{}

 // Use the mock data store in your test
 err := mockDataStore.Save("Test Data")
 if err != nil {
  t.Errorf("Unexpected error during save: %v", err)
 }

 data, err := mockDataStore.Retrieve()
 if err != nil {
  t.Errorf("Unexpected error during retrieve: %v", err)
 }

 // Assert the retrieved data
 expectedData := "Mocked Data"
 if data != expectedData {
  t.Errorf("Expected data %s, but got %s", expectedData, data)
 }
}

這種方法提供了一種簡單輕量的方式,利用空結構體創建模擬實現,使開發者能夠專注於測試代碼的行爲。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/S0BpYUvHtd5zf-Z_SOnTwQ