PHP转Go之基础数据类型的坑

本文主要讲解Go中基础数据类型,字符串、整数、浮点数、布尔这几个类型,像slice、map、struct等类型会有单独文章讲解

本文概要

  • 1、要转换思维,尽可能的不再定义弱类型,比如Go里的interface{}
  • 2、务必要注意强类型里的默认值

一、基础类型对比

我们在很多文章中都能看到类似的说明,这里copy一份,仅供参考

语言\类型 boolean string int float array
PHP bool string int float 1,2,3/"a"=\>1
Go bool string int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64 float32、float64 slice/map

二、认知和使用差异点

  • 对于强类型语言只有类型相同的值才能够比较
php 复制代码
<?php
$a = 1;
$b = "1";
$c = true;
var_dump($a == $b);
var_dump($b == $c);
var_dump($a == $c);

// Output,当然php也支持强类型的判等比较 ===
bool(true)
bool(true)
bool(true)
go 复制代码
package main

import "fmt"

func main() {
	a := int32(1)
	b := int64(2)
	if a == b { // 这一行直接报错,根本无法编译过去
		fmt.Println("相等")
	}
}
  • 变量必须声明并初始化

在PHP中可以随时声明一个变量并赋予任意类型的任意值,这在Go中是行不通的,在强类型语言中,必须要提前声明变量类型,当然可以显示的声明,也可以是推导式的声明,并且Go中的所有类型都是有默认值的

go 复制代码
package main

import "fmt"

func main() {
	var a string // 默认空字符串
	var b int // 所有int都默认0
	var c float64 // 所有float64都默认0
	d := false // 推导出d是布尔值,bool值默认false
	fmt.Println(a, b, c, d)
   // b = "1" // 这种赋值会直接报错
}
// Output, 需注意string默认值为空字符串,所以未能有效输出展示
0 0 false

protobuf中也会利用默认值的特性,不传递变量是默认值的数据,减少传输数据量

  • 如何做类型转换

PHP中可以利用(int),(string)等方式直接完成类型强转,且不会报错,但是Go中就略显繁琐,不同的类型转到同一个类型可能使用的方法不同,比如:

go 复制代码
package main

import (
	"fmt"
	"strconv"
)

func main() {
	a := "1"
	b := 1.0
	var c interface{}
	c = "2"
	d, _ := strconv.Atoi(a)
	f, _ := strconv.Atoi(c.(string))
	fmt.Println(int(b)) // int和float之间可以直接转换,但是要注意范围溢出的问题
	fmt.Println(d, f)   // 字符串转数字需要使用特定函数,还要注意错误
   fmt.Printf("%d %.1f", bb, b) // 其他类型转字符串,使用格式化字符串函数,最简单直观
}

Go中使用类型转换最流行的一个库就是 spf13/cast 库了,强烈建议直接引用,不要自己造轮子,需要注意的是 cast 库避免了 panic,所以在很多转换过程中,对于失败的案例,返回值就变成了变量默认值,这个需要看下自己业务场景是否正常

  • Unicode字符处理

在php中,Unicode字符声明时并无特殊,而仅仅是在处理时,需要使用一些列以 mb_开头的函数

php 复制代码
<?php
$a = "我爱中国";
echo "字节长度:", strlen($a),"字符串长度:", mb_strlen($a);
// output: 字节长度:12字符串长度:4

而在Go中,Unicode对应的是一个类型 rune,而其各种操作函数跟之前没有区别 string 背后实际利用 []byte or []rune 实现,所以可以无缝互转

golang 复制代码
package main
import "fmt"
func main() {
	str := "我爱中国"
	fmt.Println("len", len(str), len([]rune(str)))
	fmt.Println(string([]rune(str)[1:])) // 如果这里不转为[]rune,则处理unicode字符时会出现乱码
}
// Output
len 12 4
爱中国

三、基础类型使用中的常见坑

  • json转换失败,引起异常

这里核心讲下类型引起的异常,其实强语言与js这种弱类型交互处理json坑真的很多

  1. "1" 和 1 是完全不同的类型,golang无法解析"1"为 1, 因类型不对应,直接报错
  2. {"key":null} 注意对于null,golang在解析时会解析成对应key声明的类型的默认值,而不一定是 nil
  3. 不要忽略 json.Unmarshal 的报错,某个字段无法解析报错时,可能会导致结构体内都是默认值,引起异常
  4. 使用 json.Unmarshal 或者 json.Marshal时,对于结构体中的小字字母开头的变量,处理时是忽略的,这个也可能会导致与预期不一致

json的处理值得写一篇单独文章来讲解,这里许个诺,稍后期待补充

  • float的精度问题

Golang中同样有浮点数精度问题的坑

golang 复制代码
package main

import (
	"fmt"
	"math/big"
)

func main() {
	f := 0.6
	ff := 0.7
	fmt.Println(f + ff)

	a, _ := new(big.Float).SetString("0.6")
	b, _ := new(big.Float).SetString("0.7")

	// 加法运算
	cccc := new(big.Float).Add(a, b)
	fmt.Println("Add:", cccc.String())
}
// Output
Add: 1.2999999999999998
Add: 1.3

可见对于高精度需求的计算时这还是有点问题的,Golang自带的math包可以有效的解决这个问题,另外推荐第三方库https://github.com/shopspring/decimal也能很好的解决这个问题,使用起来可能更方便一点~

下一章:讲解下 slicemap

相关推荐
Rust研习社1 小时前
组合真的优于继承吗?为什么 Rust 和 Go 都拥抱组合舍弃继承?
后端·rust·编程语言
IT_陈寒2 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
CaffeinePro3 小时前
Pydantic深度使用:数据校验、枚举、ORM映射
后端·fastapi
Chenyiax3 小时前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH3 小时前
Koa和Express的区别
后端
MariaH3 小时前
Koa框架的使用
后端
luckdewei4 小时前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某6 小时前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy6 小时前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom6 小时前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github