Published on

GO返回参数结构体是否该用指针

Authors
  • avatar
    Name
    yushenw
    Linkedin

在 Go 语言中,决定一个方法是否应该返回一个结构体的指针(如 *pb.GreetResponse)或是直接返回结构体(如 pb.GreetResponse)通常取决于几个因素,包括性能、内存使用和函数的预期用途。让我们来看看这两种方法的应用场景:

返回 pb.GreetResponse(结构体实例):

  • 当返回的结构体是小的或者中等大小时,直接返回结构体可能更高效,因为它减少了内存分配(避免了在堆上创建对象)和垃圾回收的开销。
  • 如果函数的调用者通常不需要修改返回的结构体,或者你想确保返回的数据不会被外部修改(因为 Go 中结构体是按值传递的),那么返回结构体实例会更合适。 在结构体内容比较小的情况下,按值返回可以减少解引用的需要,从而提高访问速度。

返回 *pb.GreetResponse(结构体指针):

  • 当结构体较大时,返回指针更高效,因为它减少了数据复制的开销。这是因为返回指针只需要复制内存地址,而不是整个结构体。
  • 如果函数的调用者可能需要修改返回的结构体,那么返回指针会更合适,因为结构体指针允许调用者直接修改原始结构体。
  • 当涉及到接口实现或多态时,通常使用指针,因为结构体可能需要实现某些接口,而接口方法通常是通过指针接收者来定义的。
  • 在使用指针时,你还可以返回 nil 来表示某些特殊情况,比如错误或“无值”情况,这在返回结构体实例时无法做到。

如果 pb.GreetResponse 是一个较大的结构体或者你希望调用者能够修改返回的数据,或者你的方法需要实现某个接口,那么返回 *pb.GreetResponse 可能更合适。如果这个结构体相对较小,并且你想要保证返回的数据不会被修改,那么返回 pb.GreetResponse 可能更好。

比如grpc生成的pb.go中,因为GreetResponse使用指针接受者方法,所以返回结构体指针更合适。

// 指针方法
func (x *GreetResponse) Reset() {
	*x = GreetResponse{}
	if protoimpl.UnsafeEnabled {
		mi := &file_greeter_proto_msgTypes[1]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

Server处实现Hello

func (g *GreeterService) Hello(ctx context.Context, in *pb.GreetRequest) (*pb.GreetResponse, error) {
	log.Printf("Name: %s", in.GetName())
	return &pb.GreetResponse{Message: "Hello: " + in.GetName()}, nil
}