Я пишу код, и он мне нужен, чтобы перехватывать аргументы и передавать их 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 {}
Есть ли способ обхода этой проблемы?
type-conversion
go
круиз
источник
источник
go run test.go some test flags
), и, похоже, он работал при измененииflags.Args()...
на простоflag.Args()
(вывод[some test flags]
, за которым следует новая строка; также, похоже, работал с регистрацией фактических флагов). Не буду делать вид, что понимает почему, и ответ Стивена в любом случае более информативен :)Ответы:
Это не ошибка.
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 ненужным, компилятору все равно пришлось бы его вставить.источник
[]string
, она ожидает ainterface{}
. Структураinterface{}
памяти у An отличается от структуры памяти,string
поэтому проблема заключается в том, что каждый элемент необходимо преобразовать.Println
функция изменяет срез и устанавливает некоторые элементы (это не так, но предположим, что это так). Затем он может положить любойinterface{}
кусочек в ломтик, на котором должно быть толькоstring
s. На самом деле вам нужно что-то вроде подстановочного знака Java GenericsSlice<? extends []interface{}>
, но этого нет в Go.new()
,len()
иcopy()
. golang.org/ref/spec#Appending_and_copying_slicesЕсли вы хотите напечатать только часть строк, вы можете избежать преобразования и получить точно такой же результат, присоединившись:
package main import ( "fmt" "flag" "strings" ) func main() { flag.Parse() s := strings.Join(flag.Args(), " ") fmt.Println(s) }
источник
В этом случае преобразование типа не требуется. Просто передайте
flag.Args()
значение вfmt.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] $
источник
В Go функция может принимать аргументы только тех типов, которые указаны в списке параметров в определении функции. Функция языка с вариативными параметрами немного усложняет это, но следует четко определенным правилам.
Сигнатура функции
fmt.Println
:func Println(a ...interface{}) (n int, err error)
В зависимости от языковой специфики ,
Это означает, что вы можете передать
Println
список аргументовinterface{}
типа. Поскольку все типы реализуют пустой интерфейс, вы можете передать список аргументов любого типа,Println(1, "one", true)
например, таким образом вы можете вызывать его без ошибок. См. Раздел «Передача аргументов в ... параметры» спецификации языка:Часть, которая вызывает у вас проблемы, находится сразу после этого в спецификации:
flag.Args()
это тип[]string
. ПосколькуT
inPrintln
isinterface{}
,[]T
is[]interface{}
. Таким образом, вопрос сводится к тому, можно ли присвоить строковое значение среза переменной типа среза интерфейса. Вы можете легко проверить это в своем коде Go, попробовав присвоение, например:s := []string{} var i []interface{} i = s
Если вы попытаетесь выполнить такое назначение, компилятор выдаст это сообщение об ошибке:
cannot use s (type []string) as type []interface {} in assignment
Вот почему вы не можете использовать многоточие после отрезка строки в качестве аргумента
fmt.Println
. Это не ошибка, работает как задумано.Есть еще много способов , вы можете напечатать
flag.Args()
сPrintln
, например,(который будет выводиться как
[elem0 elem1 ...]
в документации пакета fmt )или
fmt.Println(strings.Join(flag.Args(), ` `)
(который будет выводить элементы среза строки, каждый из которых разделен одним пробелом), например, с помощью функции Join в пакете строк с разделителем строк.
источник
Я думаю, что можно использовать отражение, но я не знаю, хорошее ли это решение
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}
источник
fmt.Println
принимает вариативный параметрВозможна печать
flag.Args()
без преобразования в[]interface{}
func main() { flag.Parse() fmt.Println(flag.Args()) }
источник
fmt.Println
подпись не менялась более 6 лет (и это было просто изменение пакета дляerror
). Даже если бы это было так, в спецификации ясно сказано: "Variadic с конечным параметром p типа ... T, тогда внутри f тип p эквивалентен типу [] T.", так что это не имеет значения.fmt.Println(flags.Args()...)
по-прежнему не работает (вам не хватает расширения среза),fmt.Println(flags.Args())
всегда работал.flag.Args()
исключительно в качестве аргументаfmt.Println
сработало в то время. (Было бы удивительно, если бы этого не произошло в то время)