英文:
Why does serving a struct via http create a copy?
问题
我注意到在Go语言中创建一个结构体并在其New
函数中注册一个http处理程序时出现了一些奇怪的行为。
考虑以下代码:
package main
import (
"fmt"
"net/http"
)
type Counter struct {
name string
value int
}
func New(name string) Counter {
c := Counter{
name: name,
value: 0,
}
http.HandleFunc("/", c.serve)
return c
}
func (c *Counter) inc() { c.value++ }
func (c *Counter) reset() { c.value = 0 }
func (c *Counter) nameApp(n string) { c.name += n }
func (c *Counter) print() { fmt.Printf("[%s]: %d (%p)\n", c.name, c.value, &c) }
func (c *Counter) Reinit(name string, value int) {
c.name = name
c.value = value
}
func (c *Counter) serve(w http.ResponseWriter, req *http.Request) {
c.inc()
c.nameApp("-foo")
fmt.Println("Counter served:")
c.print()
w.WriteHeader(http.StatusOK)
w.Write([]byte{})
}
func main() {
c := New("My New Counter")
fmt.Println("New Counter:")
c.print()
c.Reinit("My reinit Counter", 10)
fmt.Println("Counter after Reinit() call:")
c.print()
http.ListenAndServe("localhost:9000", nil)
}
运行代码后,输出如下:
New Counter:
[My New Counter]: 0 (0xc00012a2a0)
Counter after Reinit() call:
[My reinit Counter]: 10 (0xc00012a2a0)
发送两个请求到服务器后,输出如下:
Counter served:
[My New Counter-foo]: 1 (0xc00012a2c0) // 期望输出为"[My reinit Counter]: 11 (0xc00012a2a0)"
Counter served:
[My New Counter-foo-foo]: 2 (0xc00012a2c0) // 期望输出为"[My reinit Counter]: 12 (0xc00012a2a0)"
为什么结构体的行为不符合我的预期,即使我正确使用了指针接收器?
如何修改我的结构体,使其能够从主程序或其他任何程序中进行修改,并反映在关联的http请求处理程序中?
英文:
I noticed some strange behaviour when I create a struct in go that registers an http-Handler in its New
function.
Consider the following code:
package main
import (
"fmt"
"net/http"
)
type Counter struct {
name string
value int
}
func New(name string) Counter {
c := Counter{
name: name,
value: 0,
}
http.HandleFunc("/", c.serve)
return c
}
func (c *Counter) inc() { c.value++ }
func (c *Counter) reset() { c.value = 0 }
func (c *Counter) nameApp(n string) { c.name += n }
func (c *Counter) print() { fmt.Printf("[%s]: %d (%p)\n", c.name, c.value, &c) }
func (c *Counter) Reinit(name string, value int) {
c.name = name
c.value = value
}
func (c *Counter) serve(w http.ResponseWriter, req *http.Request) {
c.inc()
c.nameApp("-foo")
fmt.Println("Counter served:")
c.print()
w.WriteHeader(http.StatusOK)
w.Write([]byte{})
}
func main() {
c := New("My New Counter")
fmt.Println("New Counter:")
c.print()
c.Reinit("My reinit Counter", 10)
fmt.Println("Counter after Reinit() call:")
c.print()
http.ListenAndServe("localhost:9000", nil)
}
When running it it creates the following output:
New Counter:
[My New Counter]: 0 (0xc00012a2a0)
Counter after Reinit() call:
[My reinit Counter]: 10 (0xc00012a2a0)
After sending two requests to the server the output is:
Counter served:
[My New Counter-foo]: 1 (0xc00012a2c0) // expected "[My reinit Counter]: 11 (0xc00012a2a0)"
Counter served:
[My New Counter-foo-foo]: 2 (0xc00012a2c0) // expected "[My reinit Counter]: 12 (0xc00012a2a0)"
Why does the struct not behave as I had expected, even though I am properly using pointer receivers?
How can I modify my struct from a main routine or really any other routine to and have these changes being reflected in the associated http request handlers?
答案1
得分: 2
在
func (c *Counter) print()
中,你正在打印
fmt.Printf("[%s]: %d (%p)\n", c.name, c.value, &c)
值得注意的是,你通过%p
打印了&c
。
c
是该方法的指针接收器参数。&c
是指向指针接收器的指针。换句话说,&c
是指向局部变量的指针,即接收器变量。如果你只想打印调用该方法的Counter
的地址,请使用c
。例如:
fmt.Printf("[%s]: %d (%p)\n", c.name, c.value, c)
英文:
In
func (c *Counter) print()
You are printing
fmt.Printf("[%s]: %d (%p)\n", c.name, c.value, &c)
Most notably, you are printing &c
via %p
.
c
is the pointer receiver argument to the method. &c
is a pointer to the pointer receiver. In other words, &c
is a pointer to a local variable, the receiver variable. If you want to print just the address of the Counter
which was called on, use the plain c
. For example:
fmt.Printf("[%s]: %d (%p)\n", c.name, c.value, c)
答案2
得分: 1
在撰写问题时找到了解决方案:
新函数的返回值需要是一个指针,否则它将返回一个副本。以下更改解决了这个问题:
func New(name string) *Counter { // 添加 *
// ... 之前的代码
return &c // 添加 &
}
(但是,我真的不明白的是,为什么初始输出中的地址保持不变!?)
英文:
Found the solution while writing up the question:
The return value of the new function needs to be a pointer, otherwise it will return a copy. The following change solves the problem:
func New(name string) *Counter { // add the *
// ... as before
return &c // add the &
}
(What I don't really get, though, is why the address in initial output stays the same!?)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论