Golang中无法访问主函数中声明的全局变量

Golang中无法访问主函数中声明的全局变量 我正在编写一个使用 spf13/cobra 进行命令行处理的应用程序。请原谅我是新手,这是我学习 Go 语言的第四周。我见过其他类似的帖子,但不知为何对我无效。我肯定哪里做错了。

我的目录结构如下:

myapp/cmd/myapp
    main.go
    types.go
    <其他文件>
myapp/cmd/myapp/cli
    root.go
    <其他各种命令文件>

我的伪代码:

// main.go:
package main

import myapp/cmd/myapp/cli

var  Cfg *MyStruct

func main() {
    Cfg = &MyStruct{}
}

// cli/root.go:
package cli

func init() {
    rootCmd.PersistentFlags().StringVarP(
        &Cfg.Options.Endpoint,
        "blah",
        "",
        "",
        "")
}

报错提示 Cfg 未定义。

感谢任何帮助。


更多关于Golang中无法访问主函数中声明的全局变量的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

那么你需要重新设计并将该变量移至另一个包中。

更多关于Golang中无法访问主函数中声明的全局变量的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


但这在 main 函数中,无法被导入。

我最终通过结合使用结构体的构造函数包装器和一个配置包来实现类似的功能。

感谢您的帮助。

那么当某个地方首次导入包时,会调用 cli.init() 吗?

另外还有什么其他方式可以初始化 cli?我以为这就是 init() 的作用?

所有导入包的初始化函数都会在 main 函数之前被调用。你可以创建一个名为 setup、initialize 或 config 等函数,并在 main 函数中调用它。

创建一个名为"config"的包,其中包含导出的变量Cfg。然后将"config"包导入到"main"包中,并为’config.Cfg’设置值。再将"config"包导入到"cli"包中,并使用’config.Cfg’的值。

如果变量在main包中,应该通过main.Cfg来访问,但是cli的init例程在main()之前运行,此时该变量将为nil。你可以不使用init函数,而是创建一个用于初始化cli的函数,并将Cfg作为参数传递给该函数。

你需要包含这个包然后使用它。

如果你的包名为 example.com/foo,并且其中有一个变量 Bar,你可以在导入该包后使用 foo.Bar 来访问它。类型和函数的使用方式类似。

func main() {
    fmt.Println("hello world")
}

好吧,我有点困惑。我尝试了所有可能的方法来创建子目录、新包、init()函数、setup()函数,但每次都会遇到障碍。

如果我创建一个名为"setup"的子目录,里面包含setup.go文件,然后尝试返回我的MyStruct类型,但它在setup包中未定义…如果我修复了这个问题,问题又出现在其他地方。

我到底需要做什么才能让一个结构体变量在我的编译二进制文件中对所有内容可见,无论它位于哪个包或子目录中?

天啊,几乎都想回去写C语言了。

在你的代码中,Cfg 变量在 main 包中声明,但在 cli 包中无法直接访问,因为 Go 语言中包级别的变量作用域仅限于其所在的包。要解决这个问题,你需要通过包导出或传递引用的方式来共享 Cfg 变量。以下是两种常见的解决方案:

方案一:将 Cfg 变量导出并在 cli 包中导入

首先,在 main.go 中,将 Cfg 变量定义为导出变量(首字母大写),这样其他包可以访问它。然后,在 cli 包中导入 main 包并使用该变量。

  1. 修改 main.go,确保 Cfg 是导出的:
// main.go
package main

import "myapp/cmd/myapp/cli"

var Cfg *MyStruct // 注意:变量名首字母大写,表示导出

func main() {
    Cfg = &MyStruct{}
    // 初始化 cli 命令,如果需要,可以传递 Cfg 引用
    cli.Execute()
}
  1. cli/root.go 中,导入 main 包并使用 main.Cfg
// cli/root.go
package cli

import "myapp/cmd/myapp" // 导入 main 包

func init() {
    rootCmd.PersistentFlags().StringVarP(
        &main.Cfg.Options.Endpoint, // 通过 main.Cfg 访问变量
        "blah",
        "",
        "",
        "")
}

注意:这种方法要求 MyStruct 和其字段(如 Options)也是导出的(即首字母大写),否则在 cli 包中无法访问。例如,在 types.go 中:

// types.go
package main

type MyStruct struct {
    Options struct {
        Endpoint string
    }
}

方案二:通过函数参数传递 Cfg 引用

更推荐的方式是通过函数参数将 Cfg 的引用传递给 cli 包,这样可以避免全局变量的直接暴露,提高代码的模块化。

  1. main.go 中,修改为传递 Cfgcli 包:
// main.go
package main

import "myapp/cmd/myapp/cli"

var Cfg *MyStruct

func main() {
    Cfg = &MyStruct{}
    cli.SetConfig(Cfg) // 调用 cli 包的函数设置配置
    cli.Execute()
}
  1. cli 包中,定义一个函数来接收 Cfg 引用,并在内部使用它:
// cli/root.go
package cli

var cfg *main.MyStruct // 在 cli 包内部定义变量来存储引用

func SetConfig(c *main.MyStruct) {
    cfg = c
}

func init() {
    rootCmd.PersistentFlags().StringVarP(
        &cfg.Options.Endpoint, // 使用内部存储的引用
        "blah",
        "",
        "",
        "")
}

确保在 cli 包中导入 main 包,并且 MyStruct 是导出的(在 types.go 中定义时首字母大写)。

总结

问题根源在于 Go 的包作用域规则:每个包的变量默认是私有的。通过导出变量或传递引用,你可以跨包共享数据。在 cobra 应用中,方案二更常见,因为它减少了全局状态的依赖。如果问题持续,检查你的导入路径和结构体字段的导出性。

回到顶部