英文:
strconv.Itoa does not accept values of type int64
问题
我正在学习使用Go创建REST API。以下是我遇到的问题。
当用户发送一个CREATE请求时:
- 从文章切片中取出最后一篇文章
- 将ID(原本是字符串)转换为整数
- 增加整数的值并将其转换回字符串并保存
文章结构体
type Article struct {
Id string `json:"id"`
Title string `json:"title"`
Desc string `json:"desc"`
Content string `json:"content"`
}
以下是逻辑代码
// 获取最后一个id并将其转换为整数并增加
lastId, err := strconv.ParseInt(Articles[len(Articles) - 1].Id, 10, 64)
lastId = lastId + 1
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
response := []Article{
{
Id: strconv.Itoa(int(lastId)),// 错误
Title: articleBody.Title,
Desc: articleBody.Desc,
Content: articleBody.Content,
},
}
错误
无法将类型为int64的lastId用作strconv.Itoa函数的int参数(IncompatibleAssign)
英文:
Im learning to create REST APIs using Go. Here's where I am stuck.
> When user sends a CREATE request:
- From the Slice of articles, I need to take the last article
- Convert the ID(originally string) to Integer
- Increment the Integer and convert it back to string and save it
Article Struct
type Article struct {
Id string `json:"id"`
Title string `json:"title"`
Desc string `json:"desc"`
Content string `json:"content"`
}
Here's the logic
// get the last id and convert it to integer and increment
lastId, err := strconv.ParseInt(Articles[len(Articles) - 1].Id, 10, 64)
lastId = lastId + 1
if err != nil {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
response := []Article{
{
Id: strconv.Itoa(lastId),// 👈 ERROR
Title: articleBody.Title,
Desc: articleBody.Desc,
Content: articleBody.Content,
},
}
<h3>ERROR</h3>
cannot use lastId (variable of type int64) as int value
in argument to strconv.Itoa compiler (IncompatibleAssign)
答案1
得分: 5
Go语言具有强类型系统,因此Int32和Int64不是兼容的类型。在调用Itoa时,尝试将lastId
转换为int
类型:
response := []Article{
{
Id: strconv.Itoa(int(lastId)),
Title: articleBody.Title,
Desc: articleBody.Desc,
Content: articleBody.Content,
},
}
编辑:
正如@kostix在他的答案中提到的,当转换int类型时要注意溢出问题(有关详细信息,请参阅他的答案)。
更安全的解决方案可能是这样的:
newId := int(lastId)
if int64(newId) != lastId {
panic("overflows!")
}
response := []Article{
{
Id: strconv.Itoa(newId),
Title: articleBody.Title,
Desc: articleBody.Desc,
Content: articleBody.Content,
},
}
英文:
Go has a strong typing system so Int32 and Int64 are not compatible type. Try to convert lastId
into an int
when calling Itoa:
response := []Article{
{
Id: strconv.Itoa(int(lastId)),
Title: articleBody.Title,
Desc: articleBody.Desc,
Content: articleBody.Content,
},
}
Edit:
As mention by @kostix in his answer, beware of overflows when converting int type (see his answer for details).
A safer solution would be something like this:
newId := int(lastId)
if int64(newId) != lastId {
panic("overflows!")
}
response := []Article{
{
Id: strconv.Itoa(newId),
Title: articleBody.Title,
Desc: articleBody.Desc,
Content: articleBody.Content,
},
}
答案2
得分: 2
语言规范中指出:
uint
可以是32位或64位int
与uint
大小相同
这意味着,在特定的平台/Go版本上,int
的大小可能与 int32
相同,这就是为什么 Go 不会默默地允许你将 int64
类型的值作为 int
类型的参数传递的原因。
此外,另一个答案中建议的简单类型转换 int(lastId)
应该谨慎对待:当你的程序被编译并且在编译后的代码中 int
的大小为32位时,如果特定的 lastId
值超出了有符号32位整数支持的数字范围,比如 2,147,483,648,会发生什么呢?
再次,规范中指出:
- 在整数类型之间进行转换时,如果值是有符号整数,则进行符号扩展以达到隐式的无限精度;否则进行零扩展。然后将其截断以适应结果类型的大小。例如,如果
v := uint16(0x10F0)
,那么uint32(int8(v)) == 0xFFFFFFF0
。转换总是产生一个有效的值;没有溢出的指示。
因此,下面的代码
var i64 int64 = 2_147_483_648
var i32 = int32(i64)
fmt.Println(i32)
输出
-2147483648
当这个值传递给 strconv.Itoa
时,它返回 "-2147483648",这可能不是你期望的结果。
因此,在健壮的代码中,当进行此类类型转换时,你应该小心,并且要么检查转换后的值是否合理,例如:
v := int(lastId)
if int64(v) != lastId {
panic("ouch!")
}
或者仅仅使用最大方便的类型,通过 strconv.FormatInt
。
英文:
The language specification says:
> uint
either 32 or 64 bits
> int
same size as uint
This means, on a particular platform/version of Go int
may be the same size as int32
, and this is the reason why Go would not silently allow you to pass a value of type int64
as an argument of type int
.
Moreover, a plain type conversion int(lastId)
suggested in another answer should be taken with a grain of salt: what happens when your program is compiled and int
ends up having 32 bits in size in the compiled code, and a particular lastId
value is outside the number range supported by a signed 32-bit integer, say, 2,147,483,648?
Again, the spec says:
> When converting between integer types, if the value is a signed integer, it is sign extended to implicit infinite precision; otherwise it is zero extended. It is then truncated to fit in the result type's size. For example, if v := uint16(0x10F0)
, then uint32(int8(v)) == 0xFFFFFFF0
. The conversion always yields a valid value; there is no indication of overflow.
Hence the code
var i64 int64 = 2_147_483_648
var i32 = int32(i64)
fmt.Println(i32)
prints
-2147483648
And when this value is passed to strconv.Itoa
, it returns "-2147483648" — quite possibly not what you would expect.
So, in a robust code, you ought to watch out when doing such type conversions, and either check the converted value for sanity, like in
v := int(lastId)
if int64(v) != lastId {
panic("ouch!")
}
or merely use the largest convenient type via strconv.FormatInt
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论