英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论