Go's Package Name Space

hittata · · 1887 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

 

Go's PackageName Space

Posted onTuesday, February 2, 2010. 

Go organizes programs into individualpieces called packages. A package gets to pick a short name for itself,like vector, even if the import statement must use a longer pathlike "container/vector"to name the file where the compiled codeis installed. The early Go compilers used the package name as a uniqueidentifier during linking, so that vector's New function could be distinguished fromlist's New. In the final binary, one was vector.New and the other list.New. As we started to fill out the standardlibrary, it became clear that we needed to do something about managing thepackage name space: if multiple packages tried to be package vector, their symbols would collide in thelinker. For a while we considered segmenting the name space, reservinglower-case names for standard packages and upper-case names for local packages.(Since package names and object file names are conventionally the same, onereason not to do this is that it would require a case-sensitive file system.)

Other languages simply use longer names.Both Java and Python tie the name to the directory in which the package isfound, as in com.java.google.WebServer for the code incom/java/google/WebServer.class. In practice this leads to unnecessarilylong identifiers, something Go tries to avoid. It also ties the name to aparticular mechanism for finding code: a file system. One of the reasons thatimport paths are string constants in Go is so that it is easy to substituteother notations, like URLs.

Last spring, during a long discussionabout how to divide up the package name space, Robert Griesemer cut the Gordianknot by suggesting thatwe allow multiple packages to choose asingle name and fix the tool chain to cope. The import statement already allows introducing a localalias for the package during the import, so there's no linguistic reason packagenames have to be unique. We all agreed that this was the right approach, but weweren't sure how to implement it. Other considerations, like the open sourcerelease, took priority during most of 2009, but we recently returned to theproblem.(翻译:去年春天,对于如何划分包的命名空间,大家经过一个漫长的讨论,最后Robert Griesemer快刀斩乱麻的接收建议:我们允许多个包选用同一个名称,通过修改go编译工具链以适应这种决策。import语句在引进包时允许定义一个别名。所以没有语言级别限制package名称必须唯一。我们认为这是个正确的道路,但是我们不能确信能否实现它。考虑到其他因素:比如将2009年开源发布作为优先事件,但是我们还是回到解决这个问题上。)

包引用格式:

import"lib/math"      // math.Sin   正常应用
import M "lib/math"  // M.Sin       采用别名,避免引入包名相同的包引发冲突
import . "lib/math"     Sin       省略掉包前缀,直接使用包中的函数和变量

import _ "lib/math"  //_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。

Go 支持初始化函数:

• 每个源⽂文件都可以定义一个或多个初始化函数 func init() {}。

• 所有初始化函数都会在 main() 之前,在单一线程上被调⽤用,仅执行一次。

• 编译器不能保证多个初始化函数的执行次序。

• 初始化函数在当前包所有全局变量初始化 (零或初始化表达式值) 完成后执⾏行。

• 不能在程序代码中直接或间接调⽤用初始化函数。

Ultimately, the linker needs some uniquename for each symbol in the program; the fundamental problem caused by decidingthat package names won't be unique is to find another source of uniqueness thatfits into the tool chain well.

The best approach* seems to be to use thepackage's import path as the unique identifier, since it must uniquely identifythe package in the import statement already. Then container/vector's New iscontainer/vector.New. But! When you're compiling a package,how does the compiler know what the package's import path will be? The packagestatement just says vector, and while every compilation thatimports "container/vector" knows the import path, thecompilation of vector itself does not, because compilation is handledseparately from installing the binary in its final, importable location.

Last week I changed the gc compiler suiteto do this. My solution to the import path question was to introduce a specialname syntax that refers to “this package's import path.” Because the importpaths are string literals in the Go compiler metadata, I chose the emptystring—""—as the self-reference name. Thus, in the object file for package vector, the local symbol New is written "".New. When the linker reads the object file,it knows what import path it used to find the file. It substitutes that pathfor the "", producing, in this case, the unique name container/vector.New.

Not embedding a package's final installedlocation in its object file makes the object files easy to move and duplicate.For example, consider this trivial package:

package seq

var n int

func Next() int {

   n++

   return n

}

It's valid for a Go program to import thesame path multiple times using different local names, but all the names end upreferring to the same package:

package main

 

import (

   "fmt"

   s "seq" // changed to "seq1" later

   t "seq"

)

 

func main() {

   fmt.Println(s.Next(), s.Next(), t.Next(), t.Next())

}

prints 1 2 3 4, because it all four calls are to thesame Next function:

$ 6g seq.go

$ 6g -I. main.go

$ 6l -L. main.6

$ 6.out

1 2 3 4

$

But if we change one of the imports tosay "seq1" and then merely copy the "seq" binary to "seq1", we've created a distinct package, usinglowly cp instead of a compiler:

$ cp seq.6 seq1.6

$ ed main.go

120

/seq

 s"seq"

s/seq/seq1

 s"seq1"

wq

121

$ 6g -I. main.go

$ 6l -L. main.6

$ 6.out

1 2 1 2

$

Now the s.Next calls refer to seq1.6's Next, while the t.Next calls refer to seq.6's Next. Duplicating the object actuallyduplicated the code. This is very different from the behavior of a traditionalC compiler and linker.

A digression: the explicit "". prefix is not strictly necessary. Itwould be cleaner if the linker treated every symbol as needing to be qualifiedby the import path, so that all the "". could be dropped. But occasionallyit's important to be able to break the rules, for example to define a symbolthat is logically in one package be implemented in another. For example, theimplementation of unsafe.Reflectis actually in the binary for package runtime, because that's where all the interfacemanipulation code lives:

$ 6nm pkg/darwin_amd64/runtime.a|grepReflect

iface.6: T unsafe.Reflect

$

Another reason to use an explicit prefixis to admit names with no prefix at all, as would be generated by legacy Ccode. Otherwise, what should C's printf be in? If the linker enforced astrict boundary between packages, both of these examples would be impossible.Most of the time that would be a good thing, but systems languages do not havethe luxury of stopping at “most of the time.” Last October, a few weeks beforethe public release of Go, I changed the linker to insert import path qualifierson all names during linking, but it was too disruptive a change to commitbefore the release. Last week's implementation, which allows for semipermeablepackage boundaries, is a much better fit for Go.

This week Ian Lance Taylor is working oneliminating the global package name space assumption in gccgo. He'd like toavoid making changes to the linker, which rules out introducing a “thispackage” notation like "". Gccgo must be able to write objects thatknow their own import paths, which means gccgo must know the import path atcompile time. But how? There will be a new gccgo command line option, and thebuild system will simply tell the compiler what the import path is.

In retrospect, I wonder if the effortof "" in the gc tool chain was justified compared toadding an option. The gc implementation is easier to use, but it's not clearhow important that will be. Time will tell.

 

 


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:hittata

查看原文:Go's Package Name Space

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1887 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传