为什么我不能做fmt.Sprintf("%d。%d。%d。%d",a ...)?

时间:2015-10-29 15:17:28

标签: go

我正在学习Go和我一直坚持Go巡演(exercise-stringer.go:https://tour.golang.org/methods/7)。

这里有一些代码:

type IPAddr [4]byte  
// TODO: Add a "String() string" method to IPAddr.
func (a IPAddr) String() string {
    return fmt.Sprintf("%d.%d.%d.%d", a...)
}

所以我认为IPAddr的内部表示是[4]byte,因此传播运算符起作用。但是我得到了:

cannot use []string literal (type []string) as type []interface {} in argument to fmt.Sprintf

到底是什么?字符串切片也不起作用,这里发生了什么?

编辑:抱歉,我的问题出现了错误 - 错误与IPAddr类型有关,而不是[]string。我正在玩代码而且我粘贴了错误的输出。无论如何,感谢peterSO0x434D53关于Go中切片的不变性。

嗯,这提出了另一个问题。为什么以这种方式实施?我想你只是有一些Iterable接口,所以任何实现它的结构都会#34;只是工作"。

旁注:当我第一次听说Go时,有一个大胆的声明"编译,但富有表现力"。显式接口实现就是这方面的一个很好的例子,但是显式转换,缺少运算符重载等等都会给我带来Java感觉" 90。这很难过,因为Go看起来很棒。

7 个答案:

答案 0 :(得分:8)

  

https://developer.apple.com/library/prerelease/tvos/documentation/GraphicsImaging/Reference/CGPDFPage/index.html

     

练习:Stringers

     

使IPAddr类型工具fmt.Stringer将地址打印为   点缀四边形。

     

例如,IPAddr{1, 2, 3, 4}应打印为"1.2.3.4"。

package main

import "fmt"

type IPAddr [4]byte

// TODO: Add a "String() string" method to IPAddr.

func main() {
  addrs := map[string]IPAddr{
      "loopback":  {127, 0, 0, 1},
      "googleDNS": {8, 8, 8, 8},
  }
  for n, a := range addrs {
      fmt.Printf("%v: %v\n", n, a)
  }
}

没有[]string[]interface {}的隐式转换。请参阅A Tour of Go中的Conversions。您需要提供显式转换。例如,

package main

import "fmt"

type IPAddr [4]byte

// A "String() string" method for IPAddr.
func (a IPAddr) String() string {
    return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3])
}

func main() {
    addrs := map[string]IPAddr{
        "loopback":  {127, 0, 0, 1},
        "googleDNS": {8, 8, 8, 8},
    }
    for n, a := range addrs {
        fmt.Printf("%v: %v\n", n, a)
    }
}

输出:

loopback: 127.0.0.1
googleDNS: 8.8.8.8

答案 1 :(得分:3)

正如go faq中所述,没有从类型数组到[]interface {}的隐式转换。

以下解决方案有效但需要创建中间切片:

func (ip IPAddr) String() string {
    tmp := make([]interface{}, len(ip))
    for i, val := range ip {
        tmp[i] = val
    }
    return fmt.Sprintf("%d.%d.%d.%d", tmp...)
}

答案 2 :(得分:2)

从go语言规范:

  

如果f是可变参数且最终参数p的类型为...... T,那么在f中p的类型等同于类型[] T

但是在Go切片和数组中类型是不变的。因此[]T[]U不同,如果TU是不同的类型。它们完全没有关系,即使TU的结构子类型。因此[]string不是[]interface

答案 3 :(得分:0)

您需要为Stringer接口实现此方法。

func (ip IPAddr) String() string {
    return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3])
}

答案 4 :(得分:0)

应该使用range来迭代数组,所以答案如下:

func (ip IPAddr) String() string {
    out := fmt.Sprintf("%v", ip[0])
    for _, value := range ip[1:] {
        out += fmt.Sprintf(".%v", value)
    }
    return out
}

答案 5 :(得分:0)

func (ip IPAddr) String() string {
    var s []string;
    for _, v := range ip {
        s = append(s, fmt.Sprintf("%v", v))
    }
    return strings.Join(s, ".")
}

答案 6 :(得分:-2)

首先,当我'跑'时: 包主要

导入“fmt”

type IPAddr [4]byte

// TODO: Add a "String() string" method to IPAddr.
func (a IPAddr) String() string {
    return fmt.Sprintf("%d.%d.%d.%d", a...)
}

func main() {
    addrs := map[string]IPAddr{
        "loopback":  {127, 0, 0, 1},
        "googleDNS": {8, 8, 8, 8},
    }
    for n, a := range addrs {
        fmt.Printf("%v: %v\n", n, a)
    }
}

错误是:

  

prog.go:9:不能使用(类型IPAddr)作为类型[] interface {}   fmt.Sprintf的参数

而不是

  

不能使用[] string literal(type [] string)作为类型[] interface {}   fmt.Sprintf的参数

所以,我认为复制和粘贴时会出现一些不同步的现象。

type IPAddr [4]byte没有定义字符串,因此问题中的错误消息会产生误导。

它是一个[4]byte,一个完全不同的类型(从Go语言类型的角度来看)来自字符串。它也不是[]byte

type IPAddr [4]byte也不满足fmt.Sprintf可以使用的接口,例如实现String(),因为IPAddr的String()方法不会被编译。

您可能会尝试将[4]byte转换为字符串,但转换string(a)不合法。更糟糕的是,四字节值将被视为字符代码,而不会转换为4个小整数值的字符表示。一些IPAddr字节值很可能是无效的UTF-8,如果程序试图打印它会更加奇怪。

如其他答案所述,
return fmt.Sprintf("%d.%d.%d.%d", a[0], a[1], a[2], a[3])
以您要瞄准的格式返回IPAddr的字符串值。

一旦func (a IPAddr) String() string有效,它就有效; IPAddr实现了fmt.Stringer接口。

然后%vfmt.Printf("%v: %v\n", n, a)
 可以由中的%s替换 fmt.Printf("%s: %s\n", n, a)
因为fmt输出方法具有String()的实现。

我更喜欢%s%v因为它表示程序不依赖于'默认Go值表示'(即对于该数组[127 0 0 1]),并且该类型实现串()。