[] 문자열을 [] 인터페이스 {}로 변환 할 수 없습니다.


97

나는 몇 가지 코드를 작성하고 있으며 인수를 잡아서 전달하는 데 필요합니다 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], 그 뒤에 줄 바꿈이 이어지고 실제 플래그를 등록하는 것으로도 작동하는 것처럼 보임) 변경하면 작동하는 것처럼 보였습니다. 이유를 이해하는 척하지 않을 것입니다. 그리고 Stephen의 대답은 어쨌든 훨씬 더 유익합니다. :)
RocketDonkey

답변:


117

이것은 버그가 아닙니다. 유형이 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) 시간이 필요합니다. 그것이 내가 말한 것입니다. 를받는 함수 []stringinterface{}. An interface{}은 a와 다른 메모리 레이아웃을 가지고 string있으므로 각 요소를 변환해야한다는 사실이 문제입니다.
Stephen Weinberg

1
@karlrh : 아니요, Println함수가 슬라이스를 수정하고 일부 요소를 설정한다고 가정합니다 (그렇지 않지만 그렇게한다고 가정합니다). 그런 다음 s interface{}만 있어야하는 슬라이스 에 무엇이든 넣을 수 있습니다 string. 정말로 원하는 것은 Java Generics 와일드 카드와 같은 Slice<? extends []interface{}>것이지만 Go에는 존재하지 않습니다.
newacct

4
Append는 마법입니다. 내장되어 있으며 언어별로 특별히 처리됩니다. 다른 예로는 new(), len()copy(). golang.org/ref/spec#Appending_and_copying_slices
Stephen Weinberg

1
오늘은 'new'가 예비 키워드가 아님을 배웠습니다! 메이크도 아닙니다!
Sridhar

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

1

Go에서 함수는 함수 정의의 매개 변수 목록에 지정된 유형의 인수 만받을 수 있습니다. 가변 매개 변수 언어 기능은이를 약간 복잡하게 만들지 만 잘 정의 된 규칙을 따릅니다.

의 함수 서명 fmt.Println은 다음과 같습니다.

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

언어 specifiction ,

함수 시그니처의 최종 수신 매개 변수에는 ... 접두사가 붙은 유형이있을 수 있습니다. 이러한 매개 변수가있는 함수를 가변이라고하며 해당 매개 변수에 대해 0 개 이상의 인수를 사용하여 호출 할 수 있습니다.

Println, interface{}유형 의 인수 목록을 전달할 수 있습니다 . 모든 유형이 빈 인터페이스를 구현하므로 모든 유형의 인수 목록을 전달할 수 있습니다 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(), ` `)

(각각 단일 공백으로 구분 된 문자열 슬라이스 요소를 출력합니다) 예를 들어 문자열 구분 기호가있는 문자열 패키지의 Join 함수를 사용 합니다 .


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). 그것이 있었다고하더라도, 스펙 은`... T 유형의 최종 매개 변수 p를 갖는 가변적이라고 명시하고, f 내에서 p 유형은 유형 [] T.`와 동일하므로 중요하지 않습니다. fmt.Println(flags.Args()...)여전히 작동하지 않고 (슬라이스 확장이 누락 됨) fmt.Println(flags.Args())항상 작동합니다.
Marc

fmt. Println의 서명이 6 년 동안 변경되지 않은이 정보를 어떻게 얻었습니까? 소스 코드를 확인 했습니까?
Shahriar


op에 대한 의견은 그 시간을 거슬러 올라 가기 flag.Args()위해 전적으로 논쟁으로 사용한다고 제안합니다 fmt.Println. (해당 시간에하지 않았다 경우는 놀라게 될 것이다)
잎을 비밥

그래서? 댓글이있는 경우 내 답변에 반대 투표를해야합니까?
Shahriar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.