Chain functions in Go?

I tried to do this:

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) } 

But I got these errors:

 prog.go:30: cannot call pointer method on s.tolower() prog.go:30: cannot take the address of s.tolower() Program exited. 

Why can't I get this chain to work?

+4
source share
4 answers

It works:

main package

 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) } 

The return type is String, for functions defined for pointers to String. It would be impractical to bind them.

+7
source

tolower () and toupper () have a pointer to String as receivers, but they return a String (not a pointer to a string).

You can fix this by changing one or the other.

eg. change the function signature to:

 func (s *String) toupper() *String 

or

 func (s String) toupper() String 

(see: http://play.golang.org/p/FaCD8AQtIX )

+4
source

When you call a method with a receiver of a pointer to a variable ( s in your example), then the address of this value will be taken automatically. So you basically call (&s).toupper() . This mechanism works for all addressable values.

The return values โ€‹โ€‹of functions are not addressed unless you store them in a variable (so that they have a constant place in the current stack stack or heap).

I would recommend the following API because it looks like the user of your string type should work with String , not *String . Therefore, it makes sense to develop a consistent API that also uses String to avoid confusion. Passing a string by value is extremely large because they are implemented as pointers to immutable arrays inside:

 func (s String) tolower() String { return String(strings.ToLower(string(s))) } 

This method does not need a pointer receiver, because it does not change the current line. Instead, it returns a new line. You can also easily link these methods.

Alternatively, you can implement the methods as follows:

 func (s *String) tolower() *String { *s = String(strings.ToLower(string(*s))) return s } 

In this case, you continue to return the same pointer. Thus, to call (s.tolower()).toupper() you need to have the address s , which is possible since you assigned it to a variable. Then further calls to methods in the chain are possible, because you call them with a pointer to your initial variable. This is different from your attempt at a method chain, each method call had to take the address of a temporary variable in order to change it (which is not very useful).

+2
source

maybe you can try this project: 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()) } } 
0
source

Source: https://habr.com/ru/post/1496203/


All Articles