go语言错题集(坑)【三】_type *shopi.shopclient is pointer to interface, no-程序员宅基地

技术标签: go  

系列相关:

go语言错题集(坑)【一】

go语言错题集(坑)【二】

go语言错题集(坑)【三】

目录

 

不要对Go并发函数的执行时机做任何假设

假设T类型的方法上接收器既有T类型的,又有*T指针类型的,那么就不可以在不能寻址的T值上调用*T接收器的方法

一个包含nil指针的接口不是nil接口

将map转化为json字符串的时候,json字符串中的顺序和map赋值顺序无关

Json反序列化数字到interface{}类型的值中,默认解析为float64类型

即使在有多个变量、且有的变量存在有的变量不存在、且这些变量共同赋值的情况下,也不可以使用:=来给全局变量赋值

*interface 是一个指向interface的指针类型,而不是interface类型


不要对Go并发函数的执行时机做任何假设

请看下列的列子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import (
	"fmt"
	"runtime"
	"time"
)

func main(){
	names := []string{"lily", "yoyo", "cersei", "rose", "annei"}
	for _, name := range names{
		go func(){
			fmt.Println(name)
		}()
	}
	runtime.GOMAXPROCS(1)
	runtime.Gosched()
}

请问输出什么?

答案:

1
2
3
4
5
annei
annei
annei
annei
annei

为什么呢?是不是有点诧异?
输出的都是“annei”,而“annei”又是“names”的最后一个元素,那么也就是说程序打印出了最后一个元素的值,而name对于匿名函数来讲又是一个外部的值。因此,我们可以做一个推断:虽然每次循环都启用了一个协程,但是这些协程都是引用了外部的变量,当协程创建完毕,再执行打印动作的时候,name的值已经不知道变为啥了,因为主函数协程也在跑,大家并行,但是在此由于names数组长度太小,当协程创建完毕后,主函数循环早已结束,所以,打印出来的都是遍历的names最后的那一个元素“annei”。
如何证实以上的推断呢?
其实很简单,每次循环结束后,停顿一段时间,等待协程打印当前的name便可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import (
	"fmt"
	"runtime"
	"time"
)

func main(){
	names := []string{"lily", "yoyo", "cersei", "rose", "annei"}
	for _, name := range names{
		go func(){
			fmt.Println(name)
		}()
		time.Sleep(time.Second)
	}
	runtime.GOMAXPROCS(1)
	runtime.Gosched()
}

打印结果:

1
2
3
4
5
lily
yoyo
cersei
rose
annei

以上我们得出一个结论,不要对“go函数”的执行时机做任何的假设,除非你确实能做出让这种假设成为绝对事实的保证。

假设T类型的方法上接收器既有T类型的,又有*T指针类型的,那么就不可以在不能寻址的T值上调用*T接收器的方法

请看代码,试问能正常编译通过吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import (
	"fmt"
)
type Lili struct{
	Name string
}

func (Lili *Lili) fmtPointer(){
	fmt.Println("poniter")
}

func (Lili Lili) fmtReference(){
	fmt.Println("reference")
}


func main(){
	li := Lili{}
	li.fmtPointer()
}

答案:

1
能正常编译通过,并输出"poniter"

感觉有点诧异,请接着看以下的代码,试问能编译通过?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import (
	"fmt"
)
type Lili struct{
	Name string
}

func (Lili *Lili) fmtPointer(){
	fmt.Println("poniter")
}

func (Lili Lili) fmtReference(){
	fmt.Println("reference")
}


func main(){
	Lili{}.fmtPointer()
}

答案:

1
2
3
不能编译通过。
“cannot call pointer method on Lili literal”
“cannot take the address of Lili literal”

是不是有点奇怪?这是为什么呢?其实在第一个代码示例中,main主函数中的“li”是一个变量,li的虽然是类型Lili,但是li是可以寻址的,&li的类型是*Lili,因此可以调用*Lili的方法。

一个包含nil指针的接口不是nil接口

请看下列代码,试问返回什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import (
	"bytes"
	"fmt"
	"io"
)

const debug = true

func main(){
	var buf *bytes.Buffer
	if debug{
		buf = new(bytes.Buffer)
	}
	f(buf)
}
func f(out io.Writer){

	if out != nil{
		fmt.Println("surprise!")
	}
}

答案是输出:surprise。
ok,让我们吧debug开关关掉,及debug的值变为false。那么输出什么呢?是不是什么都不输出?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import (
	"bytes"
	"fmt"
	"io"
)

const debug = false

func main(){
	var buf *bytes.Buffer
	if debug{
		buf = new(bytes.Buffer)
	}
	f(buf)
}
func f(out io.Writer){

	if out != nil{
		fmt.Println("surprise!")
	}
}

答案是:依然输出surprise。

这是为什么呢?
这就牵扯到一个概念了,是关于接口值的。概念上讲一个接口的值分为两部分:一部分是类型,一部分是类型对应的值,他们分别叫:动态类型和动态值。类型系统是针对编译型语言的,类型是编译期的概念,因此类型不是一个值。
在上述代码中,给f函数的out参数赋了一个*bytes.Buffer的空指针,所以out的动态值是nil。然而它的动态类型是bytes.Buffer,意思是:“A non-nil interface containing a nil pointer”,所以“out!=nil”的结果依然是true。
但是,对于直接的``
bytes.Buffer``类型的判空不会出现此问题。

1
2
3
4
5
6
7
8
9
10
11
import (
	"bytes"
	"fmt"
)

func main(){
	var buf *bytes.Buffer
	if buf == nil{
		fmt.Println("right")
	}
}

还是输出: right
只有 接口指针 传入函数的接口参数时,才会出现以上的坑。
修改起来也很方便,把*bytes.Buffer改为io.Writer就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import (
	"bytes"
	"fmt"
	"io"
)
const debug = false
func main(){
	var buf  io.Writer //原来是var buf *bytes.Buffer
	if debug{
		buf = new(bytes.Buffer)
	}
	f(buf)
}
func f(out io.Writer){
	if out != nil{
		fmt.Println("surprise!")
	}
}

将map转化为json字符串的时候,json字符串中的顺序和map赋值顺序无关

请看下列代码,请问输出什么?若为json字符串,则json字符串中key的顺序是什么?

1
2
3
4
5
6
7
8
9
10
func main() {
	params := make(map[string]string)

	params["id"] = "1"
	params["id1"] = "3"
	params["controller"] = "sections"

	data, _ := json.Marshal(params)
	fmt.Println(string(data))
}

答案:输出{"controller":"sections","id":"1","id1":"3"}
利用Golang自带的json转换包转换,会将map中key的顺序改为字母顺序,而不是map的赋值顺序。map这个结构哪怕利用for range遍历的时候,其中的key也是无序的,可以理解为map就是个无序的结构,和php中的array要区分开来

Json反序列化数字到interface{}类型的值中,默认解析为float64类型

请看以下程序,程序想要输出json数据中整型id加上3的值,请问程序会报错吗?

1
2
3
4
5
6
7
8
9
10
11
12
func main(){
	jsonStr := `{"id":1058,"name":"RyuGou"}`
	var jsonData map[string]interface{}
	json.Unmarshal([]byte(jsonStr), &jsonData)

	sum :=  jsonData["id"].(int) + 3
	fmt.Println(sum)
}

``` 
答案是会报错,输出结果为:

panic: interface conversion: interface {} is float64, not int

1
使用 Golang 解析 JSON  格式数据时,若以 interface{} 接收数据,则会按照下列规则进行解析:

bool, for JSON booleans

float64, for JSON numbers

string, for JSON strings

[]interface{}, for JSON arrays

map[string]interface{}, for JSON objects

nil for JSON null

1
2
3
4
5
6
7
8
9
10
11
应该改为:
```go
func main(){
	jsonStr := `{"id":1058,"name":"RyuGou"}`
	var jsonData map[string]interface{}
	json.Unmarshal([]byte(jsonStr), &jsonData)

	sum :=  int(jsonData["id"].(float64)) + 3
	fmt.Println(sum)
}

即使在有多个变量、且有的变量存在有的变量不存在、且这些变量共同赋值的情况下,也不可以使用:=来给全局变量赋值

:=往往是用来声明局部变量的,在多个变量赋值且有的值存在的情况下,:=也可以用来赋值使用,例如:

1
2
msgStr := "hello wolrd"
msgStr, err := "hello", errors.New("xxx")//err并不存在

但是,假如全局变量也使用类似的方式赋值,就会出现问题,请看下列代码,试问能编译通过吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var varTest string

func test(){
	varTest, err := function()
	fmt.Println(err.Error())
}

func function()(string, error){
	return "hello world", errors.New("error")
}


func main(){
	test()
}

答案是:通不过。输出:

1
varTest declared and not used

但是如果改成如下代码,就可以通过:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var varTest string

func test(){
	err := errors.New("error")
	varTest, err = function()
	fmt.Println(err.Error())
}

func function()(string, error){
	return "hello world", errors.New("error")
}


func main(){
	test()
}

输出:

1
error

这是什么原因呢?
答案其实很简单,在test方法中,如果使用varTest, err := function()这种方式的话,相当于在函数中又定义了一个和全局变量varTest名字相同的局部变量,而这个局部变量又没有使用,所以会编译不通过。

*interface 是一个指向interface的指针类型,而不是interface类型

请问以下代码,能编译通过吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import (
	"fmt"
)

type Father interface {
	Hello()
}


type Child struct {
	Name string
}

func (s Child)Hello()  {

}

func main(){
	var buf  Child
	buf = Child{}
	f(&buf)
}
func f(out *Father){
	if out != nil{
		fmt.Println("surprise!")
	}
}

答案是:不能编译通过。输出:

1
*Father is pointer to interface, not interface

注意了:接口类型的变量可以被赋值为实现接口的结构体的实例,但是并不能代表接口的指针可以被赋值为实现接口的结构体的指针实例。即:

1
var buf Father = Child{}

是对的,但是

1
var buf *Father = new(Child)

却是不对的。应该改为:

1
2
var buf Father = Child{}
var pointer *Father = &buf

要想让问题最开始的代码编译通过要将以上代码修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import (
	"fmt"
)

type Father interface {
	Hello()
}


type Child struct {
	Name string
}

func (s Child)Hello()  {

}

func main(){
	var buf  Father
	buf = Child{}
	f(&buf)
}
func f(out *Father){
	if out != nil{
		fmt.Println("surprise!")
	}
}

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sinat_38068807/article/details/104012833

智能推荐

不是阿里P8级大佬,岂能错过这篇MySQL运维内参?啃透涨薪so easy-程序员宅基地

文章浏览阅读176次。写在前面MySQL被设计为一个可移植的数据库,几乎在当前所有系统上都能运行,如Linux、Solaris、 FreeBSD、 Mac和Windows。尽管各平台在底层(如线程)实现方面都各有不同,但是MySQL基本上能保证在各平台上的物理体系结构的一致性。因此,用户应该能很好地理解MySQL数据库在所有这些平台上是如何运作的。由于工作的缘故,笔者的大部分时间需要与开发人员进行数据库方面的沟通,并对他们进行培训。不论他们是DBA,还是开发人员,似乎都对MySQL的体系结构了解得不够透彻。很多人喜欢把M_mysql运维内参

百度正用谷歌AlphaGo,解决一个比围棋更难的问题 | 300块GPU在燃烧-程序员宅基地

文章浏览阅读382次。晓查 发自 凹非寺量子位 报道 | 公众号 QbitAI9102年,人类依然不断回想起围棋技艺被AlphaGo所碾压的恐怖。却也有不以为然的声音:只会下棋的AI,再厉害..._alpha go训练用了多少个gpu

docker 容器 设置网络代理_docker export http_proxy-程序员宅基地

文章浏览阅读3.3k次。docker 容器 设置网络代理以/bin/bash 形式进入容器:【设置http 及https代理】,如下:export http_proxy=http://172.16.0.20:3128export https_proxy=https://172.16.0.20:3128要取消该设置:unsethttp_proxyunset https_proxy..._docker export http_proxy

linux之笔记_linux 0775十六進制-程序员宅基地

文章浏览阅读263次。授课环境: 结束程序运行: ctrl + c 共享目录(工作目录): /kyo /Videos 访问共享目录流程: 是否能连通服务器 ping 3.3.3.9 是否服务器开启共享 showmount -e 3.3.3.9 挂载共享目录到本地: _linux 0775十六進制

普通屏幕已过时?裸眼3D屏幕显示效果更胜一筹!

与普通屏幕中播放的视频相对,裸眼3D屏幕需要先将裸眼3D视频分成两部分,分别呈现在左右两个视窗上,因此后者需要更高的分辨率,以及更精细的图像处理能力,以此使裸眼3D屏幕的画面展示效果更加细腻,进而加深每个物体和场景的深度感和空间感,让每个驻足于此的观众惊叹于裸眼3D屏幕的震撼视觉效果。另外,裸眼3D屏幕的色彩表现,也比大多的普通屏幕更加丰富和鲜艳,能够展现出电影级别的画面质量,总而言之,裸眼3D屏幕比之普通屏幕的显示效果,有着巨大的优势,这也是使裸眼3D成为重要显示技术的重要原因!

如何安全可控的进行跨区域数据交换,提高数据价值?

飞驰云联是中国领先的数据安全传输解决方案提供商,长期专注于安全可控、性能卓越的数据传输技术和解决方案,公司产品和方案覆盖了跨网跨区域的数据安全交换、供应链数据安全传输、数据传输过程的防泄漏、FTP的增强和国产化替代、文件传输自动化和传输集成等各种数据传输场景。飞驰云联主要服务于集成电路半导体、先进制造、高科技、金融、政府机构等行业的中大型客户,现有客户超过500家,其中500强和上市企业150余家,覆盖终端用户超过40万,每年通过飞驰云联平台进行数据传输和保护的文件量达到4.4亿个。

随便推点

OpenHarmony语言基础类库【@ohos.util.List (线性容器List)】

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点。

[自学笔记] ESP32-C3 Micropython初次配置

2、本次测试了两款IDE,分别是"thonny-4.1.4.exe"和"uPyCraft-v1.0.exe"。Thonny支持中文及多语言。而uPyCraft-v1.0只支持英文语言。因此入门时选用了Thonny作为IDE。(注:1、测试过程中IDE正常连接ESP32C3简约版的虚拟串口。不受简约版无串口芯片的影响。

初识Electron,创建桌面应用

古有匈奴犯汉,晋室不纲,铁木夺宋,虏清入关,神舟陆沉二百年有余,中国之见灭于满清初非满人能灭之,能有之也因有汉奸以作虎怅,残同胞媚异种,始有吴三桂洪承畴,继有曾国藩袁世凯以为厉。今率堂堂之师,征讨汉贼袁氏筑共和之体,或免于我子子孙孙被异族奴役。---- 《讨汉贼袁世凯檄文》- DOMContentLoaded事件:此时浏览器已经完全加载了HTML文件,并且DOM树已经生成好了。- Load事件:此时浏览器已经将所有的资源都加载完毕,可以正确读取页面中的资源。补充知识:Electron 生命周期。

Xcode 15构建问题

将ENABLE_USER_SCRIPT_SANDBOXING设为“no”即可!

OpenVINO应用案例:部署YOLO模型到边缘计算摄像头_openvino yolo-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏23次。一、实现路径通过OpenVINO部署YOLO模型到边缘计算摄像头,其实现路径为:训练(YOLO)->转换(OpenVINO)->部署运行(OpenNCC)。二、具体步骤1、训练YOLO模型1.1 安装环境依赖有关安装详情请参阅 https://github.com/AlexeyAB/darknet#requirements-for-windows-linux-and-macos 。1.2 编译训练工具git clone https://github.com/AlexeyAB/da_openvino yolo

数据污染对大型语言模型的潜在影响

总之,数据污染在LLMs中构成一个潜在的重要问题,可能影响它们在各种任务中的性能。它可能导致结果偏倚并削弱LLMs的真实有效性。通过识别和减轻数据污染,我们可以确保LLMs运行良好并产生准确的结果。现在是技术社区优先考虑数据完整性在LLMs的开发和利用中的时候了。通过这样做,我们可以确保LLMs产生无偏见且可靠的结果,这对于新技术和人工智能的发展至关重要。