Saya sedang menulis beberapa kode, dan saya membutuhkannya untuk menangkap argumen dan meneruskannya fmt.Println
(saya ingin perilaku default-nya, untuk menulis argumen yang dipisahkan oleh spasi dan diikuti oleh baris baru). Namun yang dibutuhkan []interface {}
tetapi flag.Args()
mengembalikan a []string
.
Berikut contoh kodenya:
package main
import (
"fmt"
"flag"
)
func main() {
flag.Parse()
fmt.Println(flag.Args()...)
}
Ini mengembalikan kesalahan berikut:
./example.go:10: cannot use args (type []string) as type []interface {} in function argument
Apakah ini bug? Tidak harus fmt.Println
mengambil array apapun ? Ngomong-ngomong, saya juga mencoba melakukan ini:
var args = []interface{}(flag.Args())
tapi saya mendapatkan error berikut:
cannot convert flag.Args() (type []string) to type []interface {}
Apakah ada cara "Mulai" untuk mengatasinya?
type-conversion
go
cruizh
sumber
sumber
go run test.go some test flags
), dan tampaknya berfungsi ketika mengubahflags.Args()...
menjadi hanyaflag.Args()
(keluaran adalah[some test flags]
, diikuti oleh baris baru; juga tampaknya bekerja dengan mendaftarkan bendera yang sebenarnya). Tidak akan berpura-pura mengerti mengapa, dan jawaban Stephen jauh lebih informatif :)Jawaban:
Ini bukan bug.
fmt.Println()
membutuhkan[]interface{}
tipe. Artinya, itu harus berupa potonganinterface{}
nilai dan bukan "potongan apa pun". Untuk mengonversi potongan, Anda perlu mengulang dan menyalin setiap elemen.old := flag.Args() new := make([]interface{}, len(old)) for i, v := range old { new[i] = v } fmt.Println(new...)
Alasan Anda tidak dapat menggunakan potongan apa pun adalah karena konversi antara a
[]string
dan a[]interface{}
memerlukan tata letak memori untuk diubah dan terjadi dalam waktu O (n). Mengubah tipe menjadi aninterface{}
membutuhkan waktu O (1). Jika mereka membuat ini untuk loop tidak diperlukan, kompilator masih perlu memasukkannya.sumber
[]string
, mengharapkan ainterface{}
. Aninterface{}
memiliki tata letak memori yang berbeda dari astring
sehingga fakta bahwa setiap elemen perlu diubah adalah masalahnya.Println
fungsi memodifikasi irisan, dan menyetel beberapa elemen (tidak, tetapi anggaplah demikian). Kemudian ia bisa memasukkan apapuninterface{}
ke dalam irisan, yang seharusnya hanya memilikistring
s. Yang Anda inginkan sebenarnya adalah wildcard Java GenericsSlice<? extends []interface{}>
, tetapi itu tidak ada di Go.new()
,len()
, dancopy()
. golang.org/ref/spec#Appending_and_copying_slicesJika itu hanya sepotong string yang ingin Anda cetak, Anda dapat menghindari konversi dan mendapatkan hasil yang sama persis dengan bergabung:
package main import ( "fmt" "flag" "strings" ) func main() { flag.Parse() s := strings.Join(flag.Args(), " ") fmt.Println(s) }
sumber
Dalam kasus ini, konversi tipe tidak diperlukan. Cukup teruskan
flag.Args()
nilainya kefmt.Println
.Dalam kasus ini, konversi tipe tidak diperlukan. Cukup teruskan
flag.Args()
nilainya kefmt.Println
, yang menggunakan refleksi untuk menafsirkan nilai sebagai tipe[]string
. Packagereflect
mengimplementasikan refleksi run-time, memungkinkan program untuk memanipulasi objek dengan tipe arbitrer. Sebagai contoh,args.go
:package main import ( "flag" "fmt" ) func main() { flag.Parse() fmt.Println(flag.Args()) }
Keluaran:
$ go build args.go $ ./args arg0 arg1 [arg0 arg1] $
sumber
Dalam Go, sebuah fungsi hanya dapat menerima argumen dari tipe yang ditentukan dalam daftar parameter dalam definisi fungsi. Fitur bahasa parameter variadic sedikit memperumitnya, tetapi mengikuti aturan yang ditentukan dengan baik.
Tanda tangan fungsi untuk
fmt.Println
adalah:func Println(a ...interface{}) (n int, err error)
Sesuai dengan spesifikasi bahasa ,
Ini berarti Anda dapat memberikan
Println
daftarinterface{}
tipe argumen . Karena semua tipe mengimplementasikan antarmuka kosong, Anda bisa meneruskan daftar argumen tipe apa pun, begitulah cara Anda memanggilPrintln(1, "one", true)
, misalnya, tanpa kesalahan. Lihat bagian "Meneruskan argumen ke ... parameter" pada spesifikasi bahasa:Bagian yang memberi Anda masalah ada tepat setelah itu dalam spesifikasi:
flag.Args()
adalah tipe[]string
. SejakT
diPrintln
adalahinterface{}
,[]T
adalah[]interface{}
. Jadi pertanyaannya adalah apakah nilai potongan string dapat ditetapkan ke variabel jenis potongan antarmuka. Anda dapat dengan mudah mengujinya di kode go Anda dengan mencoba sebuah tugas, misalnya:s := []string{} var i []interface{} i = s
Jika Anda mencoba melakukan tugas seperti itu, kompilator akan menampilkan pesan kesalahan ini:
cannot use s (type []string) as type []interface {} in assignment
Dan itulah mengapa Anda tidak dapat menggunakan elipsis setelah potongan string sebagai argumen untuk
fmt.Println
. Ini bukan bug, ini berfungsi sebagaimana mestinya.Ada banyak masih dari cara Anda dapat mencetak
flag.Args()
denganPrintln
, seperti(yang akan ditampilkan sebagai
[elem0 elem1 ...]
, sesuai dokumentasi paket fmt )atau
fmt.Println(strings.Join(flag.Args(), ` `)
(yang akan mengeluarkan elemen potongan string, masing-masing dipisahkan oleh satu spasi) menggunakan fungsi Gabung dalam paket string dengan pemisah string, misalnya.
sumber
Saya pikir itu mungkin menggunakan refleksi, tetapi saya tidak tahu apakah itu solusi yang baik
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) }
Keluaran:
$ go run .\main.go arg1 arg2 arg1,arg2 hello,world 1,2,3,4,5,6 {John 30},{Not John 20}
sumber
fmt.Println
mengambil parameter variadicMungkin untuk mencetak
flag.Args()
tanpa mengubahnya menjadi[]interface{}
func main() { flag.Parse() fmt.Println(flag.Args()) }
sumber
fmt.Println
tanda tangan tidak berubah selama lebih dari 6 tahun (dan itu hanya perubahan paket untukerror
). Bahkan jika ada, spesifikasi dengan jelas mengatakan `variadic dengan parameter akhir p tipe ... T, maka dalam f tipe p sama dengan tipe [] T.` jadi tidak masalah.fmt.Println(flags.Args()...)
masih tidak berfungsi (Anda kehilangan ekspansi slice),fmt.Println(flags.Args())
selalu berfungsi.flag.Args()
hanya sebagai argumen untukfmt.Println
bekerja di masa itu. (Akan mengherankan jika tidak pada saat itu)