【Golang】Golang进阶系列教程--为什么 Go 不支持 []T 转换为 []interface
文章目录
- 前言
- 官方解释
- 内存布局
- 程序运行中的内存布局
- 通用方法
前言
在 Go 中,如果 interface{} 作为函数参数的话,是可以传任意参数的,然后通过类型断言来转换。
举个例子:
package mainimport "fmt"func foo(v interface{}) {if v1, ok1 := v.(string); ok1 {fmt.Println(v1)} else if v2, ok2 := v.(int); ok2 {fmt.Println(v2)}
}func main() {foo(233)foo("666")
}
不管是传 int 还是 string,最终都能输出正确结果。
那么,既然是这样的话,我就有一个疑问了,拿出我举一反三的能力。是否可以将 []T 转换为 []interface 呢?
比如下面这段代码:
func foo([]interface{}) { /* do something */ }func main() {var a []string = []string{"hello", "world"}foo(a)
}
很遗憾,这段代码是不能编译通过的,如果想直接通过 b := []interface{}(a) 的方式来转换,还是会报错:
cannot use a (type []string) as type []interface {} in function argument
正确的转换方式需要这样写:
b := make([]interface{}, len(a), len(a))
for i := range a {b[i] = a[i]
}
本来一行代码就能搞定的事情,却非要让人写四行,是不是感觉很麻烦?那为什么 Go 不支持呢?我们接着往下看。
官方解释
这个问题在官方 Wiki 中是有回答的,我复制出来放在下面:
The first is that a variable with type []interface{} is not an interface! It is a slice whose element type happens to be interface{}. But even given this, one might say that the meaning is clear.
Well, is it? A variable with type []interface{} has a specific memory layout, known at compile time.
Each interface{} takes up two words (one word for the type of what is contained, the other word for either the contained data or a pointer to it). As a consequence, a slice with length N and with type []interface{} is backed by a chunk of data that is N2 words long.
This is different than the chunk of data backing a slice with type []MyType and the same length. Its chunk of data will be Nsizeof(MyType) words long.
The result is that you cannot quickly assign something of type []MyType to something of type []interface{}; the data behind them just look different.
大概意思就是说,主要有两方面原因:
- []interface{} 类型并不是 interface,它是一个切片,只不过碰巧它的元素是 interface;
- []interface{} 是有特殊内存布局的,跟 interface 不一样。
下面就来详细说说,是怎么个不一样。
内存布局
首先来看看 slice 在内存中是如何存储的。在源码中,它是这样定义的:
// src/runtime/slice.gotype slice struct {array unsafe.Pointerlen intcap int
}
- array 是指向底层数组的指针;
- len 是切片的长度;
- cap 是切片的容量,也就是 array 数组的大小。
举个例子,创建如下一个切片:
is := []int64{0x55, 0x22, 0xab, 0x9}
那么它的布局如下图所示:
假设程序运行在 64 位的机器上,那么每个「正方形」所占空间是 8 bytes。上图中的 ptr 所指向的底层数组占用空间就是 4 个「正方形」,也就是 32 bytes。
接下来再看看 []interface{} 在内存中是什么样的。
回答这个问题之前先看一下 interface{} 的结构,Go 中的接口类型分成两类:
- iface 表示包含方法的接口;
- eface 表示不包含方法的空接口。
源码中的定义分别如下:
type iface struct {tab *itabdata unsafe.Pointer
}
type eface struct {_type *_typedata unsafe.Pointer
}
具体细节我们不去深究,但可以明确的是,每个 interface{} 包含两个指针, 会占据两个「正方形」。第一个指针指向 itab 或者 _type;第二个指针指向实际的数据。
所以它在内存中的布局如下图所示:
因此,不能直接将 []int64 直接传给 []interface{}。
程序运行中的内存布局
接下来换一个更形象的方式,从程序实际运行过程中,看看内存的分布是怎么样的?
看下面这样一段代码:
package mainvar sum int64func addUpDirect(s []int64) {for i := 0; i < len(s); i++ {sum += s[i]}
}func addUpViaInterface(s []interface{}) {for i := 0; i < len(s); i++ {sum += s[i].(int64)}
}func main() {is := []int64{0x55, 0x22, 0xab, 0x9}addUpDirect(is)iis := make([]interface{}, len(is))for i := 0; i < len(is); i++ {iis[i] = is[i]}addUpViaInterface(iis)
}
我们使用 Delve 来进行调试,可以点击这里进行安装。
dlv debug slice-layout.go
Type 'help' for list of commands.
(dlv) break slice-layout.go:27
Breakpoint 1 set at 0x105a3fe for main.main() ./slice-layout.go:27
(dlv) c
> main.main() ./slice-layout.go:27 (hits goroutine(1):1 total:1) (PC: 0x105a3fe)22: iis := make([]interface{}, len(is))23: for i := 0; i < len(is); i++ {24: iis[i] = is[i]25: }26:
=> 27: addUpViaInterface(iis)28: }
打印 is 的地址:
(dlv) p &is
(*[]int64)(0xc00003a740)
接下来看看 slice 在内存中都包含了哪些内容:
(dlv) x -fmt hex -len 32 0xc00003a740
0xc00003a740: 0x10 0xa7 0x03 0x00 0xc0 0x00 0x00 0x00
0xc00003a748: 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xc00003a750: 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xc00003a758: 0x00 0x00 0x09 0x00 0xc0 0x00 0x00 0x00
每行有 8 个字节,也就是上文说的一个「正方形」。第一行是指向数据的地址;第二行是 4,表示切片长度;第三行也是 4,表示切片容量。
再来看看指向的数据到底是怎么存的:
(dlv) x -fmt hex -len 32 0xc00003a710
0xc00003a710: 0x55 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xc00003a718: 0x22 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xc00003a720: 0xab 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xc00003a728: 0x09 0x00 0x00 0x00 0x00 0x00 0x00 0x00
这就是一片连续的存储空间,保存着实际数据。
接下来用同样的方式,再来看看 iis 的内存布局。
(dlv) p &iis
(*[]interface {})(0xc00003a758)
(dlv) x -fmt hex -len 32 0xc00003a758
0xc00003a758: 0x00 0x00 0x09 0x00 0xc0 0x00 0x00 0x00
0xc00003a760: 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xc00003a768: 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0xc00003a770: 0xd0 0xa7 0x03 0x00 0xc0 0x00 0x00 0x00
切片的布局和 is 是一样的,主要的不同是所指向的数据:
(dlv) x -fmt hex -len 64 0xc000090000
0xc000090000: 0x00 0xe4 0x05 0x01 0x00 0x00 0x00 0x00
0xc000090008: 0xa8 0xee 0x0a 0x01 0x00 0x00 0x00 0x00
0xc000090010: 0x00 0xe4 0x05 0x01 0x00 0x00 0x00 0x00
0xc000090018: 0x10 0xed 0x0a 0x01 0x00 0x00 0x00 0x00
0xc000090020: 0x00 0xe4 0x05 0x01 0x00 0x00 0x00 0x00
0xc000090028: 0x58 0xf1 0x0a 0x01 0x00 0x00 0x00 0x00
0xc000090030: 0x00 0xe4 0x05 0x01 0x00 0x00 0x00 0x00
0xc000090038: 0x48 0xec 0x0a 0x01 0x00 0x00 0x00 0x00
仔细观察上面的数据,偶数行内容都是相同的,这个是 interface{} 的 itab 地址。奇数行内容是不同的,指向实际的数据。
打印地址内容:
(dlv) x -fmt hex -len 8 0x010aeea8
0x10aeea8: 0x55 0x00 0x00 0x00 0x00 0x00 0x00 0x00
(dlv) x -fmt hex -len 8 0x010aed10
0x10aed10: 0x22 0x00 0x00 0x00 0x00 0x00 0x00 0x00
(dlv) x -fmt hex -len 8 0x010af158
0x10af158: 0xab 0x00 0x00 0x00 0x00 0x00 0x00 0x00
(dlv) x -fmt hex -len 8 0x010aec48
0x10aec48: 0x09 0x00 0x00 0x00 0x00 0x00 0x00 0x00
很明显,通过打印程序运行中的状态,和我们的理论分析是一致的。
通用方法
通过以上分析,我们知道了不能转换的原因,那有没有一个通用方法呢?因为我实在是不想每次多写那几行代码。
也是有的,用反射 reflect,但是缺点也很明显,效率会差一些,不建议使用。
func InterfaceSlice(slice interface{}) []interface{} {s := reflect.ValueOf(slice)if s.Kind() != reflect.Slice {panic("InterfaceSlice() given a non-slice type")}// Keep the distinction between nil and empty slice inputif s.IsNil() {return nil}ret := make([]interface{}, s.Len())for i := 0; i < s.Len(); i++ {ret[i] = s.Index(i).Interface()}return ret
}
还有其他方式吗?答案就是 Go 1.18 支持的泛型,这里就不过多介绍了,大家有兴趣的话可以继续研究。
相关文章:
![](https://img-blog.csdnimg.cn/d248e8f19b51463297f721a2641fc471.png)
【Golang】Golang进阶系列教程--为什么 Go 不支持 []T 转换为 []interface
文章目录 前言官方解释内存布局程序运行中的内存布局通用方法 前言 在 Go 中,如果 interface{} 作为函数参数的话,是可以传任意参数的,然后通过类型断言来转换。 举个例子: package mainimport "fmt"func foo(v inter…...
![](https://img-blog.csdnimg.cn/95148e11605f41f2b0017ba32b3fd5ef.png)
两数相加 II——力扣445
题目描述 法一 栈 本题旨在从后往前加,为了逆序处理所有数位,利用栈,把数字压入栈中,再依次取出相加,注意进位!进位是/10,另外需要注意栈的常用函数,push()、pop()、top()࿰…...
![](https://www.ngui.cc/images/no-images.jpg)
js获取上传视频的封面第一帧
代码如下:粘贴到这个在线编辑器里,可以测试效果。 菜鸟教程在线编辑器 <div><div style"flex: 1;border: 1px solid #999; position:relative;color: #333;background-color:#FFF2B8;"><span style"position: absolute…...
![](https://img-blog.csdnimg.cn/529448960ce844ce8b5746a72251ca10.png)
Nginx 高可用负载均衡(三种模式)
一、nginx普通集群负载均衡 1、安装keepalived (1)下载 https://www.keepalived.org/download.html(2)解压 tar -zxvf keepalived-2.0.18.tar.gz(3)使用configure命令配置安装目录与核心配置文件所在位置: ./configure --prefix/usr/local/keepalived --sysconf/e…...
![](https://www.ngui.cc/images/no-images.jpg)
Linux tail命令
在Linux中,tail命令用于查看文件的末尾内容。它可以显示文件的最后几行,默认情况下显示最后10行。 以下是一些常见的使用方式和示例: 显示文件的最后10行: tail filename将会显示名为filename的文件的最后10行内容。 显示文件…...
![](https://img-blog.csdnimg.cn/813d4e85537e4b30b71a6e7639fe814b.png#pic_center)
【屏幕适配发展介绍 Objective-C语言】
一、接下来,我们花一天时间,给大家介绍这个屏幕适配 1.那么,屏幕适配,是什么意思啊 我们说,写程序的时候,我们有时候要做 1)系统适配 2)屏幕适配 1)系统适配:是指的你写的这个代码,在iOS6、iOS7、iOS8,在不同的iOS系统下,是不是运行的效果,一致吧 这个指的是…...
![](https://www.ngui.cc/images/no-images.jpg)
linux中ls命令详解
ls 显示目录内容列表 补充说明 ls命令 就是list的缩写,用来显示目标列表,在Linux中是使用率较高的命令。ls命令的输出信息可以进行彩色加亮显示,以分区不同类型的文件。 语法 ls [选项] [文件名...][-1abcdfgiklmnopqrstuxABCDFGLNQRSUX…...
![](https://img-blog.csdnimg.cn/ad723e25ba764ff3a967c4f847a60911.png)
大盗阿福(记忆化搜索板子)
提供核心代码:(经典的记忆化搜索套路) int dfs(int pos){if(f[pos]!-1) return f[pos];//记忆化if(pos>n) return 0;//边界,越界int sum0;//模板int f10,f20;f1dfs(pos1);f2dfs(pos2)w[pos];summax(f1,f2);//模板f[pos]sum;//模…...
![](https://www.ngui.cc/images/no-images.jpg)
打卡力扣题目八
#左耳听风 ARST 打卡活动重启# 目录 一、问题 二、解题方法一 三、解题方法二 四、两种方法的区别 关于 ARTS 的释义 —— 每周完成一个 ARTS: ● Algorithm: 每周至少做一个 LeetCode 的算法题 ● Review: 阅读并点评至少一篇英文技术文章 ● Tips: 学习至少一…...
![](https://img-blog.csdnimg.cn/2a4afa9d67654c1c9066313de9b87b7d.png)
matlab使用教程(5)—矩阵定义和基本运算
本博客介绍如何在 MATLAB 中创建矩阵和执行基本矩阵计算。 MATLAB 环境使用矩阵来表示包含以二维网格排列的实数或复数的变量。更广泛而言,数组为向量、矩阵或更高维度的数值网格。MATLAB 中的所有数组都是矩形,在这种意义上沿任何维度的分量向量的长度…...
![](https://img-blog.csdnimg.cn/00794742e8a04826af7a9dbe1f45c229.png)
用HTML写一个简单的静态购物网站
实现代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>购物网站</title> &l…...
![](https://www.ngui.cc/images/no-images.jpg)
如何在go中实现程序的优雅退出,go-kratos源码解析
使用kratos这个框架有近一年了,最近了解了一下kratos关于程序优雅退出的具体实现。 这部分逻辑在app.go文件中,在main中,找到app.Run方法,点进入就可以了 它包含以下几个部分: App结构体:包含应用程序的配置选项和运行时状态。 …...
![](https://img-blog.csdnimg.cn/1b934526ee52476195f3be8365be53ee.png)
Appium+python自动化(二十八)- 高级滑动(超详解)
高级溜冰的滑动 滑动操作一般是两点之间的滑动,这种滑动在这里称其为低级的溜冰滑动;就是上一节给小伙伴们分享的。然而实际使用过程中用户可能要进行一些多点连续滑动操作。如九宫格滑动操作,连续拖动图片移动等场景。那么这种高级绚丽的溜…...
![](https://img-blog.csdnimg.cn/9d0a7ea3030a4f70a4dc02f9e0a96d8d.png)
github token使用方法
git remote set-url origin https://<githubtoken>github.com/<username>/<repositoryname>.git 在私有仓库的HTTPS的url上加入<githubtoken>即为token url,可以免ssh key登录...
![](https://www.ngui.cc/images/no-images.jpg)
Spring属性注解对配置项名称的自动转换
一、前言 在Spring中,我们经常需要将配置文件中的属性值注入到Java类中。Spring提供了两个主要的注解来实现这一功能:Value 和 ConfigurationProperties。其中 ConfigurationProperties支持将配置项名称与Java类中的属性名进行自动转换,包括…...
![](https://img-blog.csdnimg.cn/6369911301e14492bf08e9d459824596.jpeg)
Docker 安全 Docker HTTPS请求过程与配置
Docker 容器安全注意点 尽量别做的事 尽量不用 --privileged 运行容器(授权容器root用户拥有宿主机的root权限) 尽量不用 --network host 运行容器(使用 host 网络模式共享宿主机的网络命名空间) 尽量不在容器中运行 ssh 服务 尽…...
![](https://img-blog.csdnimg.cn/ef9a8f6cf94b467095e45f82571b6338.png)
DevOps(三)
CD(二) 1. 整体流程2. 环境准备1. jenkins安装2. 编译安装git3. docker安装4. docker-compose安装5. sonarqube安装6. harbor安装7. gitlab私服8. maven安装9. Nexus部署10. K8s部署3. 安装java及编写代码3.1 安装java3.2 安装IntelliJ IDEA3.3 安装tomcat3.4 安装maven3.5 c…...
![](https://www.ngui.cc/images/no-images.jpg)
AOP的妙用
一、改代码 自定义注解用于提示该代码已经在AOP中重构了 public interface ReviseToAop {// 用于记录修改状态String value() default ""; }使用注解(无意义,只是表名被修改) ReviseToAop("修改于:2023/7/30&quo…...
![](https://img-blog.csdnimg.cn/img_convert/8df63de3ecc488a3d3fff60c6e5bdec0.png)
CAN转ETHERCAT网关将CAN 总线和 ETHERCAT 网络连接方法
由于好多现场会出现将CAN总线的设备接到EtherCAT网络中,由于协议的不相同,不能直接进行连接,现需一种能同时兼容CAN 总线和ETHERCAT网络的一种设备,由此捷米JM-ECT-CAN 是自主研发的一款 ETHERCAT 从站功能的通讯网关。该产品主要…...
![](https://img-blog.csdnimg.cn/061f6e15fe3744eaadf521cbf64bfc81.png)
【大数据趋势】7月30日 汇率,恒指期货的大数据趋势概率分析。
1. 数据源头之一 : 汇率变化 从程序模拟趋势来看,美元在持续弱势状态,周线上正在构建一个新的下跌趋势,而且正在反抽过程中,即将完成,如果没有外部干预,会顺势往下。从月线来看,高点逐步降低&a…...
![](https://img-blog.csdnimg.cn/774a60ee98dc461c8e53168013d7e7d1.png)
mac使用mvn下载node-sass 会Binary download failed, trying source
m1 上使用nvm 以下node的版本可以直接下载(Binary download,而不是 trying source)而不用切换mac cpu架构 zhiwenwenzhiwenwendeMBP cockpit % nvm install 14.15.5 Downloading and installing node v14.15.5... Downloading https://node…...
![](https://img-blog.csdnimg.cn/6cbcd6c17cec4dba9bb3c0f895f02fa2.png)
【C++】开源:Muduo网络库配置与使用
😏★,:.☆( ̄▽ ̄)/$:.★ 😏 这篇文章主要介绍Muduo网络库配置与使用。 无专精则不能成,无涉猎则不能通。——梁启超 欢迎来到我的博客,一起学习,共同进步。 喜欢的朋友可以关注一下,下…...
![](https://www.ngui.cc/images/no-images.jpg)
VCS ICO - Intelligent Coverage Optimization
ico是vcs提供的用于优化覆盖率的feature;一般用户通过dist solver bofore等约束了变量的随机概率,而ico会在用户约束的基础上,做一些自动“修正”,以此来优化随机激励,提高随机多样性,加速覆盖率收敛&#…...
![](https://img-blog.csdnimg.cn/0cf804d67f134149982033b1127337f5.png)
【分布式系统】分布式系统的8个谬误
网络可靠 对于分布式系统来说,网络、计算、存储是三大基石,系统之间进行拆分隔离之后,那么必定存在网络通讯,而网络是最不可靠的。 不管是从硬件层面还是软件层面来说,网络是不可靠的。(断电、配置错误、ID…...
![](https://img-blog.csdnimg.cn/ff192303f5ea4826b0871d40a13a711a.png)
tinkerCAD案例:25. 量角器 - 测量角度
tinkerCAD案例:25. 量角器 - 测量角度 原文 Now we’re going to make a protractor! A Protractor is one of the most basic, but essential, tools for making measurements. It is, then, surprising that the modern protractor is barely over 200 years ol…...
![](https://img-blog.csdnimg.cn/bd36e8520ecf4ba8b61cbfd4e7a85295.gif)
Flutter 使用texture_rgba_renderer实现桌面端渲染视频
Flutter视频渲染系列 第一章 Android使用Texture渲染视频 第二章 Windows使用Texture渲染视频 第三章 Linux使用Texture渲染视频 第四章 全平台FFICustomPainter渲染视频 第五章 Windows使用Native窗口渲染视频 第六章 桌面端使用texture_rgba_renderer渲染视频(本…...
![](https://www.ngui.cc/images/no-images.jpg)
linux虚拟机开机后桌面显示CentOS-7.5-x86盘片文件,并且无法远程连接虚拟机?
在虚拟机启动后遇到了显示CentOS-7.5-x86光盘片文件的问题,并且无法远程连接到虚拟机,有几个可能的解决方法: 检查虚拟机设置:确保虚拟机的网络适配器已正确配置,并且虚拟机配置的网络选项是桥接模式或 NAT 模式&#…...
![](https://img-blog.csdnimg.cn/8a014edcdc484fb4aa861c0859c94ad8.png)
【Spring Boot 源码学习】走近 AutoConfigurationImportSelector
AutoConfigurationImportSelector 源码解析 引言主要内容1. ImportSelector 接口2. DeferredImportSelector 接口3. AutoConfigurationImportSelector 功能概述 总结 引言 上篇博文我们了解了 EnableAutoConfiguration 注解,其中真正实现自动配置功能的核心实现者 …...
![](https://www.ngui.cc/images/no-images.jpg)
系统学习Linux-MySQL数据库备份(四)
一、概述 数据库备份是指将数据库中的数据、表格、视图、存储过程、触发器等信息备份到另一个地方,一遍在数据库丢失或损坏时进行恢复,数据库备份是数据库管理中必不可少的一项工作,通过备份可以保护数据库中的数据和业务。 二、数据备份的…...
![](https://img-blog.csdnimg.cn/9c53983dd8c847509b434d1de3825375.png#pic_center)
具身智能controller---RT-1(Robotics Transformer)(中---实验介绍)
6 实验 实验目的是验证以下几个问题: RT-1可以学习大规模指令数据,并且可以在新任务、对象和环境上实现zero-shot的泛化能力?训练好的模型可以进一步混合多种其他数据(比如仿真数据和来自其他机器人的数据)吗?多种方…...
![](/images/no-images.jpg)
ftp服务器搭建设置网站信息/企业网站模板图片
收集一些有用的博客,造福全人类: http://fullstack.info/ 不知道是谁,好像很吊的样子http://substack.net/ substack nodejs大神,贡献了几百个nodejs模块http://blog.izs.me/ nodejs巨屌http://lucumr.pocoo.org/ Flask作者http:…...
![](https://images2018.cnblogs.com/blog/974776/201805/974776-20180505003738174-1281180334.png)
怎么做网站作业/网络推广公司专业网络
数组是一段线性分配的内存, 它通过整数计算偏移并访问其中的元素. 数组是一种性能出色的数据结构. 1.数组字面量 数组字面量提供了一种非常方便地创建新数组的表示法. 多个用逗号分隔的值的表达式. 数组字面量允许出现在任何表达式可以出现的地方. 数组的第一个值将获得属性名0…...
![](https://img-blog.csdnimg.cn/img_convert/9b012a54f8cff246931a6e0207f02bd2.png)
做网站的最终目的/建立一个网站需要多少钱
JVM参数-XX:PrintGCDetails 在发生垃圾收集行为时打印内存回收日志,并且在进程退出的时候输出当前的内存各区域分配情况。 Minor GC VS Full GC 新生代GC(Minor GC):发生在新生代的垃圾收集动作,Java对象大多朝生夕…...
![](https://img-blog.csdnimg.cn/f6c7759ac4b04000ae173026beb641ef.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA6ZmM5a2Q44CC,size_20,color_FFFFFF,t_70,g_se,x_16)
网站建设排名的公司哪家好/文明seo技术教程网
本文通过Python代码的编写,对NGSIM数据集中车辆变道时周边车辆的加速度、速度等信息进行提取,主要介绍代码逻辑及思路。 关于NGSIM数据集不再赘述,本人上传有NGSIM各路段各车型的车辆数据以及各路段平面示意图,可点击链接NGSIM数据…...
东莞樟木头网站设计/起名最好的网站排名
今天看到了一个Python库,名为markdown。瞬间就给了我一个灵感,那就是制作一个将markdown文件转换成html文件的小工具。 我的实验环境 操作系统: Windows 7 64位 旗舰版Python版本: 2.7.11IDE: PyCharm pro 2016.1所需…...
![](/images/no-images.jpg)
自己做的网站可以挂在哪里/搜索引擎营销
写在前面: 2009年的时候结合网上的资料整理了下Flashback的用法。 2011年5月份起,把我这几年来的整理的资料又重新进行了一个整理,暂定为《David Dai Oracle 学习手册》, 等第一版整理完会免费上传到网络。 对Flashback 这块又重新…...