表达式的求值顺序

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

Order of evaluation of expressions

问题

我对一些不同类型表达式的顺序很好奇,所以我在顶层声明级别尝试了这段代码,本以为会失败,但发现它可以工作:

http://play.golang.org/p/CfP3DEC5LP

var x = func() *Foo {
	fmt.Println(f) // 打印 &{foobar}
	return f
}()

var f = &Foo{"foobar"}

type Foo struct {
	bar string
}

请注意:

  • type Foo struct 的声明在底部

  • type 声明之前有一个 var f 声明和 &Foo{} 赋值

  • var 声明之前,有一个立即调用的函数,该函数引用并返回 f 变量。

虽然我对于在 type Foo struct 声明之前就能创建 &Foo{} 的值并不感到太惊讶,但我对于在其赋值之前就能成功引用和打印 f 的值感到惊讶。

这是一种可靠且规定的行为吗?我在规范中找不到任何关于这种顺序的参考,但也许我忽略了它。

英文:

I was curious about the order in which some various types of expressions, so I tried this code in the top declaration level, thinking it would fail, but found that it works:

http://play.golang.org/p/CfP3DEC5LP

var x = func() *Foo {
	fmt.Println(f) // prints &{foobar}
	return f
}()

var f = &Foo{"foobar"}

type Foo struct {
	bar string
}

Please note:

  • the type Foo struct declaration is at the bottom

  • before the type declaration there's a var f declaration and &Foo{] assignment

  • before the var declaration, there's a function that's invoked immediately, which references and returns the f variable.

While it didn't surprise me too much that I could make a &Foo{} value even though it took place before the type Foo struct declaration, it did surprise me that I could successfully reference and print the f value before its assignment.

Is this a reliable and specified behavior? I couldn't find any reference to such an ordering in the specification, but perhaps I overlooked it.

答案1

得分: 10

请参阅Go编程语言参考

> 在一个包内,包级变量的初始化和常量值的确定是根据引用顺序进行的:如果A的初始化器依赖于B,那么B将在A之前设置。依赖分析不依赖于被初始化项的实际值,只依赖于它们在源代码中的出现。如果A的值包含对B的提及,包含一个初始化器提及B的值,或者提及一个提及B的函数(递归地),那么A依赖于B。如果这样的依赖形成一个循环,那么就会出现错误。如果两个项之间没有相互依赖,它们将按照它们在源代码中的出现顺序进行初始化,可能在多个文件中,按照提交给编译器的顺序。由于依赖分析是按包进行的,如果A的初始化器调用在另一个包中定义的引用B的函数,它可能会产生未指定的结果。

规范的更新版本(编辑时的Go 1.20版本):

> 在一个包内,包级变量的初始化是逐步进行的,每一步都选择声明顺序中最早没有依赖于未初始化变量的变量进行。更准确地说,如果一个包级变量尚未初始化,并且没有初始化表达式,或者其初始化表达式不依赖于未初始化变量,那么它被认为是准备好进行初始化的。初始化通过重复初始化下一个在声明顺序中最早准备好进行初始化的包级变量进行,直到没有变量准备好进行初始化为止。

> 如果在此过程结束时仍有变量未初始化,则这些变量是一个或多个初始化循环的一部分,程序是无效的。

以及(同一段落)

> 依赖分析不依赖于变量的实际值,只依赖于源代码中对它们的词法引用进行传递分析。例如,如果变量x的初始化表达式引用一个函数,而该函数的主体引用变量y,则x依赖于y。

英文:

See the Go programming language reference

> Within a package, package-level variables are initialized, and
> constant values are determined, according to order of reference: if
> the initializer of A depends on B, A will be set after B. Dependency
> analysis does not depend on the actual values of the items being
> initialized, only on their appearance in the source. A depends on B if
> the value of A contains a mention of B, contains a value whose
> initializer mentions B, or mentions a function that mentions B,
> recursively. It is an error if such dependencies form a cycle. If two
> items are not interdependent, they will be initialized in the order
> they appear in the source, possibly in multiple files, as presented to
> the compiler. Since the dependency analysis is done per package, it
> can produce unspecified results if A's initializer calls a function
> defined in another package that refers to B.

Newer version of the specification (Go 1.20 at time of editing):

> Within a package, package-level variable initialization proceeds stepwise, with each step selecting the variable earliest in declaration order which has no dependencies on uninitialized variables.
>
> More precisely, a package-level variable is considered ready for initialization if it is not yet initialized and either has no initialization expression or its initialization expression has no dependencies on uninitialized variables. Initialization proceeds by repeatedly initializing the next package-level variable that is earliest in declaration order and ready for initialization, until there are no variables ready for initialization.
>
> If any variables are still uninitialized when this process ends, those variables are part of one or more initialization cycles, and the program is not valid.

and (same paragraph)

> Dependency analysis does not rely on the actual values of the variables, only on lexical references to them in the source, analyzed transitively. For instance, if a variable x's initialization expression refers to a function whose body refers to variable y then x depends on y.

huangapple
  • 本文由 发表于 2013年6月1日 03:40:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/16864693.html
匿名

发表评论

匿名网友

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

确定