在初始化期间建立Go中的常数

在初始化期间建立Go中的常数,go,Go,在我的Go程序中,有一些配置值我希望在程序执行期间保持不变,但我希望能够在部署站点进行更改。据我所知,使用const关键字无法实现这一点,因为(同样,据我所知)它的值必须是编译时指定的常量。这意味着实现我想要的唯一方法是声明普通变量并在包的init函数中初始化它们。这并不是说这不起作用,而是说现在没有什么可以阻止这些伪常量的值发生变化 我的两个问题是: 我是否遗漏了const的工作原理 假设我不是,处理这个问题的首选方法是什么?一个公共函数,它返回一个我从未公开、从未更改过的私有变量?只是希望人

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

我的两个问题是:

  • 我是否遗漏了
    const
    的工作原理
  • 假设我不是,处理这个问题的首选方法是什么?一个公共函数,它返回一个我从未公开、从未更改过的私有变量?只是希望人们不要改变变量,因为它们实际上是配置设置

  • 下面的代码与@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) {
            //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
    }
    

    您可以使用加载和保存配置文件

    这是一个非常好的问题,因为它深入研究了我怀疑可能是Go-immutable状态的遗漏

    从中可以看出,“常量表达式只能包含常量操作数,并在编译时求值。”

    你不能使变量恒定,这是一种耻辱。Joe的回答建议将封装作为一种解决方案,这将很好地工作——但它冗长、乏味,并且可能会引入错误

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

    因此,在我看来,在Go的众多优势中,这是Go的缺点之一。支持val和VAR可能并不难,区别在于编译器检查每个val是否只分配了一次


    在添加该功能之前,封装是您唯一的选择。

    您可以执行以下操作:

    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
    
    这样,您就可以在编译时定义一个常量。

    创建一个文件“config.go”,并创建要公开的变量

    不要导出它们(将它们全部小写)。而是创建公共(大写)
    func
    s,以访问每个项目

    package config
    
    var x = 10
    
    func X() int {
       return x
    }
    
    当需要这些变量时,只需导入./config,并在代码中使用它们,如下所示:

    if config.X()
    

    显然,您可以在包
    init

    中设置变量,只需使用standard with。标准go标志允许通过传递命令行标志在程序启动时设置任意配置变量,而INIFALGS“神奇地”添加了对从ini文件读取配置变量的支持。

    您可以创建包含私有变量的类型(可从模块的init内部设置,但不能从外部设置)。然后将访问器funcs添加到类型中。这并不完全相同,但它是这样工作的。更好的方法是:创建一个文件“config.go”,创建要公开的变量,但不要导出它们(都是小写)。然后创建public(大写)
    func
    s,用于访问每个项目。不需要类型,只需要一个模块。如果你回答@ChristopherPfohl,我会选择它。注意Go字符串在内部是不可变的。然而,保存字符串的变量本身是可变的,即使它保存的字符串是不可变的。这里有一个讨论:因为Go有指针,检查单个赋值不是不可能吗?不,不可能。检查单个赋值(注意,线索在名称中)和不变性之间有很大区别。像Scala这样的语言提供了这两种功能。你可以有一个而没有另一个,但两者兼顾似乎更有用。在我提到的论坛讨论中,有人提出了一个“不可变结构”,一旦完全初始化,该结构将变得不可变。这是个好主意。我认为单一赋值(“val”)也是一个很好的补充。它强调了Go+1中缺少的函数编程的一部分。这是一个有趣的想法。然而,除了有限的范围之外,没有什么可以阻止“a”被更改。所以它实际上不是一个常量,更多的是一个配置参数。虽然,由于straight Go缺少编译时参数,这在其他上下文中仍然有用。很好的调用,我最近在其他地方看到过这个成语,不记得在哪里了