2

The original question

I have the following Go code. I would like to handle Foo a struct or Bar a type as a string. With "handle" I mean that I would like to convert/cast/whatever it's underlaying value to the (real) type string. I have a workaround, but I find it unintuitive in the case of a struct.

Going for a Type (instead of a struct) seems the better approach. I don't need to maintain any state, nor do I have any need for "inheriting" type specific functionality, so in my case it should work. However a call to type.String() causes stack recursion. I'm mostly curious if I'm not missing something (obvious).

package main

import "fmt"

type Foo struct {
    string
}

func (f *Foo) String() string {
    return f.string
}


type Bar string

func (b *Bar) String() string {
    return fmt.Sprintf("%s", b) // Cannot use: return string(b) here.
}

func main() {
    a := Foo{"a"}
    var b Bar
    b = "b"

    fmt.Printf("A is: %s\n", a) // Doesn't call a.String() ?
    //fmt.Printf("A is: %s\n", string(a)) // Doesn't work
    fmt.Printf("A is: %s\n", a.string)   // workaround A
    fmt.Printf("A is: %s\n", a.String()) // workaround B, required if I want to use it in a different package

    fmt.Printf("B is: %s\n", b) // Calls b.String()
    fmt.Printf("B is: %s\n", string(b))
    //fmt.Printf("B is: %s\n", b.String()) // Causes a stack overflow
}

Output:

A is: {a}
A is: a
A is: a
B is: b
B is: b

Code on Go's Playground: https://play.golang.org/p/zgrKao4cxa The behaviour is from Go version 1.5.2

Answers

The following are short examples based on the answers of my original questions. Also, the following post helped in understanding and reasoning of the subject: Value receiver vs. Pointer receiver in Golang?

In case of a type, the following works:

type MyString string

func (b MyString) String() string {
    return string(b)
}

Go's Playground link: https://play.golang.org/p/H12bteAk8D

In case of a struct, the following works:

package main

import "fmt"

type MyString struct {
    string
    someState int
}

func (m MyString) String() string {
    return string(m.string)
}

func main() {
    // The verbose version: 
    //var a MyString = MyString{string: "a", someState: 1}

    a := MyString{"a", 1}

    fmt.Printf("A is: %s\n", a)
    fmt.Printf("A is: %s\n", a.String())
}

Go's Playground link: https://play.golang.org/p/GEKeY4rmB8

Community
  • 1
  • 1
Dynom
  • 1,000
  • 12
  • 19
  • 1
    b's String() function can use `return string(*b)`, didn't need to sprintf the value. just needed to match the return type of of the function – David Budworth Jan 25 '16 at 14:56
  • Ah, you're right. I can't believe I missed that. I think I was thrown off by the error message `cannot convert b (type *Bar) to type string`. Thanks for the help. – Dynom Jan 26 '16 at 13:45
  • @Dynom Please don't add answers to your question. If you've got some interesting info to share, add your own answer below. – Duncan Jones Jun 30 '17 at 07:30

1 Answers1

0

You've made a pointer receivers for your String methods, but you are working with values, not pointers to them, so it wouldn't apply. You need to switch to pointers or change String methods signatures:

package main

import "fmt"

type Foo struct {
    string
}

func (f Foo) String() string {
    return "My " + f.string
}

type Bar string

func (b Bar) String() string {
    return fmt.Sprintf("My %s", string(b))
}

func main() {
    a := Foo{"a"}
    var b Bar = "b"

    fmt.Printf("A is: %s\n", a)
    fmt.Printf("B is: %s\n", b)
}

Please, be careful with receivers type:

The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers

Oh, and one more thing. fmt.Sprintf("%s", b) will call String method if it's defined. So, you'll get a recursion.

Community
  • 1
  • 1
alex vasi
  • 5,304
  • 28
  • 31
  • Actually, that makes a lot of sense. But how come dereferencing in the method doesn't work? I've tried this, but that doesn't work: func (f *Foo) String() string { return (*f).string } – Dynom Jan 25 '16 at 14:17
  • 2
    When you pass printf a you are passing it the value a. Change it to &a (pointer to a) and your String() func will get called (from line 24 in your play example). But it's notmally weird to pass around pointers to strings, so this answer makes more sense – David Budworth Jan 25 '16 at 14:55
  • @Dynom, "The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers" – alex vasi Jan 25 '16 at 20:50
  • Allright, that helped. Thanks for the explanations! – Dynom Jan 26 '16 at 13:37