Kiểu chuyển đổi lát giao diện


194

Tôi tò mò tại sao Go không hoàn toàn chuyển đổi []Tthành []interface{}khi nào nó sẽ hoàn toàn chuyển đổi Tthành interface{}. Có điều gì không tầm thường về chuyển đổi này mà tôi đang thiếu?

Thí dụ:

func foo([]interface{}) { /* do something */ }

func main() {
    var a []string = []string{"hello", "world"}
    foo(a)
}

go build phàn nàn

không thể sử dụng một (kiểu [] chuỗi) làm kiểu [] giao diện {} trong đối số hàm

Và nếu tôi cố gắng làm điều đó một cách rõ ràng, điều tương tự: b := []interface{}(a)phàn nàn

không thể chuyển đổi một (loại [] chuỗi) sang loại [] giao diện {}

Vì vậy, mỗi khi tôi cần thực hiện chuyển đổi này (dường như xuất hiện rất nhiều), tôi đã làm một việc như thế này:

b = make([]interface{}, len(a), len(a))
for i := range a {
    b[i] = a[i]
}

Có cách nào tốt hơn để làm điều này hoặc các chức năng thư viện tiêu chuẩn để trợ giúp với các chuyển đổi này không? Có vẻ hơi ngớ ngẩn khi viết thêm 4 dòng mã mỗi khi tôi muốn gọi một hàm có thể lấy danh sách ví dụ ints hoặc chuỗi.


vì vậy, bạn xác định "ngầm định chuyển đổi [] T thành [] giao diện {}" để có nghĩa là "phân bổ một lát cắt mới và sao chép tất cả các yếu tố trên". Điều đó không phù hợp với những gì "ngầm chuyển đổi T thành giao diện {}", đây chỉ là một khung nhìn có cùng giá trị như một kiểu tĩnh chung hơn; không có gì được sao chép; và nếu bạn gõ xác nhận nó trở lại kiểu T, bạn vẫn sẽ nhận lại được điều tương tự.
newacct

1
đã không suy nghĩ quá nhiều về cách nó sẽ được thực hiện, chỉ là ở cấp độ cao hơn, sẽ thuận tiện để có thể dễ dàng chuyển đổi toàn bộ danh sách sang loại khác như một cách giải quyết cho việc không có loại chung.
danny

Câu trả lời:


215

Trong Go, có một quy tắc chung là cú pháp không được ẩn các hoạt động phức tạp / tốn kém. Chuyển đổi a stringthành an interface{}được thực hiện trong thời gian O (1). Chuyển đổi a []stringthành an interface{}cũng được thực hiện trong thời gian O (1) vì một lát vẫn là một giá trị. Tuy nhiên, chuyển đổi a []stringthành an []interface{}là thời gian O (n) vì mỗi phần tử của lát phải được chuyển đổi thành an interface{}.

Một ngoại lệ cho quy tắc này là chuyển đổi chuỗi. Khi chuyển đổi stringthành và từ a []bytehoặc a []rune, Go (O) hoạt động ngay cả khi chuyển đổi là "cú pháp".

Không có chức năng thư viện tiêu chuẩn sẽ thực hiện chuyển đổi này cho bạn. Bạn có thể tạo một phản xạ, nhưng nó sẽ chậm hơn tùy chọn ba dòng.

Ví dụ với sự phản ánh:

func InterfaceSlice(slice interface{}) []interface{} {
    s := reflect.ValueOf(slice)
    if s.Kind() != reflect.Slice {
        panic("InterfaceSlice() given a non-slice type")
    }

    ret := make([]interface{}, s.Len())

    for i:=0; i<s.Len(); i++ {
        ret[i] = s.Index(i).Interface()
    }

    return ret
}

Mặc dù vậy, lựa chọn tốt nhất của bạn chỉ là sử dụng các dòng mã bạn đã đưa ra trong câu hỏi của mình:

b := make([]interface{}, len(a))
for i := range a {
    b[i] = a[i]
}

3
nó có thể chậm hơn, nhưng nó sẽ hoạt động chung với bất kỳ loại lát nào
newacct

Toàn bộ câu trả lời này áp dụng cho maps quá btw.
RickyA

Điều này cũng áp dụng cho channelslà tốt.
Justin Ohms

Cảm ơn đã giải thích rõ ràng, nếu có thể bạn có thể thêm ref. cho Converting a []string to an interface{} is also done in O(1) time?
Mayur

56

Điều bạn đang thiếu là Tinterface{}nắm giữ một giá trị Tđã trình bày khác nhau trong bộ nhớ do đó không thể được chuyển đổi trivially.

Một biến kiểu Tchỉ là giá trị của nó trong bộ nhớ. Không có thông tin loại liên quan (trong Go mỗi biến có một loại duy nhất được biết tại thời gian biên dịch không phải lúc chạy). Nó được biểu diễn trong bộ nhớ như thế này:

  • giá trị

Một interface{}biến có kiểu biến Tđược biểu diễn trong bộ nhớ như thế này

  • con trỏ để gõ T
  • giá trị

Vì vậy, quay trở lại câu hỏi ban đầu của bạn: tại sao đi không hoàn toàn chuyển đổi []Tthành []interface{}?

Chuyển đổi []Tsang []interface{}sẽ liên quan đến việc tạo ra một lát interface {}giá trị mới, đây là một hoạt động không tầm thường vì cách bố trí trong bộ nhớ hoàn toàn khác nhau.


10
Đây là thông tin và được viết tốt (+1), nhưng bạn không giải quyết phần khác của câu hỏi của mình: "Có cách nào tốt hơn để làm điều này ..." (-1).
weberc2

13

Dưới đây là lời giải thích chính thức: https://github.com/golang/go/wiki/InterfaceSlice

var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
    interfaceSlice[i] = d
}

Thông tin tốt nhưng nó không phải là một lời giải thích chính thức. Đó là một wiki nơi mọi người có thể chỉnh sửa.
Inanc Gumus

Làm thế nào tôi nên chuyển đổi []interfacesang loại tôi mong đợi như []map[string]interface{}?
Lewis Chan

8

Hãy thử interface{}thay thế. Để quay lại như lát, hãy thử

func foo(bar interface{}) {
    s := bar.([]string)
    // ...
}

3
Điều này đặt ra câu hỏi tiếp theo: làm thế nào OP được lặp đi lặp lại barđể giải thích nó là "một lát cắt bất kỳ loại nào"? Lưu ý rằng ba lớp lót của anh ta tạo ra một []interface{}, không []stringhoặc một lát của loại bê tông khác.
kostix

13
-1: Điều này chỉ hoạt động nếu thanh là [] chuỗi, trong trường hợp đó, bạn cũng có thể viết:func foo(bar []string) { /* ... */ }
weberc2

2
sử dụng một loại chuyển đổi, hoặc sử dụng sự phản chiếu như trong câu trả lời được chấp nhận.
dskinner

OP sẽ phải tìm ra loại của anh ấy trong thời gian chạy bằng mọi cách. Sử dụng non lát interface{}sẽ làm cho trình biên dịch không phàn nàn khi truyền đối số như trong foo(a). Anh ta có thể sử dụng phản xạ hoặc chuyển đổi loại ( golang.org/doc/effective_go.html#type_switch ) bên trong foo.
Cassiohg

2

Chuyển đổi interface{}thành bất kỳ loại.

Cú pháp:

result := interface.(datatype)

Thí dụ:

var employee interface{} = []string{"Jhon", "Arya"}
result := employee.([]string)   //result type is []string.

2
Bạn chắc chứ? Tôi không nghĩ rằng điều này là hợp lệ. Bạn sẽ thấy nó: invalid type assertion: potato.([]string) (non-interface type []interface {} on left)
Adriano Tadao

2

Trong trường hợp bạn cần rút ngắn nhiều mã hơn, bạn có thể tạo loại mới cho trình trợ giúp

type Strings []string

func (ss Strings) ToInterfaceSlice() []interface{} {
    iface := make([]interface{}, len(ss))
    for i := range ss {
        iface[i] = ss[i]
    }
    return iface
}

sau đó

a := []strings{"a", "b", "c", "d"}
sliceIFace := Strings(a).ToInterfaceSlice()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.