Похоже, вы пытаетесь создать тип объединения (то, что ML-семейство языков называет «enum»). Я знаю пару моделей для этого:
0. Основная обработка (playground)
Я подозреваю, что вы делаете ошибки только основной обработки ошибок. В Go мы используем несколько возвращаемых значений и проверяем результат. Это почти наверняка, что вы хотите сделать:
package main
import (
"fmt"
"log"
)
type Data struct {
ID int
Name string
}
type DataError struct {
Message string
ErrorCode string
}
// Implement the `error` interface. `error` is an interface with
// a single `Error() string` method
func (err DataError) Error() string {
return fmt.Sprintf("%s: %s", err.ErrorCode, err.Message)
}
func SomeFunction(returnData bool) (Data, error) {
if returnData {
return Data{ID: 42, Name: "Answer"}, nil
}
return Data{}, DataError{
Message: "A thing happened",
ErrorCode: "Oops!",
}
}
func main() {
// this bool argument controls whether or not an error is returned
data, err := SomeFunction(false)
if err != nil {
log.Fatalln(err)
}
fmt.Println(data)
}
1. Интерфейсы (playground)
Опять же, если ваши варианты хорошие-данные и ошибки, вы, вероятно, следует использовать первый случай (палка с идиомой/конвенцией), но в других случаях у вас может быть несколько «хороших данных». Мы можем использовать интерфейсы для решения этой проблемы. Здесь мы добавляем фиктивный метод, чтобы сообщить компилятору ограничить возможные типы, которые могут реализовать этот интерфейс для тех, у кого есть метод IsResult(). Самым большим недостатком этого является то, что вложение вещей в интерфейс может привести к распределению, которое может быть вредным в узком цикле. Этот шаблон не очень распространен.
package main
import "fmt"
type Result interface {
// a dummy method to limit the possible types that can
// satisfy this interface
IsResult()
}
type Data struct {
ID int
Name string
}
func (d Data) IsResult() {}
type DataError struct {
Message string
ErrorCode string
}
func (err DataError) IsResult() {}
func SomeFunction(isGoodData bool) Result {
if isGoodData {
return Data{ID: 42, Name: "answer"}
}
return DataError{Message: "A thing happened", ErrorCode: "Oops!"}
}
func main() {
fmt.Println(SomeFunction(true))
}
2.Tagged Union (playground)
Этот случай похож на предыдущий случай, за исключением того, что вместо использования интерфейса мы используем структуру с тегом, которая сообщает нам, какой тип данных содержит структура (это похоже на меченый союз в C, за исключением того, что размер структуры представляет собой сумму его потенциальных типов, а не размер его наибольшего потенциального типа). В то время как это занимает больше места, его можно легко распределить по стеклу, тем самым делая его дружественным по течению (я использовал этот метод для сокращения выделов от O (n) до O (1)). В этом случае наш тег является bool, потому что у нас есть только два возможных типа (Data and DataError), но вы также можете использовать C-образное перечисление.
package main
import (
"fmt"
)
type Data struct {
ID int
Name string
}
type DataError struct {
Message string
ErrorCode string
}
type Result struct {
IsGoodData bool
Data Data
Error DataError
}
// Implements the `fmt.Stringer` interface; this is automatically
// detected and invoked by fmt.Println() and friends
func (r Result) String() string {
if r.IsGoodData {
return fmt.Sprint(r.Data)
}
return fmt.Sprint(r.Error)
}
func SomeFunction(isGoodData bool) Result {
if isGoodData {
return Result{
IsGoodData: true,
Data: Data{ID: 42, Name: "Answer"},
}
}
return Result{
IsGoodData: false,
Error: DataError{
Message: "A thing happened",
ErrorCode: "Oops!",
},
}
}
func main() {
// this bool argument controls whether or not an error is returned
fmt.Println(SomeFunction(true))
}
Нет, вы не можете назначать разные типы одной и той же переменной. Вы можете пустым 'interface {}' (https://play.golang.org/p/h5vPe3Et1O), но это не очень хорошее решение. В Go вы обычно используете отдельные значения ошибок. Прошли ли вы через вводные материалы? – JimB
@JimB, да, но интерфейсы для меня до сих пор не ясны :) – tsg
Нет проблем, так как нет необходимости объединять эти два типа. – Volker