我试过这样做:
package main
import (
"fmt"
"strings"
)
type String string
func (s *String) tolower() String {
*s = String(strings.ToLower(string(*s)))
return *s
}
func (s *String) toupper() String {
*s = String(strings.ToUpper(string(*s)))
return *s
}
func main() {
var s String = "ASDF"
(s.tolower()).toupper() // this fails
// s.toupper();s.tolower(); // this works
// s.tolower().toupper() // this fails too
fmt.Println(s)
}
但我收到了这些错误:
prog.go:30: cannot call pointer method on s.tolower()
prog.go:30: cannot take the address of s.tolower()
Program exited.
为什么我不能使这个链工作?
答案 0 :(得分:13)
这有效:
package main
import (
"fmt"
"strings"
)
type String string
func (s *String) tolower() *String {
*s = String(strings.ToLower(string(*s)))
return s
}
func (s *String) toupper() *String {
*s = String(strings.ToUpper(string(*s)))
return s
}
func main() {
var s String = "ASDF"
(s.tolower()).toupper()
s.toupper();
s.tolower();
s.tolower().toupper()
fmt.Println(s)
}
对于在指向String的指针上定义的函数,返回类型为String。能够将它们链起来是没有意义的。
答案 1 :(得分:4)
tolower()和toupper()将指针指向String作为接收者,但它们返回的是String(不是指向String的指针)。
您可以通过更改其中一个来解决此问题。
e.g。将函数的签名更改为:
func (s *String) toupper() *String
或
func (s String) toupper() String
答案 2 :(得分:3)
当您在变量(示例中为s
)上调用带有指针接收器的方法时,将自动获取该值的地址。所以,你基本上是在呼叫(&s).toupper()
。此机制适用于addressable的所有值。
除非将函数存储在变量中(以便它们在当前堆栈框架或堆中具有永久位置),否则函数的返回值是不可寻址的。
我建议使用以下API,因为看起来您的字符串类型的用户应该使用String
而不是*String
。因此,设计一致的API也是有意义的,该API也使用String
来避免混淆。无论如何,按值传递字符串非常快,因为它们在内部实现为指向不可变数组的指针:
func (s String) tolower() String {
return String(strings.ToLower(string(s)))
}
此方法不需要指针接收器,因为它不会修改当前字符串。它返回一个新字符串。您也可以轻松地链接这些方法。
或者,您可以这样实现方法:
func (s *String) tolower() *String {
*s = String(strings.ToLower(string(*s)))
return s
}
在这种情况下,您将继续返回相同的指针。因此,为了调用(s.tolower()).toupper()
,您需要能够获取s
的地址,因为您已将其分配给变量。然后链中的所有其他方法调用也是可能的,因为您使用指向初始变量的指针调用它们。这与您对链接方法的尝试不同,每个方法调用必须采用临时变量的地址才能对其进行修改(这不是非常有用)。
答案 3 :(得分:0)
也许你可以尝试这个项目:https://github.com/Laisky/go-chaining
import "github.com/Laisky/go-chaining"
func toLower(c *chaining.Chain) (interface{}, error) {
v := c.GetString()
return strings.ToLower(v), nil
}
func toUpper(c *chaining.Chain) (interface{}, error) {
v := c.GetString()
return strings.ToUpper(v), nil
}
func TestChainWithString(t *testing.T) {
f := func() (string, error) { return "aBcD", nil }
r := chaining.New(f()).
Next(toLower).
Next(toUpper)
expectVal := "ABCD"
if r.GetString() != expectVal {
t.Errorf("expect %v, got %v", expectVal, r.GetString())
}
}