其实这个问题在前面可变函数参数列表中提到了,就是不能因为interface{}是万用类型的就将其他类型的变量切片赋值给它,原文链接
问题如下:
I’m writing some code, and I need it to catch the arguments and pass them through fmt.Println
(I want its default behaviour, to write arguments separated by spaces and followed by a newline). However it takes []interface {}
but flag.Args()
returns a []string
.
Here’s the code example:
<span class="kwd">package</span><span class="pln"> main
</span><span class="kwd">import</span> <span class="pun">(</span>
<span class="str">"fmt"</span>
<span class="str">"flag"</span>
<span class="pun">)</span><span class="pln">
func main</span><span class="pun">()</span> <span class="pun">{</span><span class="pln">
flag</span><span class="pun">.</span><span class="typ">Parse</span><span class="pun">()</span><span class="pln">
fmt</span><span class="pun">.</span><span class="typ">Println</span><span class="pun">(</span><span class="pln">flag</span><span class="pun">.</span><span class="typ">Args</span><span class="pun">()...)</span>
<span class="pun">}</span>`</pre>
This returns the following error:
<pre class="lang-golang prettyprint prettyprinted">`<span class="pun">./</span><span class="pln">example</span><span class="pun">.</span><span class="pln">go</span><span class="pun">:</span><span class="lit">10</span><span class="pun">:</span><span class="pln"> cannot </span><span class="kwd">use</span><span class="pln"> args </span><span class="pun">(</span><span class="pln">type </span><span class="pun">[]</span><span class="kwd">string</span><span class="pun">)</span> <span class="kwd">as</span><span class="pln"> type </span><span class="pun">[]</span><span class="kwd">interface</span> <span class="pun">{}</span> <span class="kwd">in</span> <span class="kwd">function</span><span class="pln"> argument</span>`</pre>
Is this a bug? Shouldn't `fmt.Println` take **any** array? By the way, I've also tried to do this:
<pre class="lang-golang prettyprint prettyprinted">`<span class="kwd">var</span><span class="pln"> args </span><span class="pun">=</span> <span class="pun">[]</span><span class="kwd">interface</span><span class="pun">{}(</span><span class="pln">flag</span><span class="pun">.</span><span class="typ">Args</span><span class="pun">())</span>`</pre>
but I get the following error:
<pre class="lang-golang prettyprint prettyprinted">`<span class="pln">cannot convert flag</span><span class="pun">.</span><span class="typ">Args</span><span class="pun">()</span> <span class="pun">(</span><span class="pln">type </span><span class="pun">[]</span><span class="kwd">string</span><span class="pun">)</span><span class="pln"> to type </span><span class="pun">[]</span><span class="kwd">interface</span> <span class="pun">{}</span>`</pre>
Is there a "Go" way to workaround this?
----------------答案如下----------------
</div>
<div class="post-text">
This is not a bug. `fmt.Println()` requires a `[]interface{}` type. That means, it must be a slice of `interface{}` values and not "any slice". In order to convert the slice, you will need to loop over and copy each element.
<pre class="lang-golang prettyprint prettyprinted">`<span class="pln">old </span><span class="pun">:=</span><span class="pln"> flag</span><span class="pun">.</span><span class="typ">Args</span><span class="pun">()</span>
<span class="kwd">new</span> <span class="pun">:=</span><span class="pln"> make</span><span class="pun">([]</span><span class="kwd">interface</span><span class="pun">{},</span><span class="pln"> len</span><span class="pun">(</span><span class="pln">old</span><span class="pun">))</span>
<span class="kwd">for</span><span class="pln"> i</span><span class="pun">,</span><span class="pln"> v </span><span class="pun">:=</span><span class="pln"> range old </span><span class="pun">{</span>
<span class="kwd">new</span><span class="pun">[</span><span class="pln">i</span><span class="pun">]</span> <span class="pun">=</span><span class="pln"> v
</span><span class="pun">}</span><span class="pln">
fmt</span><span class="pun">.</span><span class="typ">Println</span><span class="pun">(</span><span class="kwd">new</span><span class="pun">...)</span>
The reason you can’t use any slice is that conversion between a []string
and a []interface{}
requires the memory layout to be changed and happens in O(n) time. Converting a type to an interface{}
requires O(1) time. If they made this for loop unnecessary, the compiler would still need to insert it.
———-相关评论———-
If each iteration requires O(1) time, wouldn’t the whole loop need O(n) time? – cruizh Oct 20 ‘12 at 17:06 | |||
If every element in the slice satisfies interface{} , then a function receiving []string instead should have no problem, since every element inside satisfies the interface, is it? – cruizh Oct 20 ‘12 at 17:14 | |||
| By the way, I found this link in golang-nuts: groups.google.com/d/topic/golang-nuts/Il-tO1xtAyE/discussion – cruizh Oct 20 ‘12 at 17:51 | ||
| Yes, each iteration requires O(1) time and the loop requires O(n) time. That is what I said. As for the function receiving a []string , it expects a interface{} . An interface{} has a different memory layout from a string so the fact that each element needs to be converted is the problem. – Stephen Weinberg Oct 20 ‘12 at 17:52 | ||
| @karlrh: No, suppose the Println function modifies the slice, and sets some elements (it doesn’t, but suppose it does). Then it can put any interface{} into the slice, which should only have string s. What you really want is something like the Java Generics wildcard Slice<? extends []interface{}> , but that doesn’t exist in Go. – newacct Oct 21 ‘12 at 2:05 | ||
| Append is magical. It is built-in and treated specially by the language. Other examples include new() , len() , and copy() . golang.org/ref/spec#Appending_and_copying_slices – Stephen Weinberg Jan 23 ‘14 at 17:52 | ||
| |||
Explicit conversion to interface{} is unnecessary, instead of new[i] = interface{}(v) you can simply write new[i] = v . – icza Jun 2 ‘15 at 6:57 | |||