Home  >  Article  >  Backend Development  >  Go - Force interface to be satisfied only by types with pointer receivers on methods?

Go - Force interface to be satisfied only by types with pointer receivers on methods?

PHPz
PHPzforward
2024-02-10 09:27:201163browse

Go - 强制接口仅由方法上带有指针接收器的类型满足?

php editor Baicao introduces to you the mandatory interface rules in Go language, that is, only types with pointer receivers on methods can meet the requirements of the interface. Go language is a statically typed programming language that implements polymorphism through interfaces. When defining an interface, you can specify the receiver type of a method, which can be a value type or a pointer type. However, when we use mandatory interface rules, only types with pointer receivers on methods can meet the requirements of the interface. This is because pointer types can modify the content of the value, but value types cannot. This rule ensures that interface methods do not cause unpredictable behavior when manipulating values. By understanding this rule, we can better understand the use and design of interfaces in the Go language.

Question content

I'm doing some experiments with type parameters to come up with a general way of concatenating structures to generate responses to json http requests.

The method interface that the structure must implement has a setparams method. As long as the implementation uses pointer receivers, this will work as expected.

My question: If setparams has a value receiver, is there any way to make it a compile-time error?

The following example demonstrates the problem with setparams with a value receiver:

package main

import (
    "encoding/json"
    "fmt"
    "log"
)

type PingParams struct {
    Name string
}

type PingResponse struct {
    Message string
}

func (p PingParams) Greeting() string {
    if p.Name != "" {
        return fmt.Sprintf("Hello, %s", p.Name)
    }

    return fmt.Sprintf("Hello, nobody!")
}

type GoodPing struct {
    Params PingParams
}

// SetParams has a pointer receiver.
func (m *GoodPing) SetParams(p PingParams) {
    fmt.Printf("assign %v with pointer receiver, Good!\n", p)
    m.Params = p
}
func (m GoodPing) Run() (*PingResponse, error) {
    return &PingResponse{Message: fmt.Sprintf("%T %s", m, m.Params.Greeting())}, nil
}

type BadPing struct {
    Params PingParams
}

// SetParams has a value receiver.
func (m BadPing) SetParams(p PingParams) {
    fmt.Printf("assign %v with value receiver, Bad!\n", p)
    m.Params = p
}
func (m BadPing) Run() (*PingResponse, error) {
    return &PingResponse{Message: fmt.Sprintf("%T %s", m, m.Params.Greeting())}, nil
}

type Method[M, RQ, RS any] interface {
    // Run builds the RPC result.
    Run() (*RS, error)
    // SetParams is intended to set the request parameters in the struct implementing the RPC method.
    // This then allows the request parameters to be easily available to all methods of the Method struct.
    // The method MUST have a pointer receiver. This is NOT enforced at compile time.
    SetParams(p RQ)
    // The following line requires the implementing type is a pointer to M.
    *M
    // https://stackoverflow.com/a/72090807
}

func HandlerMethod[M, RQ, RS any, T Method[M, RQ, RS]](in json.RawMessage) (*RS, error) {
    // A real implementation of this would return a func for wiring into a request router

    var req RQ

    err := json.Unmarshal(in, &req)

    if err != nil {
        return nil, err
    }

    var m T = new(M)

    m.SetParams(req)

    return m.Run()
}

func main() {

    payload := []byte(`{"Name": "Mark"}`)

    bad, err := HandlerMethod[BadPing, PingParams, PingResponse](payload)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(bad.Message)

    good, err := HandlerMethod[GoodPing, PingParams, PingResponse](payload)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(good.Message)
}

https://go.dev/play/p/eii8adkmdxe

Solution

You can't do this.

When you do the following in your code:

var m T = new(M)

Even though the type set of t only includes *m as type items, the method set of *m is also included on m Declared method. The compiler cannot check how this method appears in *m's method set.

When declaring a method setparam on badping, it is your responsibility to ensure that the method does not attempt in vain to modify the receiver.

The above is the detailed content of Go - Force interface to be satisfied only by types with pointer receivers on methods?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete