[]文字列を[]インターフェース{}に変換できません


96

私はいくつかのコードを書いていますが、引数をキャッチして渡すためfmt.Println
に必要です(スペースで区切られ、改行が続く引数を書き込むというデフォルトの動作が必要です)。ただし、時間[]interface {}がかかりますが、をflag.Args()返します[]string
コード例は次のとおりです。

package main

import (
    "fmt"
    "flag"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args()...)
}

これにより、次のエラーが返されます。

./example.go:10: cannot use args (type []string) as type []interface {} in function argument

これはバグですか?べきではないfmt.Println取る任意の配列を?ちなみに、私もこれをやろうとしました:

var args = []interface{}(flag.Args())

しかし、次のエラーが発生します。

cannot convert flag.Args() (type []string) to type []interface {}

これを回避する「実行」方法はありますか?


1
簡単な例(go run test.go some test flags)をいじっていましたが、flags.Args()...justに変更すると機能するようでしたflag.Args()(出力は[some test flags]、改行が続きます。実際のフラグの登録でも機能するようです)。理由を理解するふりをしないでください、そしてスティーブンの答えはとにかくはるかに有益です:)
RocketDonkey 2012年

回答:


115

これはバグではありません。タイプfmt.Println()が必要[]interface{}です。つまり、interface{}「任意のスライス」ではなく、値のスライスである必要があります。スライスを変換するには、各要素をループしてコピーする必要があります。

old := flag.Args()
new := make([]interface{}, len(old))
for i, v := range old {
    new[i] = v
}
fmt.Println(new...)

スライスを使用できない理由は、a[]stringとaの間の変換で[]interface{}はメモリレイアウトを変更する必要があり、O(n)時間で発生するためです。型をに変換するには、interface{}O(1)時間が必要です。これをforループで不要にした場合でも、コンパイラはそれを挿入する必要があります。


2
ちなみに、このリンクはgolang-nutsにあります:groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion
cruizh

2
はい、各反復にはO(1)時間が必要であり、ループにはO(n)時間が必要です。それが私が言ったことです。を受け取る関数については[]string、を期待しinterface{}ます。ANは、interface{}異なるメモリレイアウトを有するstring各要素が変換する必要があるという事実が問題であるように。
スティーブンワインバーグ

1
@karlrh:いいえ、Println関数がスライスを変更し、いくつかの要素を設定するとします(変更しませんが、変更するとします)。次にinterface{}、スライスに任意のものを入れることができます。スライスにはstringsのみが含まれている必要があります。本当に必要なのはJavaGenericsワイルドカードのようなものですがSlice<? extends []interface{}>、それはGoには存在しません。
newacct 2012年

4
追加は魔法です。組み込みであり、言語によって特別に扱われます。他の例としてはnew()len()、とcopy()golang.org/ref/spec#Appending_and_copying_slices
Stephen Weinberg

1
今日、私は「新しい」が予約キーワードではないことを学びました!作ることもできません!
Sridhar 2015

11

印刷する文字列のスライスだけの場合は、以下を結合することで、変換を回避し、まったく同じ出力を得ることができます。

package main

import (
    "fmt"
    "flag"
    "strings"
)

func main() {
    flag.Parse()
    s := strings.Join(flag.Args(), " ")
    fmt.Println(s)
}

11

この場合、型変換は不要です。flag.Args()値をに渡すだけfmt.Printlnです。


質問:

[]文字列を[]インターフェース{}に変換できません

私はいくつかのコードを書いていますが、引数をキャッチしてfmt.Printlnに渡すために必要です(スペースで区切られ、その後に改行が続く引数を書き込むデフォルトの動作が必要です)。

コード例は次のとおりです。

package main

import (
    "fmt"
    "flag"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args()...)
}

パッケージフラグ

import "flag"

func Args

func Args() []string

Args フラグ以外のコマンドライン引数を返します。


パッケージfmt

import "fmt"

func Println

func Println(a ...interface{}) (n int, err error)

Printlnオペランドにデフォルトのフォーマットを使用してフォーマットし、標準出力に書き込みます。オペランド間には常にスペースが追加され、改行が追加されます。書き込まれたバイト数と発生した書き込みエラーを返します。


この場合、型変換は不要です。flag.Args()値をfmt.Printlnに渡すだけ[]stringです。これは、リフレクションを使用して値を型として解釈します。パッケージreflectは実行時リフレクションを実装し、プログラムが任意のタイプのオブジェクトを操作できるようにします。例えば、

args.go

package main

import (
    "flag"
    "fmt"
)

func main() {
    flag.Parse()
    fmt.Println(flag.Args())
}

出力:

$ go build args.go
$ ./args arg0 arg1
[arg0 arg1]
$ 

角かっこを省略したい場合でも、変換は不要ですか?
cruizh 2018

1

Goでは、関数は関数定義のパラメーターリストで指定されたタイプの引数のみを受け入れることができます。可変個引数パラメータ言語機能はそれを少し複雑にしますが、明確に定義されたルールに従います。

の関数シグネチャfmt.Printlnは次のとおりです。

func Println(a ...interface{}) (n int, err error)

パー言語仕様

関数シグニチャの最後の入力パラメータには、接頭辞...が付いた型があります。このようなパラメータを持つ関数は可変個引数と呼ばれ、そのパラメータに対して0個以上の引数を指定して呼び出すことができます。

これはPrintlninterface{}型の引数のリストを渡すことができることを意味します。すべての型が空のインターフェイスを実装しているため、任意の型の引数のリストを渡すことができます。これPrintln(1, "one", true)により、たとえば、エラーなしで呼び出すことができます。言語仕様の「...パラメータへの引数の受け渡し」セクションを参照してください。

渡される値は、タイプ[] Tの新しいスライスであり、新しい基になる配列があり、その連続する要素は実際の引数であり、すべてTに割り当てることができる必要があります。

問題を引き起こしている部分は、仕様の直後です。

最後の引数がスライスタイプ[] Tに割り当て可能である場合、引数の後に...が続くと、... Tパラメータの値として変更されずに渡される可能性があります。この場合、新しいスライスは作成されません。

flag.Args()タイプ[]stringです。以来TではPrintlnあるinterface{}[]Tです[]interface{}。したがって、問題は、文字列スライス値がインターフェイススライスタイプの変数に割り当て可能かどうかにあります。割り当てを試みることで、goコードでそれを簡単にテストできます。次に例を示します。

s := []string{}
var i []interface{}
i = s

このような割り当てを試みると、コンパイラは次のエラーメッセージを出力します。

cannot use s (type []string) as type []interface {} in assignment

そのため、文字列スライスの後の省略記号を引数として使用することはできません。 fmt.Println。これはバグではなく、意図したとおりに機能しています。

あなたが印刷できる方法はたくさん残っていflag.Args()Printlnなどは、

fmt.Println(flag.Args())

fmtパッケージのドキュメント[elem0 elem1 ...]ごとに、として出力されます

または

fmt.Println(strings.Join(flag.Args(), ` `)

(それぞれが単一のスペースで区切られた文字列スライス要素を出力します)たとえば、文字列区切り文字を使用して文字列パッケージの結合関数を使用ます。


0

リフレクションを使えば可能だと思いますが、それが良い解決策かどうかはわかりません

package main

import (
    "fmt"
    "reflect"
    "strings"
)

type User struct {
    Name string
    Age  byte
}

func main() {
    flag.Parse()
    fmt.Println(String(flag.Args()))
    fmt.Println(String([]string{"hello", "world"}))
    fmt.Println(String([]int{1, 2, 3, 4, 5, 6}))
    u1, u2 := User{Name: "John", Age: 30},
        User{Name: "Not John", Age: 20}
    fmt.Println(String([]User{u1, u2}))
}

func String(v interface{}) string {
    val := reflect.ValueOf(v)
    if val.Kind() == reflect.Array || val.Kind() == reflect.Slice {
        l := val.Len()
        if l == 0 {
            return ""
        }
        if l == 1 {
            return fmt.Sprint(val.Index(0))
        }
        sb := strings.Builder{}
        sb.Grow(l * 4)
        sb.WriteString(fmt.Sprint(val.Index(0)))
        for i := 1; i < l; i++ {
            sb.WriteString(",")
            sb.WriteString(fmt.Sprint(val.Index(i)))
        }
        return sb.String()
    }

    return fmt.Sprintln(v)
}

出力:

$ go run .\main.go arg1 arg2
arg1,arg2
hello,world
1,2,3,4,5,6
{John 30},{Not John 20}

-1

fmt.Println可変個引数パラメータを取ります

func Println(a ... interface {})(n int、err error)

flag.Args()変換せずに印刷することが可能[]interface{}

func main() {
    flag.Parse()
    fmt.Println(flag.Args())
}

2
fmt.Printlnの署名は6年以上変更されていません(これはのパッケージ変更にすぎませんでしたerror)。たとえそれがあったとしても、仕様は「タイプ... Tの最終パラメータpを持つ可変個引数」と明確に述べています。f内では、pのタイプはタイプ[] Tと同等であるため、問題ではありません。fmt.Println(flags.Args()...)それでも機能しません(スライス拡張がありません)、fmt.Println(flags.Args())常に機能します。
マーク

fmt.Printlnの署名が6年以上変更されていないというこの情報をどのようにして入手しましたか?ソースコードを確認しましたか?
Shahriar


作戦に関するコメントは、当時を取り戻すflag.Args()ための議論としてのみ使用することを示唆しfmt.Printlnています。(その時にそうでなかったら驚くでしょう)
葉のビバップ2018年

そう?コメントがある場合、それは私の答えが反対票を投じられるべきであることを意味しますか?
Shahriar 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.