Невозможно преобразовать [] строку в [] интерфейс {}

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()...на просто flag.Args()(вывод [some test flags], за которым следует новая строка; также, похоже, работал с регистрацией фактических флагов). Не буду делать вид, что понимает почему, и ответ Стивена в любом случае более информативен :)
RocketDonkey

Ответы:

116

Это не ошибка. 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 -uts: groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion
cruizh
2
Да, каждая итерация требует времени O (1), а цикл требует времени O (n). Так я сказал. Что касается функции, получающей a []string, она ожидает a interface{}. Структура interface{}памяти у An отличается от структуры памяти, stringпоэтому проблема заключается в том, что каждый элемент необходимо преобразовать.
Стивен Вайнберг
1
@karlrh: Нет, предположим, что Printlnфункция изменяет срез и устанавливает некоторые элементы (это не так, но предположим, что это так). Затем он может положить любой interface{}кусочек в ломтик, на котором должно быть только strings. На самом деле вам нужно что-то вроде подстановочного знака Java Generics Slice<? extends []interface{}>, но этого нет в Go.
newacct
4
Добавление волшебное. Он встроен и специально обрабатывается языком. Другие примеры включают в себя new(), len()и copy(). golang.org/ref/spec#Appending_and_copying_slices
Стивен Вайнберг
1
Сегодня я узнал, что «новое» - не резервное ключевое слово! И это сделать!
Шридхар
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]
$ 
Питер SO
источник
Если бы я хотел опустить скобки, не было бы необходимости в преобразовании?
круиз,
1

В Go функция может принимать аргументы только тех типов, которые указаны в списке параметров в определении функции. Функция языка с вариативными параметрами немного усложняет это, но следует четко определенным правилам.

Сигнатура функции fmt.Println:

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

В зависимости от языковой специфики ,

Последний входящий параметр в сигнатуре функции может иметь тип с префиксом .... Функция с таким параметром называется вариативной и может быть вызвана с нулем или более аргументов для этого параметра.

Это означает, что вы можете передать Printlnсписок аргументов interface{}типа. Поскольку все типы реализуют пустой интерфейс, вы можете передать список аргументов любого типа, Println(1, "one", true)например, таким образом вы можете вызывать его без ошибок. См. Раздел «Передача аргументов в ... параметры» спецификации языка:

переданное значение представляет собой новый фрагмент типа [] T с новым базовым массивом, последовательные элементы которого являются фактическими аргументами, которые все должны быть присвоены T.

Часть, которая вызывает у вас проблемы, находится сразу после этого в спецификации:

Если последний аргумент может быть назначен типу фрагмента [] T, он может быть передан без изменений в качестве значения параметра ... T, если за аргументом следует .... В этом случае новый фрагмент не создается.

flag.Args()это тип []string. Поскольку Tin Printlnis interface{}, []Tis []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())

(который будет выводиться как [elem0 elem1 ...]в документации пакета fmt )

или

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

(который будет выводить элементы среза строки, каждый из которых разделен одним пробелом), например, с помощью функции Join в пакете строк с разделителем строк.

jrefior
источник
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)

Возможна печать flag.Args()без преобразования в[]interface{}

func main() {
    flag.Parse()
    fmt.Println(flag.Args())
}
Шахриар
источник
2
fmt.Printlnподпись не менялась более 6 лет (и это было просто изменение пакета для error). Даже если бы это было так, в спецификации ясно сказано: "Variadic с конечным параметром p типа ... T, тогда внутри f тип p эквивалентен типу [] T.", так что это не имеет значения. fmt.Println(flags.Args()...)по-прежнему не работает (вам не хватает расширения среза), fmt.Println(flags.Args())всегда работал.
Marc
Как вы узнали, что подпись fmt.Println не менялась более 6 лет? Вы проверяли исходный код?
Shahriar
Комментарий к операции предполагает, что использование flag.Args()исключительно в качестве аргумента fmt.Printlnсработало в то время. (Было бы удивительно, если бы этого не произошло в то время)
лист бибоп
Так? Если есть комментарий, значит, мой ответ должен быть отклонен?
Shahriar