10

私はこれをやってみました:

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

4 に答える 4

3

変数のポインター レシーバーを使用してメソッドを呼び出すと (このs例では)、その値のアドレスが自動的に取得されます。したがって、基本的には を呼び出して(&s).toupper()います。このメカニズムは、アドレス指定可能なすべての値に対して機能します。

関数の戻り値は、(現在のスタック フレームまたはヒープに永続的な場所を持つように) 変数に格納しない限り、アドレス指定できません。

あなたの文字列型のユーザーはStringではなくで動作するはずなので、次の API をお勧めします*StringStringしたがって、混乱を避けるために も使用する一貫した API を設計することは理にかなっています。文字列を値で渡すのは、内部的に不変配列へのポインタとして実装されているため、非常に高速です。

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その後、初期変数へのポインターを使用して呼び出すため、チェーン内のそれ以降のすべてのメソッド呼び出しも可能です。これは、メソッドをチェーン化する試みとは異なります。各メソッド呼び出しは、それを変更するために一時変数のアドレスを取得する必要がありました (これはあまり役に立ちません)。

于 2013-08-09T18:05:57.173 に答える