英文:
Initialize a nil pointer struct in method
问题
我有一个名为Article的结构体,其中有一个字段叫做Image。默认情况下,Image的值为nil。由于只希望将Image作为Image.Id持久化到数据库中,我使用了bson.BSONGetter、bson.BSONSetter和json.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,因为Image是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)
}
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 &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.
答案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 (
"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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论