我有以下代码:
package main
import (
"log"
)
type Data struct {
Id int
Name string
}
type DataError struct {
Message string
ErrorCode string
}
func main() {
response := Data{Id: 100, Name: `Name`}
if true {
response = DataError{Message: `message`, ErrorCode: `code`}
}
log.Println(response)
}
此代码返回错误:
./ start.go:20:不能使用DataError文字(类型DataError)作为类型 作业中的数据
似乎是我无法为不同类型的response
var数据分配(在我的情况下为DataError
)。我听说可能的解决办法是通过接口统一Data
和DataError
结构。或许还有另一个更好的解决方案?
你能指点我如何解决这个问题吗?
由于
答案 0 :(得分:3)
看起来你正试图建立联盟类型(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)
同样,如果你的选择是好的数据和错误,你应该使用第一种情况(坚持使用成语/惯例),但有时你可能有多个&#34;良好的数据&#34;选项。我们可以使用接口来解决这个问题。在这里,我们重新添加一个伪方法,告诉编译器将可以实现此接口的可能类型约束到具有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。标记联盟(playground)
这种情况与前一种情况类似,除了使用接口之外,我们使用带有标记的结构来告诉我们结构包含哪种类型的数据(这类似于C中的标记联合) ,除了结构的大小是其潜在类型的总和,而不是其最大潜在类型的大小)。虽然这会占用更多空间,但它可以很容易地进行堆栈分配,从而使其紧密循环友好(我已经使用这种技术来减少从O(n)到O(1)的分配)。在这种情况下,我们的标签是bool,因为我们只有两种可能的类型(Data和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))
}
答案 1 :(得分:1)
除非使用特定的接口签名或空接口,否则不能将2个不可“分配”的不同类型分配给同一个变量。
https://golang.org/ref/spec#Assignability
该代码将编译:
func main() {
var response interface{} // empty interface AKA Object AKA void pointer
response = Data{Id: 100, Name: `Name`}
if true {
response = DataError{Message: `message`, ErrorCode: `code`}
}
log.Println(response)
}
因为每个类型都实现了空接口,但只有在没有其他选项的情况下才能这样做。
如果2种类型共享某些方法,则使用特定的接口,例如(伪代码):
type Responder interface {
Respond() string
}
type Data struct { /* code */
}
func (d Data) Respond() string { return "" }
type DataError struct { /* code */
}
func (d DataError) Respond() string { return "" }
func main() {
var response Responder // declared as interface
response = Data{}
response = DataError{}
fmt.Println(response)
}
每当你怀疑对go规范的快速扫描是否有用时,它是唯一的权威,并且与大多数规范相比都写得非常好。在大多数情况下,规则非常明确,这是Go的优势。