
本文介绍一种符合 Go 语言设计规范的方案:通过创建独立的配置包,在 main.go 初始化环境变量后,供 util.go 等其他文件安全复用,避免“program not importable package”错误。
本文介绍一种符合 go 语言设计规范的方案:通过创建独立的配置包,在 main.go 初始化环境变量后,供 util.go 等其他文件安全复用,避免“program not importable package”错误。
在 Go 中,main 包是可执行程序的入口,不能被其他包导入——这正是你遇到 import github.com/my_project/my_api is a program, not an importable package 错误的根本原因。直接从 main.go 导出变量或调用其 init() 函数在语义和编译层面均不可行。正确的解法是将配置逻辑抽离为一个可复用、可导入的独立包。
✅ 推荐实践:创建 config 包统一管理初始化配置
首先,在项目根目录下新建 config/ 目录,并创建 config/config.go:
// config/config.go
package config
import (
"net/url"
"os"
)
var Data url.Values // 导出变量,供其他包使用
func init() {
Data = url.Values{}
Data.Add("client_id", os.Getenv("CLIENTID"))
Data.Add("client_secret", os.Getenv("CLIENTSECRET"))
}
? 注意:
Data是包级变量(首字母大写),在init()中完成初始化,确保任何导入config包的代码都能安全访问已初始化的数据。
接着,调整你的目录结构为:
my_project/
├── go.mod
├── main.go // 原 my_api/main.go → 移至项目根目录(推荐)
├── config/
│ └── config.go
└── util/
└── util.go // 原 util_dir/util.go → 重命名为更规范的 util/
然后修改 main.go,仅保留程序入口逻辑(移除冗余 init):
// main.go
package main
import (
"log"
"my_project/config" // ✅ 可正常导入
"my_project/util"
)
func main() {
// config.init() 已自动执行,Data 已就绪
log.Printf("Client ID loaded: %s", config.Data.Get("client_id"))
// 调用工具函数
util.Validation()
}
最后更新 util/util.go,直接使用 config.Data:
// util/util.go
package util
import (
"bytes"
"fmt"
"io"
"net/http"
"my_project/config" // ✅ 正确导入配置包
)
func Validation() {
// 复用并扩展 config.Data
config.Data.Add("scope", "xyzid")
config.Data.Add("another_secret", "SECRET")
urlStr := "https://api.example.com/token"
client := &http.Client{}
req, err := http.NewRequest("POST", urlStr, bytes.NewBufferString(config.Data.Encode()))
if err != nil {
fmt.Printf("Failed to create request: %v\n", err)
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("HTTP request failed: %v\n", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Response: %s\n", body)
}
⚠️ 重要注意事项
-
init()的执行时机:Go 保证在main()运行前,所有导入包的init()函数按依赖顺序执行完毕,因此config.Data在util.Validation()中始终可用。 -
避免并发写入风险:
url.Values非线程安全。若需在多 goroutine 中动态修改,请加锁(如sync.RWMutex)或改用初始化后只读模式(推荐)。 -
环境变量校验建议:生产环境中应在
config.init()中添加非空校验与 panic 提示,例如:if config.Data.Get("client_id") == "" { panic("missing required env var: CLIENTID") } -
模块路径一致性:确保
go.mod中的 module 名与导入路径完全匹配(如module my_project),否则导入会失败。
该方案清晰分离关注点:config 负责加载与暴露配置,main 负责启动流程,util 负责业务逻辑复用——符合 Go 的工程化最佳实践,也彻底规避了“main 包不可导入”的限制。











