Go 由于不支持泛型而臭名昭著,但最近,泛型已接近成为现实。Go 团队实施了一个看起来比较稳定的设计草案,并且正以源到源翻译器原型的形式获得关注。本文讲述的是泛型的最新设计,以及如何自己尝试泛型。
创新互联是一家集网站建设,白银区企业网站建设,白银区品牌网站建设,网站定制,白银区网站建设报价,网络营销,网络优化,白银区网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
例子
FIFO Stack
假设你要创建一个先进先出堆栈。没有泛型,你可能会这样实现:
type Stack []interface{}func (s Stack) Peek() interface{} {
return s[len(s)-1]
}
func (s *Stack) Pop() {
*s = (*s)[:
len(*s)-1]
}
func (s *Stack) Push(value interface{}) {
*s =
append(*s, value)
}
但是,这里存在一个问题:每当你 Peek 项时,都必须使用类型断言将其从 interface{} 转换为你需要的类型。如果你的堆栈是 *MyObject 的堆栈,则意味着很多 s.Peek().(*MyObject)这样的代码。这不仅让人眼花缭乱,而且还可能引发错误。比如忘记 * 怎么办?或者如果您输入错误的类型怎么办?s.Push(MyObject{})` 可以顺利编译,而且你可能不会发现到自己的错误,直到它影响到你的整个服务为止。
通常,使用 interface{} 是相对危险的。使用更多受限制的类型总是更安全,因为可以在编译时而不是运行时发现问题。
泛型通过允许类型具有类型参数来解决此问题:
type Stack(type T) []Tfunc (s Stack(T)) Peek() T {
return s[len(s)-1]
}
func (s *Stack(T)) Pop() {
*s = (*s)[:
len(*s)-1]
}
func (s *Stack(T)) Push(value T) {
*s =
append(*s, value)
}
这会向 Stack 添加一个类型参数,从而完全不需要 interface{}。现在,当你使用 Peek() 时,返回的值已经是原始类型,并且没有机会返回错误的值类型。这种方式更安全,更容易使用。(译注:就是看起来更丑陋,^-^)
此外,泛型代码通常更易于编译器优化,从而获得更好的性能(以二进制大小为代价)。如果我们对上面的非泛型代码和泛型代码进行基准测试,我们可以看到区别:
type MyObject struct {
X
int
}
var sink MyObjectfunc BenchmarkGo1(b *testing.B) {
for i := 0; i b.N; i++ {
var s Stack
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink = s.Peek().(MyObject)
}
}
func BenchmarkGo2(b *testing.B) {
for i := 0; i b.N; i++ {
var s Stack(MyObject)
s.Push(MyObject{})
s.Push(MyObject{})
s.Pop()
sink = s.Peek()
}
}
结果:
BenchmarkGo1BenchmarkGo1-16 12837528 87.0 ns/op 48 B/op 2 allocs/opBenchmarkGo2BenchmarkGo2-16 28406479 41.9 ns/op 24 B/op 2 allocs/op
在这种情况下,我们分配更少的内存,同时泛型的速度是非泛型的两倍。
合约(Contracts)
上面的堆栈示例适用于任何类型。但是,在许多情况下,你需要编写仅适用于具有某些特征的类型的代码。例如,你可能希望堆栈要求类型实现 String() 函数
本教程介绍了使用 Godatabase/sql及其标准库中的包访问关系数据库的基础知识。
您将使用的database/sql包包括用于连接数据库、执行事务、取消正在进行的操作等的类型和函数。
在本教程中,您将创建一个数据库,然后编写代码来访问该数据库。您的示例项目将是有关老式爵士乐唱片的数据存储库。
首先,为您要编写的代码创建一个文件夹。
1、打开命令提示符并切换到您的主目录。
在 Linux 或 Mac 上:
在 Windows 上:
2、在命令提示符下,为您的代码创建一个名为 data-access 的目录。
3、创建一个模块,您可以在其中管理将在本教程中添加的依赖项。
运行go mod init命令,为其提供新代码的模块路径。
此命令创建一个 go.mod 文件,您添加的依赖项将在其中列出以供跟踪。
注意: 在实际开发中,您会指定一个更符合您自己需求的模块路径。有关更多信息,请参阅一下文章。
GO语言(二十五):管理依赖项(上)
GO语言(二十六):管理依赖项(中)
GO语言(二十七):管理依赖项(下)
接下来,您将创建一个数据库。
在此步骤中,您将创建要使用的数据库。您将使用 DBMS 本身的 CLI 创建数据库和表,以及添加数据。
您将创建一个数据库,其中包含有关黑胶唱片上的老式爵士乐录音的数据。
这里的代码使用MySQL CLI,但大多数 DBMS 都有自己的 CLI,具有类似的功能。
1、打开一个新的命令提示符。
在命令行,登录到您的 DBMS,如下面的 MySQL 示例所示。
2、在mysql命令提示符下,创建一个数据库。
3、切到您刚刚创建的数据库,以便您可以添加表。
4、在文本编辑器的 data-access 文件夹中,创建一个名为 create-tables.sql 的文件来保存用于添加表的 SQL 脚本。
将以下 SQL 代码粘贴到文件中,然后保存文件。
在此 SQL 代码中:
(1)删除名为album表。 首先执行此命令可以让您更轻松地稍后重新运行脚本。
(2)创建一个album包含四列的表:title、artist和price。每行的id值由 DBMS 自动创建。
(3)添加带有值的四行。
5、在mysql命令提示符下,运行您刚刚创建的脚本。
您将使用以下形式的source命令:
6、在 DBMS 命令提示符处,使用SELECT语句来验证您是否已成功创建包含数据的表。
接下来,您将编写一些 Go 代码进行连接,以便进行查询。
现在你已经有了一个包含一些数据的数据库,开始你的 Go 代码。
找到并导入一个数据库驱动程序,该驱动程序会将您通过database/sql包中的函数发出的请求转换为数据库可以理解的请求。
1、在您的浏览器中,访问SQLDrivers wiki 页面以识别您可以使用的驱动程序。
2、使用页面上的列表来识别您将使用的驱动程序。为了在本教程中访问 MySQL,您将使用 Go-MySQL-Driver。
3、请注意驱动程序的包名称 - 此处为github.com/go-sql-driver/mysql.
4、使用您的文本编辑器,创建一个用于编写 Go 代码的文件,并将该文件作为 main.go 保存在您之前创建的数据访问目录中。
5、进入main.go,粘贴以下代码导入驱动包。
在此代码中:
(1)将您的代码添加到main包中,以便您可以独立执行它。
(2)导入 MySQL 驱动程序github.com/go-sql-driver/mysql。
导入驱动程序后,您将开始编写代码以访问数据库。
现在编写一些 Go 代码,让您使用数据库句柄访问数据库。
您将使用指向结构的指针sql.DB,它表示对特定数据库的访问。
编写代码
1、进入 main.go,在import您刚刚添加的代码下方,粘贴以下 Go 代码以创建数据库句柄。
在此代码中:
(3)使用 MySQL 驱动程序Config和FormatDSN类型以收集连接属性并将它们格式化为连接字符串的 DSN。
该Config结构使代码比连接字符串更容易阅读。
(4)调用sql.Open 初始化db变量,传递 FormatDSN。
(5)检查来自 的错误sql.Open。例如,如果您的数据库连接细节格式不正确,它可能会失败。
为了简化代码,您调用log.Fatal结束执行并将错误打印到控制台。在生产代码中,您会希望以更优雅的方式处理错误。
(6)调用DB.Ping以确认连接到数据库有效。在运行时, sql.Open可能不会立即连接,具体取决于驱动程序。您在Ping此处使用以确认 database/sql包可以在需要时连接。
(7)检查来自Ping的错误,以防连接失败。
(8)Ping如果连接成功,则打印一条消息。
文件的顶部现在应该如下所示:
3、保存 main.go。
1、开始跟踪 MySQL 驱动程序模块作为依赖项。
使用go get 添加 github.com/go-sql-driver/mysql 模块作为您自己模块的依赖项。使用点参数表示“获取当前目录中代码的依赖项”。
2、在命令提示符下,设置Go 程序使用的DBUSER和DBPASS环境变量。
在 Linux 或 Mac 上:
在 Windows 上:
3、在包含 main.go 的目录中的命令行中,通过键入go run来运行代码。
连接成功了!
接下来,您将查询一些数据。
1、简单易学。
Go语言的作者本身就很懂C语言,所以同样Go语言也会有C语言的基因,所以对于程序员来说,Go语言天生就会让人很熟悉,容易上手。
2、并发性好。
Go语言天生支持并发,可以充分利用多核,轻松地使用并发。 这是Go语言最大的特点。
描述
Go的语法接近C语言,但对于变量的声明有所不同。Go支持垃圾回收功能。Go的并行模型是以东尼·霍尔的通信顺序进程(CSP)为基础,采取类似模型的其他语言包括Occam和Limbo,但它也具有Pi运算的特征,比如通道传输。
在1.8版本中开放插件(Plugin)的支持,这意味着现在能从Go中动态加载部分函数。
与C++相比,Go并不包括如枚举、异常处理、继承、泛型、断言、虚函数等功能,但增加了 切片(Slice) 型、并发、管道、垃圾回收、接口(Interface)等特性的语言级支持。
rune是Go语言中一种特殊的数据类型,它是int32的别名,几乎在所有方面等同于int32,用于区分字符值和整数值,官方解释如下:
下面我们通过一个例子来看一下:
我们猜测一下结果,hello5 个字符+1 个空格+3 个汉子,算起来应该是 9 个,长度为 9 才对,但是我们执行一下,
结果打印是 15,这是为什么呢?
所以计算出的长度就等于 5+1+3*3=15
如果我们需要计算出字符串的长度,而不是底层字节的个数,那么可以使用下面的方法:
运行结果如下:
在 rune 定义上方还有一个,byte = uint8