在Go语言中,在init函数期间设定的常量。

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

Constants in Go established during init

问题

在我的Go程序中,有一些配置值,我希望它们在程序执行期间保持不变,但我希望能够在部署现场进行更改。据我所知,使用const关键字无法实现这一点,因为(再次,据我所知)它的值必须是在编译时指定的常量。这意味着实现我想要的唯一方法是声明普通变量,并在包的init函数中进行初始化。并不是说这种方法行不通,而是现在没有任何东西可以阻止这些伪常量的值发生变化。

我的两个问题是:

  1. 我对const的工作原理有什么误解吗?
  2. 假设我没有误解,处理这个问题的首选方法是什么?一个返回私有变量的公共函数,我从不公开它,永远不改变它?只是希望人们不要更改这些变量,因为它们实际上是配置设置?
英文:

In my Go program, there are configuration values that I want to be constant for the duration of program execution, but that I want to be able to change at the deployment site. As far as I can tell, there's no way to achieve this with the const keyword, since (again, as far as I can tell) its value must be a constant specified at compile time. This means that the only way to achieve what I want would be to declare normal variables and initialize them during the package's init function. It's not that that won't work, but rather that there will now be nothing to prevent these pseudo-constant's values from changing.

My two questions are:

  1. Am I missing something about how const works?
  2. Assuming I'm not, what's the preferred way to handle this? A public function that returns a private variable that I never expose, never changing it? Just hoping people don't alter the variables, since they're really configuration settings?

答案1

得分: 6

创建一个名为"config.go"的文件,并创建你想要公开的变量。

不要导出它们(将它们都设为小写)。相反,创建公开(大写)的func来访问每个变量。

package config

var x = 10

func X() int {
   return x
}

当你想要使用这些变量时,只需import ./config并在代码中使用它们,如下所示:

if config.X()

显然,你可以在包的init中设置这些变量。

英文:

Create a file "config.go" and create the vars you want to expose.

Don't export them (make them all lower case). Instead create public (upper case) funcs that give access to each item.

package config

var x = 10

func X() int {
   return x
}

When you want those variables you simply import ./config and use them in code as follows:

if config.X()

Obviously, you can set the variables in the package init.

答案2

得分: 1

以下代码与@Christopher的第二种方法几乎相同,只是它不是一个模块,而是位于主包中。

package main

import (
    "os"
)

type Config struct {
    debug                bool
    key                  string
    proxyNumber          int
}

func (c *Config) Debug() bool {
    return c.debug
}
func (c *Config) Key() string {
    return c.key
}
func (c *Config) ProxyNumber() int {
    return c.proxyNumber
}

const (
    CONFIG_NAME = "config.ini"
)

var config *Config

func init() {
    DefaultConfig()
    if Exists(CONFIG_NAME) {
        //尝试保存配置文件
    }else {
        //尝试从配置文件加载
    }
}

func DefaultConfig() {
    config = &Config{debug:true, key:"abcde",
        proxyNumber:5,
    }
}

//Exist: 检查文件是否存在
func Exists(path string) bool {
    _, err := os.Stat(path)
    if err == nil { return true }
    if os.IsNotExist(err) { return false }
    return false
}

你可以使用config来加载和保存配置文件。

英文:

The following code is almost the same as the second method of @Christopher, except that it is not a module, it located in the main package.

package main

import (
	"os"
)

type Config struct {
	debug                bool
	key                  string
	proxyNumber          int
}

func (c *Config) Debug() bool {
	return c.debug
}
func (c *Config) Key() string {
	return c.key
}
func (c *Config) ProxyNumber() int {
	return c.proxyNumber
}

const (
	CONFIG_NAME = "config.ini"
)

var config *Config

func init() {
	DefaultConfig()
	if Exists(CONFIG_NAME) {
		//try to save the config file
	}else {
		//try to load from the config file

	}
}

func DefaultConfig() {
	config = &Config{debug:true, key:"abcde",
		proxyNumber:5,
	}
}

//Exist: check the file exist
func Exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil { return true }
	if os.IsNotExist(err) { return false }
	return false
}

you can use config to load and save the config file.

答案3

得分: 1

这是一个非常好的问题,因为它涉及到我怀疑Go可能存在的一个遗漏 - 不可变状态。

根据语言参考,"常量表达式只能包含常量操作数,并在编译时进行评估。"

你不能将变量声明为常量,这是一种遗憾。Joe的答案提出了封装作为解决方案,这将起到很好的作用 - 但它冗长、繁琐,并且可能引入错误。

相比之下,许多非纯函数式语言将可变变量与单赋值的不可变值结合使用。例如,Scala有关键字'val'和'var';Scala的'var'的含义与Go的'var'非常相似。不可变性是工具箱中的一个有用工具,因为可以编写引用透明、无副作用的函数,以及有状态的可变过程化代码。两者都有其适用场景。不可变性对于并发性也是一个有价值的工具,因为如果不可变值在goroutine之间共享,就不必担心可能的竞态条件。

所以在我看来,尽管Go有许多优点,但这是它的一个缺点。支持'val'和'var'应该不难,区别在于编译器检查每个'val'是否只被赋值一次。

在添加该功能之前,你只能使用封装作为唯一的选择。

英文:

This is a very good question because it delves into what I suspect may be an omission from Go - immutable state.

From the language reference, "constant expressions may contain only constant operands and are evaluated at compile-time."

You cannot make vars constant, which is a shame. Joe's answer proposes encapsulation as a solution, which will work well - but it's verbose, tedious and might introduce errors.

By comparison, many impure functional languages combine mutable variables with single-assignment immutable values. For example, Scala has the keywords 'val' and 'var'; the meaning of Scala's 'var' is quite similar to Go's 'var'. Immutability is a useful tool in the toolbox because referentially-transparent side-effect-free functions can be written, alongside stateful mutable procedural code. Both have their place. Immutability is also an valuable tool for concurrency because there is no worry about possible race conditions if immutable values are shared between goroutines.

So in my opinion, amongst its many strengths, this is one of Go's shortcomings. It would presumably not be hard to support vals as well as vars, the difference being that the compiler checks that each val is assigned exactly once.

Until that feature is added, you have encapsulation as your only option.

答案4

得分: 0

你可以这样做:

package main

import (
    "fmt"
    "strconv"
)

var a string

func main() {
    myvar, err := strconv.Atoi(a)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(myvar)
}

然后使用以下命令编译程序:

go build -ldflags '-X main.a 10' test.go

这样你就可以在编译时定义一个常量。

英文:

You can do something like this:

package main

import (
	"fmt"
	"strconv"
)

var a string

func main() {
	myvar, err := strconv.Atoi(a)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(myvar)
}

and compile the program with

go build -ldflags '-X main.a 10' test.go

That way you can define a constant during compile time.

答案5

得分: 0

只需使用标准的go flagsiniflags。标准的go flags允许通过传递命令行标志来设置任意配置变量,而iniflags则“神奇地”添加了从ini文件中读取配置变量的支持。

英文:

Just use standard go flags with iniflags. Standard go flags allow setting arbitrary config variables at program start via passing command-line flags, while iniflags "magically" add support for reading config variables from ini files.

答案6

得分: 0

你可以将变量包装在一个返回其值的函数中:

func genConst(x int) func() int {
	return func() int {
		return x
	}
}

var Constx = genConst(5)
var x1 = Constx()

这样,即使在定义它的文件中,该值也不会被意外更改。

英文:

You can wrap the variable in a function that returns its value:

func genConst(x int) func() int {
	return func() int {
		return x
	}
}

var Constx = genConst(5)
var x1 = Constx()

This way the value cannot be changed by accident, even in the file where it's defined

huangapple
  • 本文由 发表于 2013年2月16日 02:46:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/14901347.html
匿名

发表评论

匿名网友

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

确定