Go中的链接功能?

时间:2013-08-09 17:31:25

标签: go

我试过这样做:

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.

为什么我不能使这个链工作?

4 个答案:

答案 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

(见:http://play.golang.org/p/FaCD8AQtIX

答案 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())
    }
}