在方法中初始化一个空指针结构体。

huangapple go评论77阅读模式
英文:

Initialize a nil pointer struct in method

问题

我有一个名为Article的结构体,其中有一个字段叫做Image。默认情况下,Image的值为nil。由于只希望将Image作为Image.Id持久化到数据库中,我使用了bson.BSONGetterbson.BSONSetterjson.Marshaler接口来模拟这种行为。

然而,在内部,如果我使用一些其他的辅助函数将文件加载到Image中,就可以将Image作为io.ReadWriteCloser使用。

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

这种方法的问题在于,在Image.SetBSON中无法初始化Image,因为Imagenil

英文:

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

得分: 7

接收器是按值传递的,包括指针接收器:它是一个副本,改变其值不会改变调用方法的初始指针接收器。

参见“为什么在Go中接收器是按值传递?”。

返回一个新的*Foo的函数SetUp会更好:play.golang.org

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

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

输出:

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

twotwotwo在评论中提到了一个更好的约定,即创建一个包函数foo.New(),就像sha512.New()一样。但是在这里,你的Setup()函数可能不仅仅是创建一个*Foo

英文:

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: <kbd>play.golang.org</kbd>

func SetUp() *Foo {
	return &amp;Foo{&quot;Hello World&quot;}
}

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

Output:

Foo: &lt;nil&gt;
Foo: &amp;{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.

答案2

得分: 1

bson.Unmarshal在遇到bson数据中的Image值时,会创建一个指向Image值的指针。因此,一旦我们进入SetBSON函数,i已经是一个指向有效的Image结构体的指针。这意味着你没有必要分配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)

}

输出结果为:

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>

你可以在这里查看代码:http://play.golang.org/p/_wb6_8Pe-3

英文:

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 (
	&quot;fmt&quot;
	&quot;io&quot;

	&quot;gopkg.in/mgo.v2/bson&quot;
)

type Article struct {
	Name  string
	Image *Image `bson:&quot;image,omitempty&quot;`
}

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

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

}

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

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

	backAndForth(Article{Name: &quot;No img attached&quot;})
}

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

	fmt.Printf(&quot;bson form: &#39;%s&#39;\n&quot;, string(bsonData))

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

}

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

Output is:

bson form: &#39;Tname6It&#39;s all fun and games until someone pokes an eye outimage123&#39;
go form  : &amp;main.Article{Name:&quot;It&#39;s all fun and games until someone pokes an eye out&quot;, Image:(*main.Image)(0x20826c4b0)} - &amp;{123  &lt;nil&gt;}
bson form: &#39;nameNo img attached&#39;
go form  : &amp;main.Article{Name:&quot;No img attached&quot;, Image:(*main.Image)(nil)} - &lt;nil&gt;

huangapple
  • 本文由 发表于 2014年8月27日 14:42:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/25520485.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定