3

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.

1
  • 2
    What is your question? I don't see a single sentence that looks like it could be a question in your post. Commented Aug 27, 2014 at 10:57

2 Answers 2

7

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.

Sign up to request clarification or add additional context in comments.

8 Comments

Or, following the convention of the standard library, NewFoo() *Foo (or if the module is named foo, just foo.New(), like how crypto/sha512 exports sha512.New()).
@VonC okay I think it is a good idea to show you the big picture maybe my approach makes more sense then.
@twotwotwo good point. I have included it in the answer for more visibility.
@bodokaiser whatever setup is doing in your "big picture", it needs to return a *Foo (in addition of anything else it does)
@VonC Really? Maybe you have a better idea for the overall case.
|
1

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'\n", string(bsonData))

    article := &Article{}
    err = bson.Unmarshal(bsonData, article)
    if err != nil {
        panic(err)
    }
    fmt.Printf("go form  : %#v - %v\n", 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>

4 Comments

Interesting alternative, more detailed than my answer. +1
@stengaard I do not get the benefit of such a method. I mean isn't the problem that I cannot set a field in BSONSetter on an nil pointer?
@bodokaiser - yes, you are right. Just updated my answer to actually solve the original problem (though my first solution did sidestep the issue).
But in my case this leads to a panic because nil has no field id.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.