英文:
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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论