strconv.Itoa不接受int64类型的值。

huangapple go评论80阅读模式
英文:

strconv.Itoa does not accept values of type int64

问题

我正在学习使用Go创建REST API。以下是我遇到的问题。

当用户发送一个CREATE请求时:

  1. 从文章切片中取出最后一篇文章
  2. 将ID(原本是字符串)转换为整数
  3. 增加整数的值并将其转换回字符串并保存

文章结构体

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:

  1. From the Slice of articles, I need to take the last article
  2. Convert the ID(originally string) to Integer
  3. 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(&quot;overflows!&quot;)
}
response := []Article{
            {
                Id: strconv.Itoa(newId),
                Title:   articleBody.Title,
                Desc:    articleBody.Desc,
                Content: articleBody.Content,
            },
 }

答案2

得分: 2

语言规范中指出:

  • uint 可以是32位或64位
  • intuint 大小相同

这意味着,在特定的平台/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(&quot;ouch!&quot;)
}

or merely use the largest convenient type via strconv.FormatInt.

huangapple
  • 本文由 发表于 2021年8月27日 17:14:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/68950920.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定