Golang中类型定义令人意外的赋值行为解析
Golang中类型定义令人意外的赋值行为解析 根据Go语言规范中关于类型定义的描述:
新类型被称为定义类型。它不同于任何其他类型,包括创建它的源类型。
同时
定义类型总是不同于任何其他类型。
我原本期望,如果我有一个类型定义 Foo 的变量,那么只有 Foo 类型的值可以赋值给它,而底层类型或任何其他类型的值永远不行。
这是使用类型定义的主要原因之一,即我不想接受底层类型的任意值,因为这很可能是调用方存在错误。
但在Go中似乎并非如此。请看以下示例,我有一个底层类型为 string 的类型 Foo。当字符串以字面量形式传递时,编译器允许我传入任何字符串(但如果传递的值已经是 string 类型则不行?这似乎有些随意)。
package main
func main() {
type Foo string
f := func(t Foo) {}
f(Foo("abc")) // 正常工作,符合预期。
f("abc") // 意外地工作。预期会出现类型错误。
var s = "abc"
f(s) // 符合预期的类型错误
}
这肯定不是一个特性吧?如果是的话,这种行为似乎与规范不符。
更多关于Golang中类型定义令人意外的赋值行为解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
我明白了,我之前从未听说过“无类型常量”这个术语,而且允许这种情况发生实在令人遗憾 😛 感谢您的回复!
更多关于Golang中类型定义令人意外的赋值行为解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
有一些变通方法。如果你不希望字符串字面量能够作为 Foo 类型使用,可以将 Foo 转换为结构体:https://play.golang.org/p/cVhNn33Wt4t
package main
import (
"fmt"
)
type Foo struct {
s string
}
func (f Foo) String() string {
return f.s
}
func main() {
f := Foo{"bar"}
fmt.Println(f)
}
根据Go语言规范,你观察到的行为确实是语言设计的一部分,但需要仔细区分类型转换和赋值兼容性。
在Go中,常量(包括无类型常量)具有特殊的类型处理规则。当使用无类型常量(如字符串字面量"abc")时,编译器允许在满足特定条件时进行隐式转换。
你的示例中:
f("abc") // 正常工作
这里的"abc"是一个无类型字符串常量,编译器允许它隐式转换为Foo类型,因为Foo的底层类型是string。
而:
var s = "abc"
f(s) // 类型错误
这里的s已经是显式的string类型变量,需要显式类型转换。
这种行为在规范中有明确说明。根据Go规范关于常量的部分:
常量可以隐式转换为非常量类型,只要该值可以表示为该类型的值。
对于有类型变量,则需要显式转换:
type Foo string
func main() {
var s string = "abc"
var foo Foo
// 需要显式转换
foo = Foo(s)
// 但字面常量可以隐式转换
foo = "def" // 允许
// 函数调用同理
f := func(t Foo) {}
f("ghi") // 允许 - 无类型常量
f(Foo(s)) // 需要显式转换
}
这种设计允许在保持类型安全的同时,提供一定的灵活性处理常量值。如果你需要严格的类型检查,可以考虑使用结构体包装:
type Foo struct {
value string
}
func NewFoo(s string) Foo {
return Foo{value: s}
}
// 这样就不会接受字符串字面量了
func main() {
f := func(t Foo) {}
f(NewFoo("abc")) // 必须通过构造函数
// f("abc") // 编译错误
}
所以这不是bug,而是Go语言类型系统中针对常量的特殊处理规则。

