Go: Assign multiple return value function to new and old variable

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

Go: Assign multiple return value function to new and old variable

问题

在Go语言中,有些函数会返回两个或更多个值,通常其中一个是错误值。假设我想将第一个返回值存储到已经初始化的变量中,但我希望在初始化变量时包含错误值。有没有一种方法可以做到这一点?

例如,假设我有以下代码:

var a int
// 这段代码无法编译,因为err变量不存在
a, err = SomeFuncWithTwoReturnValues()
// 这段代码也无法编译
a, err := SomeFuncWithTwoReturnValues()

我知道你可以这样做,但我希望能够在一行内完成初始化:

var a int
var err error
a, err = SomeFuncWithTwoReturnValues()

或者

a, err := SomeFuncWithTwoReturnValues()

编辑:上面的代码实际上可以编译,所以我回顾了一下我的代码,进一步分析并创建了一个能够复现问题的简单示例(不仅仅是在我的想象中...)。

package main

func myfunc() (int, int) {
	return 1, 1
}

func main() {
	a := make([]int, 1)
	a[0], b := myfunc()
	a[0] = b
}

编译器报错:main.go|9| non-name a[0] on left side of :=。如果我将其改为=而不是:=,那么b将永远不会被创建。我有一种感觉,可能没有一种简洁的方式来实现这个需求。

英文:

In go there are functions which return two values or more values, commonly one is an error. Suppose that I want to store the first return value into an already initialized variable, but I would like to initialize the variable to contain the error inline. Is there a way to do this?

For example, say I had this code

var a int
//This code doesn't compile because err doesn't exist
a, err = SomeFuncWithTwoReturnValues()
//This code doesn't compile either
a, err := SomeFuncWithTwoReturnValues()

I know you could do this, but I was hoping there was a way to do it all inline

var a int
var err error
a, err = SomeFuncWithTwoReturnValues()

or

a, err := SomeFuncWithTwoReturnValues()

EDIT: The code above actually compiles, so I looked back at my code to drill down more and have created a quick sample that actually replicates the problem (not just in my mind...).

package main

func myfunc() (int, int) {
	return 1, 1
}

func main() {
	a := make([]int, 1)
	a[0], b := myfunc()
	a[0] = b
}

Compiler says main.go|9| non-name a[0] on left side of :=. If I make it = instead of := though then b is never created. I get the feeling that there is not shorthand way to do it though.

答案1

得分: 9

如您在评论中提到的那样,您需要使用=运算符来为已经声明的变量赋值。:=运算符用于同时声明和赋值变量。这两者是相同的:

var x int
x = 5
//与下面的代码等价
x := 5

这个解决方案至少可以编译通过:

package main

func myfunc() (int, int) {
    return 1, 1
}

func main() {
    var b int
    a := make([]int, 1)
    a[0], b = myfunc()
    a[0] = b
}

回答您的问题,我认为在返回多个值时,没有办法同时使用未声明的变量和已声明的变量。这将尝试同时使用两个不同的运算符。

编辑:刚刚看到您的代码示例可以编译通过,所以看起来您已经熟悉了Go的赋值运算符。无论如何,我还是会保留这个示例。

英文:

As you've mentioned in the comments, you'll need to use the = operator in order to assign to a variable you've already declared. The := operator is used to simultaneously declare and assign a variable. The two are the same:

var x int
x = 5
//is the same as
x := 5

This solution will at least compile:

package main

func myfunc() (int, int) {
    return 1, 1
}

func main() {
    var b int
    a := make([]int, 1)
    a[0], b = myfunc()
    a[0] = b
}

To answer your question, I don't think there is a way to simultaneously use an undeclared and a declared variable when returning multiple values. That would be trying to use two different operators simultaneously.

Edit: just saw your example from the code that compiles, so it appears you're already familiar with go's assignment operators. I'll leave the example up anyway.

答案2

得分: 6

Golang不是一种非常一致的语言。这是一个很好的例子。一开始我感到困惑,如果他们总是允许使用:=运算符,会简单得多。编译器足够聪明,可以检测到已经声明的变量:

package main

import "fmt"

func testFunc() (int,error) {
   return 42,fmt.Errorf("Test Error")
}

func main() {
   number1,err := testFunc() // OK
   number2,err := testFunc() // OK,即使err已经定义
   number1,err = testFunc()  // OK
   // number1,err := testFunc() // ERROR: :=左侧没有新变量

   fmt.Println(number1,number2,err)
}

Playground链接:https://play.golang.org/p/eZVB-kG6RtX

这不一致,因为Golang允许您在引入新变量的同时,对已经声明的变量使用:=。因此,编译器可以检测到变量已经存在并跳过它们的声明。但是Golang的开发人员决定只有在引入至少一个新值时才允许这样做。最后一个示例就是这样的。

英文:

Golang is not a very consistent language. This is a good example. At the beginning I was confused and it would be much simpler if they would always allow the := operator. The compiler is smart enough to detect already declared variables:

package main

import "fmt"

func testFunc() (int,error) {
   return 42,fmt.Errorf("Test Error")
}

func main() {
   number1,err := testFunc() // OK
   number2,err := testFunc() // OK, even if err is already defined
   number1,err = testFunc()  // OK
   // number1,err := testFunc() // ERROR: no new variables on left side of :=

   fmt.Println(number1,number2,err)
}

Playground Link: https://play.golang.org/p/eZVB-kG6RtX

It's not consistent, because golang allows you to use := for already declared variables if you assign to them while also introducing a new variable. So the compiler can detect that variables already exists and skip their declaration. But the golang developers decided to allow that only if you introduce at least one new value. The last example shows that.

答案3

得分: 1

我遇到了这样的情况:

package main
import "os"

func main() {
   var cache struct { dir string }
   // undefined: err
   cache.dir, err = os.UserCacheDir()
   // non-name cache.dir on left side of :=
   cache.dir, err := os.UserCacheDir()
   if err != nil {
      panic(err)
   }
   println(cache.dir)
}

正如你发现的那样,这个问题没有一个干净的解决方案。你可以声明一个额外的变量:

dir, err := os.UserCacheDir()
if err != nil {
   panic(err)
}
cache := userCache{dir}

或者,虽然更冗长,你可以事先声明错误。这可以节省内存,因为Go不使用Rust的所有权模型:

var (
   cache struct { dir string }
   err error
)
cache.dir, err = os.UserCacheDir()
英文:

I ran into this situation like this:

package main
import "os"

func main() {
   var cache struct { dir string }
   // undefined: err
   cache.dir, err = os.UserCacheDir()
   // non-name cache.dir on left side of :=
   cache.dir, err := os.UserCacheDir()
   if err != nil {
      panic(err)
   }
   println(cache.dir)
}

as you discovered, this issue does not have a clean solution. You can declare
an extra variable:

dir, err := os.UserCacheDir()
if err != nil {
   panic(err)
}
cache := userCache{dir}

Or, while more verbose, you can declare the error beforehand. This can save
memory, as Go does not use a Rust ownership model:

var (
   cache struct { dir string }
   err error
)
cache.dir, err = os.UserCacheDir()

答案4

得分: 0

根据规范中提到的,使用:=时,如果其中一个变量是new,那么旧的变量将被赋予新的数据。

与常规变量声明不同,短变量声明可以重新声明变量,前提是它们在同一代码块中(或者如果代码块是函数体,则在参数列表中)以相同的类型进行了初始声明,并且至少有一个非空白变量是新的。因此,重新声明只能出现在多变量的短变量声明中。重新声明不会引入新变量;它只是将新值赋给原始变量。

field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset)  // 重新声明了offset
英文:

As mention in the spec, while using:=, if one of the variables is new, then the old one will just be assigned with the new data.

> Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original.

field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset)  // redeclares offset

答案5

得分: 0

如其他答案所提到的,你不能在同一个返回语句中同时使用赋值和声明。你必须选择其中一种方式。

然而,我猜你提出这个问题的主要原因是为了简化代码,这样你就不必在方法或函数语句上面声明额外的err变量。

你可以通过两种方式解决这个问题:

  1. 声明一个全局的var err error变量,并在赋值中使用它:
var err error

func MyFunc(someInput string) {
  var a int
  a, err = someOtherFunction()
}
  1. 如果你的方法或函数返回一个错误,你可以使用已声明的返回变量:
func MyFunc(someInput string) (err error) {
  var a int
  a, err = someOtherFunction()
  return
}

我主要在方法中遇到这个问题,当我想要将某些东西赋值给结构体成员时,例如:

type MyStruct struct {
  so string
}

func (m *MyStruct) SomeMethod() (err error) {
  m.so, err = SomeFunction()
  // 处理错误并继续或返回错误
  return
}
英文:

As mentioned by the other answers you cannot use assignment and declaration in the same return statement. You have to use either.

However I guess the main reason for your question is cleaning up the code so you don't have to declare an extra err variable above the method or function statement.

You can solve this in two ways:

  1. Declare a global var err error variable and use it in the assignment:
var err error

func MyFunc(someInput string) {
  var a int
  a, err = someOtherFunction()
}
  1. If your method or function returns an error you can use the declared return variable
func MyFunc(someInput string) (err error) {
  var a int
  a, err = someOtherFunction()
  return
}

I mainly have the problem in methods when I want to assign something to a struct member, e.g.:

type MyStruct struct {
  so string
}

func (m *MyStruct) SomeMethod() (err error) {
  m.so, err = SomeFunction()
  // handle error and continue or return it
  return
}

huangapple
  • 本文由 发表于 2014年8月1日 23:08:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/25083235.html
匿名

发表评论

匿名网友

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

确定