在方法中初始化nil指针结构

I have a struct called Article which has a field called Image. Per default Image has value nil. As Image should be only persisted as Image.Id to database I use the bson.BSONGetter, bson.BSONSetter and json.Marshaler interfaces to fake this behavior.

However internally it is possible to use Image as an io.ReadWriteCloser if I load a file onto this with some other helper.

package main

import (
    "io"
    "fmt"

    "gopkg.in/mgo.v2"
)

type Article struct {
    Name  string
    Image *Image
}

type Image struct {
    Id interface{}

    io.ReadWriteCloser
}

func (i *Image) SetBSON(r bson.Raw) error {
    i = &Image{}

    return r.Marshal(i.Id)
}

func (i *Image) GetBSON() (interface{}, error) {
    return i.Id
}

func (i *Image) MarshalJSON() ([]byte, error) {
    return json.Marshal(i.Id)
}

Playground

The problem with this approach now is that it is not possible to initialize Image in Image.SetBSON as Image is nil.

The receiver is passed by value, including the pointer receiver: it is a copy, and changing its value doesn't change the initial pointer receiver on which the method is called.

See "Why are receivers pass by value in Go?".

A function Setup returning a new *Foo would work better: play.golang.org

func SetUp() *Foo {
    return &Foo{"Hello World"}
}

func main() {
    var f *Foo
    f = SetUp()
}

Output:

Foo: <nil>
Foo: &{Bar:Hello World}

twotwotwo points to a better convention in the comments, which is to make a package function foo.New(), as in sha512.New().
But here, your Setup() function might do more than just creating a *Foo.

bson.Unmarshal creates a pointer to an Image value when it comes across it in the bson data. So once we enter SetBSON i is already a valid pointer to an Image struct. That means that there is no reason for you to allocate the Image.

package main

import (
    "fmt"
    "io"

    "gopkg.in/mgo.v2/bson"
)

type Article struct {
    Name  string
    Image *Image `bson:"image,omitempty"`
}

type Image struct {
    Id          interface{}
    AlsoIgnored string
    io.ReadWriteCloser
}

func (i *Image) SetBSON(r bson.Raw) error {
    err := r.Unmarshal(&i.Id)
    return err

}

func (i Image) GetBSON() (interface{}, error) {
    return i.Id, nil
}

func main() {
    backAndForth(Article{
        Name: "It's all fun and games until someone pokes an eye out",
        Image: &Image{
            Id:          "123",
            AlsoIgnored: "test",
        },
    })

    backAndForth(Article{Name: "No img attached"})
}

func backAndForth(a Article) {
    bsonData, err := bson.Marshal(a)
    if err != nil {
        panic(err)
    }

    fmt.Printf("bson form: '%s'
", string(bsonData))

    article := &Article{}
    err = bson.Unmarshal(bsonData, article)
    if err != nil {
        panic(err)
    }
    fmt.Printf("go form  : %#v - %v
", article, article.Image)

}

http://play.golang.org/p/_wb6_8Pe-3

Output is:

bson form: 'Tname6It's all fun and games until someone pokes an eye outimage123'
go form  : &main.Article{Name:"It's all fun and games until someone pokes an eye out", Image:(*main.Image)(0x20826c4b0)} - &{123  <nil>}
bson form: 'nameNo img attached'
go form  : &main.Article{Name:"No img attached", Image:(*main.Image)(nil)} - <nil>