如果需要中断 for-select 语法结构,通常是需要使用标签来实现的,示例如下:
func main() {
L:
for {
select {
case <-time.After(time.Second):
fmt.Println("hello")
default:
break L
}
}
fmt.Println("ending")
}
如你所见,在 for-select 语法结构中,需要结合标签的使用来实现中断。这是一种常见的用法,但是我并不喜欢。因为示例中的循环结构看起来很简短明了,这样使用并不显得有太大的问题,但在实际中通常要比这个复杂得多,并且追踪中断情形会显得十分冗长繁琐。
因此如果我需要在 for-select 语法结构中实现中断,我通常会将其封装成一个函数来实现:
func main() {
foo()
fmt.Println("ending")
}
func foo() {
for {
select {
case <-time.After(time.Second):
fmt.Println("hello")
default:
return
}
}
}
这就简洁优雅多了,当然你也可以在这个函数中返回错误(或任意其它值)来帮助函数调用者完善业务逻辑的处理,例如这样:
// blocking
if err := foo(); err != nil {
// 处理错误
}
将 slice 或 map 定义成自定义类型可以让你的代码更容易维护
假设有一个 Server 类型和一个返回服务器列表的函数:
type Server struct {
Name string
}
func ListServers() []Server {
return []Server{
{Name: "Server1"},
{Name: "Server2"},
{Name: "Foo1"},
{Name: "Foo2"},
}
}
现在假设需要获取某些特定名字的服务器。需要对 ListServers () 做一些改动,增加筛选条件:
// ListServers 返回服务器列表。只会返回包含 name 的服务器。空的 name 将会返回所有服务器。
func ListServers(name string) []Server {
servers := []Server{
{Name: "Server1"},
{Name: "Server2"},
{Name: "Foo1"},
{Name: "Foo2"},
}
// 返回所有服务器
if name == "" {
return servers
}
// 返回过滤后的结果
filtered := make([]Server, 0)
for _, server := range servers {
if strings.Contains(server.Name, name) {
filtered = append(filtered, server)
}
}
return filtered
}
现在可以用这个来筛选有字符串 Foo 的服务器:
func main() {
servers := ListServers("Foo")
// 输出: "servers [{Name:Foo1} {Name:Foo2}]"
fmt.Printf("servers %+vn", servers)
}
显然这个函数能够正常工作。不过它的弹性并不好。如果你想对服务器集合引入其他逻辑的话会如何呢?例如检查所有服务器的状态,为每个服务器创建一个数据库记录,用其他字段进行筛选等等……
现在引入一个叫做 Servers 的新类型,并且修改原始版本的 ListServers () 返回这个新类型:
type Servers []Server
// ListServers 返回服务器列表
func ListServers() Servers {
return []Server{
{Name: "Server1"},
{Name: "Server2"},
{Name: "Foo1"},
{Name: "Foo2"},
}
}
现在需要做的是只要为 Servers 类型添加一个新的 Filter () 方法:
// Filter 返回包含 name 的服务器。空的 name 将会返回所有服务器。
func (s Servers) Filter(name string) Servers {
filtered := make(Servers, 0)
for _, server := range s {
if strings.Contains(server.Name, name) {
filtered = append(filtered, server)
}
}
return filtered
}
现在可以针对字符串 Foo 筛选服务器:
func main() {
servers := ListServers()
servers = servers.Filter("Foo")
fmt.Printf("servers %+vn", servers)
}
哈!看到你的代码是多么的简单了吗?还想对服务器的状态进行检查?或者为每个服务器添加一条数据库记录?没问题,添加以下新方法即可:
func (s Servers) Check()
func (s Servers) AddRecord()
func (s Servers) Len()
不要在String()
方法里面调用涉及String()
方法的方法
它会导致意料之外的错误,比如下面的例子,它导致了一个无限迭代(递归)调用(TT.String()
调用fmt.Sprintf
,而fmt.Sprintf
又会反过来调用TT.String()
...),很快就会导致内存溢出:
type TT float64
func (t TT) String() string {
return fmt.Sprintf("%v", t)
}
t. String()