Golang中如何调用另一个包里的函数

Golang中如何调用另一个包里的函数 这个问题之前已经被问过很多次了,但我只想知道为什么它不起作用的解释。

src
  |—main
     |–mail – main.go
     |–main.go

我想在 mail/main.go 中调用 main.go 里的函数

这个可以工作。为什么?:

import (
	mail "./mail"
	"fmt"
)

但为什么这个不行?

import (
	"main/mail"
	"fmt"
)

在任何路径中都找不到包 “main/mail”: /usr/local/go/src/main/mail (来自 $GOROOT) /Users/sibert/go/src/main/mail (来自 $GOPATH)

据我所知路径是正确的。

我寻求的是对其工作原理的解释,而不是解决方案。


更多关于Golang中如何调用另一个包里的函数的实战教程也可以访问 https://www.itying.com/category-94-b0.html

12 回复

Jaytn:

恐怕我无法回答你的问题,因为据我所知它不应该这样运作

如果不使用Github,应该如何操作?

更多关于Golang中如何调用另一个包里的函数的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


或许你应该使用来自 http://github.com 的路径

我目前还没有使用 GitHub。所以我在本地有这些 go 文件…

在$GOPATH/src中不允许使用相对导入,您说它可以正常工作,这意味着您可能没有使用Go工作区?请问安装的Go版本是什么?

由于某些原因,这个方法不起作用。

import (
"mymodule/mail")

但这个方法可以

import (
"../mail")

有人能给我解释一下吗?

或许你应该使用来自 http://github.com 的路径

例如:导入 “github.com/web/mail

http://github.com 创建你的用户名文件夹,并将你的项目放在该文件夹中,这样能获得更好的文件夹结构。

例如:http://github.com/your_username/project_folder

我进行了一些调查,你的情况似乎有些奇怪… 能否分享你的项目结构、go env的输出结果以及你的.go文件的开头部分?

凭直觉判断,我认为可能是你的Go工作区设置有问题,比如你提到的"src"文件夹实际上不在你的GOPATH中。这至少可以解释目前为止的所有错误信息。

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

codekid:

Go 无法找到你项目文件夹中的 mail 包, 尝试导入 项目名/mail 而不是 mailmain/mail。(将 项目名 替换为包含 main.go 的文件夹名称)

如果我理解正确的话,文件夹结构应该是这样的?

structure

使用这个

import (
	"fmt"
	"web/mail"
)

执行 go build 时出现以下错误:

在任何路径中都找不到包 “web/mail”: /usr/local/go/src/web/mail (来自 $GOROOT) /Users/sibert/go/src/web/mail (来自 $GOPATH)

我哪里做错了?

据我所知,情况完全如你所述(或你在第一篇帖子中描述的那样)。 另外,我推荐阅读这篇文章

我的意思是,我不明白为什么在你描述的情况下事情会这样运行或不运行,因为它们本不应该有这样的表现(应该是相反的情况)。所以你的环境中正在发生一些我无法识别的异常情况。

关于"不使用Github时"的情况:可能无需在意。 无论你的项目位于src/github.com/yourname/yourproject还是src/yourproject,这(对你的用例来说)完全无关紧要。 你会发现有些人建议将项目放在src/github.com/yourname/目录下,即使你只是在本地开发,但他们通常假设你将来可能会上传代码;那样你就需要修改其他项目中的所有导入语句。但如果这不适用于你,你可以放心忽略这个建议(除非我遗漏了什么)。

这会导致循环依赖问题,这是不被允许的。

调用函数的解决方法是减少与入口包(即 main)的耦合。

例如,您可以将函数移至 mail.go

mail/mail.go

package mail

func SomeFunc() {
    fmt.Println("I'm doing something")
}

main.go 中,您可以这样调用:

package main

import "mymodule/mail"

func main() {
    mail.SomeFunc()
}

这是完全可行的。


关于这部分错误信息:

无法在任何路径中找到包 “main/mail”: /usr/local/go/src/main/mail(来自 $GOROOT) /Users/sibert/go/src/main/mail(来自 $GOPATH)

Go 无法找到您项目文件夹中的 mail 包, 请尝试导入 projectName/mail 而不是 mailmain/mail(将 projectName 替换为包含 main.go 的文件夹名称)

感谢。 恐怕我无法回答您的问题,因为据我所知它不应该这样工作,而且我无法复现您的结果。

您说得对,导入路径应该是某些Go工作区中的绝对唯一标识符。 例如 import some/thing 会在所有 $GOPATH/src/some/thing 中查找 这里的"绝对"指的是非相对路径,这就是为什么您要导入 main/mail 而不仅仅是 mail(或者 ./mail),即使它是当前包的子包。

可以使用相对路径进行导入,但据我所知,这仅适用于不在Go工作区中的项目。如果您在工作区中使用相对路径,会抛出错误 local import "./mail" in non-local package。(参见此处

这就是为什么我猜测您的文件不在GOPATH中,那么Go将无法找到它(因此出现 cannot find package 错误),但会允许使用相对路径(不会出现 local import in non-local package 错误)。但从您提供的信息来看,一切似乎都没问题。 很抱歉我无法提供更多帮助。

//顺便提一下: 您可以在本地使用远程路径(例如 github.com),即使它们实际上并未托管在那里。它们的特殊之处仅在于告诉 go get 如何在本地不可用时获取它们。编译器不会关心所有这些,只会使用本地版本(如果不存在则失败)。但这对您的原始问题没有帮助。

Jaytn:

我做了一些调查,你的情况似乎有些奇怪… 你能分享一下你的项目结构、go env 的输出结果以及你的 .go 文件的开头部分吗?

go env

GOARCH=“amd64” GOBIN="" GOCACHE="/Users/sibert/Library/Caches/go-build" GOEXE="" GOHOSTARCH=“amd64” GOHOSTOS=“darwin” GOOS=“darwin” GOPATH="/Users/sibert/go" GORACE="" GOROOT="/usr/local/go" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64" GCCGO=“gccgo” CC=“clang” CXX=“clang++” CGO_ENABLED=“1” CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG=“pkg-config” GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/s5/8y4xhk5d5kq1rbd5r3mk4lzw0000gn/T/go-build619608189=/tmp/go-build -gno-record-gcc-switches -fno-common"

main:

package main

import (
	"../mail"
	"fmt"
	"html/template"
	"net/http"
	"strings"
)

mail:

package mail

    import (
    	"log"
    	"net/smtp"
    )

这是一个关于Go模块导入路径的常见问题。关键在于Go模块系统如何解析包路径,以及相对导入与绝对导入的区别。

工作原理解释

相对导入(你的第一个示例)

import (
    mail "./mail"
    fmt "fmt"
)

这里使用相对路径 "./mail",Go编译器会在当前目录的相对路径中查找 mail 包。这种方式:

  • 不依赖于 GOPATH 或模块名称
  • 直接基于文件系统路径解析
  • 适用于简单的项目结构或没有配置模块的情况

绝对导入(你的第二个示例)

import (
    "main/mail"
    fmt "fmt"
)

这里使用绝对路径 "main/mail",Go会:

  1. 首先检查是否在Go模块中(有go.mod文件)
  2. 如果找到go.mod,使用模块名称作为前缀
  3. 如果没有go.mod,回退到 GOPATH 模式

为什么第二个示例失败

在你的情况下,"main/mail" 失败是因为:

  1. 没有go.mod文件:你的项目可能没有初始化Go模块,所以Go回退到 GOPATH 模式

  2. GOPATH模式下的路径解析:在 GOPATH 模式下,Go期望包路径对应文件系统路径:

    • 期望在 $GOPATH/src/main/mail 找到包
    • 但你的项目在 src/main/mail 而不是 $GOPATH/src/main/mail
  3. 目录结构不匹配:你的实际目录结构是:

    src/main/mail/main.go
    src/main/main.go
    

    但Go在 GOPATH 模式下寻找的是:

    $GOPATH/src/main/mail/
    

验证示例

创建一个简单的测试来演示:

main.go:

package main

import "fmt"

func HelloFromMain() {
    fmt.Println("Hello from main package")
}

mail/main.go:

package mail

import "fmt"

func SendEmail() {
    fmt.Println("Sending email from mail package")
}

使用相对导入可以工作:

package main

import (
    mail "./mail"
)

func main() {
    mail.SendEmail()
}

但绝对导入 "main/mail" 在缺少go.mod或正确GOPATH设置时会失败。

根本原因是Go的包解析机制在模块模式和GOPATH模式下的不同行为,以及相对路径与绝对路径解析的逻辑差异。

回到顶部