1
0
mirror of https://github.com/golang/go synced 2024-11-25 01:08:02 -07:00

allow direct conversion between string and named []byte, []rune

The allowed conversions before and after are:
        type Tstring string
        type Tbyte []byte
        type Trune []rune

        string <-> string  // ok
        string <-> []byte  // ok
        string <-> []rune // ok
        string <-> Tstring // ok
        string <-> Tbyte // was illegal, now ok
        string <-> Trune // was illegal, now ok

        Tstring <-> string  // ok
        Tstring <-> []byte  // ok
        Tstring <-> []rune // ok
        Tstring <-> Tstring // ok
        Tstring <-> Tbyte // was illegal, now ok
        Tstring <-> Trune // was illegal, now ok

Update spec, compiler, tests.  Use in a few packages.

We agreed on this a few months ago but never implemented it.

Fixes #1707.

R=golang-dev, gri, r
CC=golang-dev
https://golang.org/cl/5421057
This commit is contained in:
Russ Cox 2011-11-22 12:30:02 -05:00
parent c69d6345da
commit 6e3e380923
8 changed files with 191 additions and 89 deletions

View File

@ -1,5 +1,5 @@
<!-- title The Go Programming Language Specification --> <!-- title The Go Programming Language Specification -->
<!-- subtitle Version of November 14, 2011 --> <!-- subtitle Version of November 22, 2011 -->
<!-- <!--
TODO TODO
@ -3346,42 +3346,50 @@ MyString(0x65e5) // "\u65e5" == "日" == "\xe6\x97\xa5"
</li> </li>
<li> <li>
Converting a value of type <code>[]byte</code> to a string type yields Converting a slice of bytes to a string type yields
a string whose successive bytes are the elements of the slice. If a string whose successive bytes are the elements of the slice. If
the slice value is <code>nil</code>, the result is the empty string. the slice value is <code>nil</code>, the result is the empty string.
<pre> <pre>
string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø"
type MyBytes []byte
string(MyBytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø"
</pre> </pre>
</li> </li>
<li> <li>
Converting a value of type <code>[]rune</code> to a string type yields Converting a slice of runes to a string type yields
a string that is the concatenation of the individual rune values a string that is the concatenation of the individual rune values
converted to strings. If the slice value is <code>nil</code>, the converted to strings. If the slice value is <code>nil</code>, the
result is the empty string. result is the empty string.
<pre> <pre>
string([]rune{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" string([]rune{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔"
type MyRunes []rune
string(MyRunes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔"
</pre> </pre>
</li> </li>
<li> <li>
Converting a value of a string type to <code>[]byte</code> (or <code>[]uint8</code>) Converting a value of a string type to a slice of bytes type
yields a slice whose successive elements are the bytes of the string. yields a slice whose successive elements are the bytes of the string.
If the string is empty, the result is <code>[]byte(nil)</code>. If the string is empty, the result is <code>[]byte(nil)</code>.
<pre> <pre>
[]byte("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} []byte("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
MyBytes("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}
</pre> </pre>
</li> </li>
<li> <li>
Converting a value of a string type to <code>[]rune</code> yields a Converting a value of a string type to a slice of runes type
slice containing the individual Unicode code points of the string. yields a slice containing the individual Unicode code points of the string.
If the string is empty, the result is <code>[]rune(nil)</code>. If the string is empty, the result is <code>[]rune(nil)</code>.
<pre> <pre>
[]rune(MyString("白鵬翔")) // []rune{0x767d, 0x9d6c, 0x7fd4} []rune(MyString("白鵬翔")) // []rune{0x767d, 0x9d6c, 0x7fd4}
MyRunes("白鵬翔") // []rune{0x767d, 0x9d6c, 0x7fd4}
</pre> </pre>
</li> </li>
</ol> </ol>

View File

@ -1177,19 +1177,19 @@ convertop(Type *src, Type *dst, char **why)
if(isint[src->etype] && dst->etype == TSTRING) if(isint[src->etype] && dst->etype == TSTRING)
return ORUNESTR; return ORUNESTR;
if(isslice(src) && src->sym == nil && dst->etype == TSTRING) { if(isslice(src) && dst->etype == TSTRING) {
if(eqtype(src->type, bytetype)) if(src->type->etype == bytetype->etype)
return OARRAYBYTESTR; return OARRAYBYTESTR;
if(eqtype(src->type, runetype)) if(src->type->etype == runetype->etype)
return OARRAYRUNESTR; return OARRAYRUNESTR;
} }
// 7. src is a string and dst is []byte or []rune. // 7. src is a string and dst is []byte or []rune.
// String to slice. // String to slice.
if(src->etype == TSTRING && isslice(dst) && dst->sym == nil) { if(src->etype == TSTRING && isslice(dst)) {
if(eqtype(dst->type, bytetype)) if(dst->type->etype == bytetype->etype)
return OSTRARRAYBYTE; return OSTRARRAYBYTE;
if(eqtype(dst->type, runetype)) if(dst->type->etype == runetype->etype)
return OSTRARRAYRUNE; return OSTRARRAYRUNE;
} }

View File

@ -29,71 +29,69 @@ const testInput = `
</body><!-- missing final newline -->` </body><!-- missing final newline -->`
var rawTokens = []Token{ var rawTokens = []Token{
CharData([]byte("\n")), CharData("\n"),
ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)}, ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
CharData([]byte("\n")), CharData("\n"),
Directive([]byte(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`), "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
), CharData("\n"),
CharData([]byte("\n")),
StartElement{Name{"", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}}, StartElement{Name{"", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}, StartElement{Name{"", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
CharData([]byte("World <>'\" 白鵬翔")), CharData("World <>'\" 白鵬翔"),
EndElement{Name{"", "hello"}}, EndElement{Name{"", "hello"}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"", "goodbye"}, []Attr{}}, StartElement{Name{"", "goodbye"}, []Attr{}},
EndElement{Name{"", "goodbye"}}, EndElement{Name{"", "goodbye"}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}}, StartElement{Name{"", "outer"}, []Attr{{Name{"foo", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"", "inner"}, []Attr{}}, StartElement{Name{"", "inner"}, []Attr{}},
EndElement{Name{"", "inner"}}, EndElement{Name{"", "inner"}},
CharData([]byte("\n ")), CharData("\n "),
EndElement{Name{"", "outer"}}, EndElement{Name{"", "outer"}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"tag", "name"}, []Attr{}}, StartElement{Name{"tag", "name"}, []Attr{}},
CharData([]byte("\n ")), CharData("\n "),
CharData([]byte("Some text here.")), CharData("Some text here."),
CharData([]byte("\n ")), CharData("\n "),
EndElement{Name{"tag", "name"}}, EndElement{Name{"tag", "name"}},
CharData([]byte("\n")), CharData("\n"),
EndElement{Name{"", "body"}}, EndElement{Name{"", "body"}},
Comment([]byte(" missing final newline ")), Comment(" missing final newline "),
} }
var cookedTokens = []Token{ var cookedTokens = []Token{
CharData([]byte("\n")), CharData("\n"),
ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)}, ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
CharData([]byte("\n")), CharData("\n"),
Directive([]byte(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" Directive(`DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`), "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"`),
), CharData("\n"),
CharData([]byte("\n")),
StartElement{Name{"ns2", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}}, StartElement{Name{"ns2", "body"}, []Attr{{Name{"xmlns", "foo"}, "ns1"}, {Name{"", "xmlns"}, "ns2"}, {Name{"xmlns", "tag"}, "ns3"}}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"ns2", "hello"}, []Attr{{Name{"", "lang"}, "en"}}}, StartElement{Name{"ns2", "hello"}, []Attr{{Name{"", "lang"}, "en"}}},
CharData([]byte("World <>'\" 白鵬翔")), CharData("World <>'\" 白鵬翔"),
EndElement{Name{"ns2", "hello"}}, EndElement{Name{"ns2", "hello"}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"ns2", "goodbye"}, []Attr{}}, StartElement{Name{"ns2", "goodbye"}, []Attr{}},
EndElement{Name{"ns2", "goodbye"}}, EndElement{Name{"ns2", "goodbye"}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}}, StartElement{Name{"ns2", "outer"}, []Attr{{Name{"ns1", "attr"}, "value"}, {Name{"xmlns", "tag"}, "ns4"}}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"ns2", "inner"}, []Attr{}}, StartElement{Name{"ns2", "inner"}, []Attr{}},
EndElement{Name{"ns2", "inner"}}, EndElement{Name{"ns2", "inner"}},
CharData([]byte("\n ")), CharData("\n "),
EndElement{Name{"ns2", "outer"}}, EndElement{Name{"ns2", "outer"}},
CharData([]byte("\n ")), CharData("\n "),
StartElement{Name{"ns3", "name"}, []Attr{}}, StartElement{Name{"ns3", "name"}, []Attr{}},
CharData([]byte("\n ")), CharData("\n "),
CharData([]byte("Some text here.")), CharData("Some text here."),
CharData([]byte("\n ")), CharData("\n "),
EndElement{Name{"ns3", "name"}}, EndElement{Name{"ns3", "name"}},
CharData([]byte("\n")), CharData("\n"),
EndElement{Name{"ns2", "body"}}, EndElement{Name{"ns2", "body"}},
Comment([]byte(" missing final newline ")), Comment(" missing final newline "),
} }
const testInputAltEncoding = ` const testInputAltEncoding = `
@ -101,11 +99,11 @@ const testInputAltEncoding = `
<TAG>VALUE</TAG>` <TAG>VALUE</TAG>`
var rawTokensAltEncoding = []Token{ var rawTokensAltEncoding = []Token{
CharData([]byte("\n")), CharData("\n"),
ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)}, ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)},
CharData([]byte("\n")), CharData("\n"),
StartElement{Name{"", "tag"}, []Attr{}}, StartElement{Name{"", "tag"}, []Attr{}},
CharData([]byte("value")), CharData("value"),
EndElement{Name{"", "tag"}}, EndElement{Name{"", "tag"}},
} }
@ -270,21 +268,21 @@ var nestedDirectivesInput = `
` `
var nestedDirectivesTokens = []Token{ var nestedDirectivesTokens = []Token{
CharData([]byte("\n")), CharData("\n"),
Directive([]byte(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`)), Directive(`DOCTYPE [<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">]`),
CharData([]byte("\n")), CharData("\n"),
Directive([]byte(`DOCTYPE [<!ENTITY xlt ">">]`)), Directive(`DOCTYPE [<!ENTITY xlt ">">]`),
CharData([]byte("\n")), CharData("\n"),
Directive([]byte(`DOCTYPE [<!ENTITY xlt "<">]`)), Directive(`DOCTYPE [<!ENTITY xlt "<">]`),
CharData([]byte("\n")), CharData("\n"),
Directive([]byte(`DOCTYPE [<!ENTITY xlt '>'>]`)), Directive(`DOCTYPE [<!ENTITY xlt '>'>]`),
CharData([]byte("\n")), CharData("\n"),
Directive([]byte(`DOCTYPE [<!ENTITY xlt '<'>]`)), Directive(`DOCTYPE [<!ENTITY xlt '<'>]`),
CharData([]byte("\n")), CharData("\n"),
Directive([]byte(`DOCTYPE [<!ENTITY xlt '">'>]`)), Directive(`DOCTYPE [<!ENTITY xlt '">'>]`),
CharData([]byte("\n")), CharData("\n"),
Directive([]byte(`DOCTYPE [<!ENTITY xlt "'<">]`)), Directive(`DOCTYPE [<!ENTITY xlt "'<">]`),
CharData([]byte("\n")), CharData("\n"),
} }
func TestNestedDirectives(t *testing.T) { func TestNestedDirectives(t *testing.T) {

View File

@ -48,23 +48,23 @@ type sniffSig interface {
// Data matching the table in section 6. // Data matching the table in section 6.
var sniffSignatures = []sniffSig{ var sniffSignatures = []sniffSig{
htmlSig([]byte("<!DOCTYPE HTML")), htmlSig("<!DOCTYPE HTML"),
htmlSig([]byte("<HTML")), htmlSig("<HTML"),
htmlSig([]byte("<HEAD")), htmlSig("<HEAD"),
htmlSig([]byte("<SCRIPT")), htmlSig("<SCRIPT"),
htmlSig([]byte("<IFRAME")), htmlSig("<IFRAME"),
htmlSig([]byte("<H1")), htmlSig("<H1"),
htmlSig([]byte("<DIV")), htmlSig("<DIV"),
htmlSig([]byte("<FONT")), htmlSig("<FONT"),
htmlSig([]byte("<TABLE")), htmlSig("<TABLE"),
htmlSig([]byte("<A")), htmlSig("<A"),
htmlSig([]byte("<STYLE")), htmlSig("<STYLE"),
htmlSig([]byte("<TITLE")), htmlSig("<TITLE"),
htmlSig([]byte("<B")), htmlSig("<B"),
htmlSig([]byte("<BODY")), htmlSig("<BODY"),
htmlSig([]byte("<BR")), htmlSig("<BR"),
htmlSig([]byte("<P")), htmlSig("<P"),
htmlSig([]byte("<!--")), htmlSig("<!--"),
&maskedSig{mask: []byte("\xFF\xFF\xFF\xFF\xFF"), pat: []byte("<?xml"), skipWS: true, ct: "text/xml; charset=utf-8"}, &maskedSig{mask: []byte("\xFF\xFF\xFF\xFF\xFF"), pat: []byte("<?xml"), skipWS: true, ct: "text/xml; charset=utf-8"},

View File

@ -185,7 +185,7 @@ func (a *Address) String() string {
type addrParser []byte type addrParser []byte
func newAddrParser(s string) *addrParser { func newAddrParser(s string) *addrParser {
p := addrParser([]byte(s)) p := addrParser(s)
return &p return &p
} }

96
test/convert1.go Normal file
View File

@ -0,0 +1,96 @@
// errchk $G -e $D/$F.go
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
type Tbyte []byte
type Trune []rune
type Tint64 []int64
type Tstring string
func main() {
s := "hello"
sb := []byte("hello")
sr := []rune("hello")
si := []int64{'h', 'e', 'l', 'l', 'o'}
ts := Tstring(s)
tsb := Tbyte(sb)
tsr := Trune(sr)
tsi := Tint64(si)
_ = string(s)
_ = []byte(s)
_ = []rune(s)
_ = []int64(s) // ERROR "cannot convert.*\[\]int64"
_ = Tstring(s)
_ = Tbyte(s)
_ = Trune(s)
_ = Tint64(s) // ERROR "cannot convert.*Tint64"
_ = string(sb)
_ = []byte(sb)
_ = []rune(sb) // ERROR "cannot convert.*\[\]rune"
_ = []int64(sb) // ERROR "cannot convert.*\[\]int64"
_ = Tstring(sb)
_ = Tbyte(sb)
_ = Trune(sb) // ERROR "cannot convert.*Trune"
_ = Tint64(sb) // ERROR "cannot convert.*Tint64"
_ = string(sr)
_ = []byte(sr) // ERROR "cannot convert.*\[\]byte"
_ = []rune(sr)
_ = []int64(sr) // ERROR "cannot convert.*\[\]int64"
_ = Tstring(sr)
_ = Tbyte(sr) // ERROR "cannot convert.*Tbyte"
_ = Trune(sr)
_ = Tint64(sr) // ERROR "cannot convert.*Tint64"
_ = string(si) // ERROR "cannot convert.* string"
_ = []byte(si) // ERROR "cannot convert.*\[\]byte"
_ = []rune(si) // ERROR "cannot convert.*\[\]rune"
_ = []int64(si)
_ = Tstring(si) // ERROR "cannot convert.*Tstring"
_ = Tbyte(si) // ERROR "cannot convert.*Tbyte"
_ = Trune(si) // ERROR "cannot convert.*Trune"
_ = Tint64(si)
_ = string(ts)
_ = []byte(ts)
_ = []rune(ts)
_ = []int64(ts) // ERROR "cannot convert.*\[\]int64"
_ = Tstring(ts)
_ = Tbyte(ts)
_ = Trune(ts)
_ = Tint64(ts) // ERROR "cannot convert.*Tint64"
_ = string(tsb)
_ = []byte(tsb)
_ = []rune(tsb) // ERROR "cannot convert.*\[\]rune"
_ = []int64(tsb) // ERROR "cannot convert.*\[\]int64"
_ = Tstring(tsb)
_ = Tbyte(tsb)
_ = Trune(tsb) // ERROR "cannot convert.*Trune"
_ = Tint64(tsb) // ERROR "cannot convert.*Tint64"
_ = string(tsr)
_ = []byte(tsr) // ERROR "cannot convert.*\[\]byte"
_ = []rune(tsr)
_ = []int64(tsr) // ERROR "cannot convert.*\[\]int64"
_ = Tstring(tsr)
_ = Tbyte(tsr) // ERROR "cannot convert.*Tbyte"
_ = Trune(tsr)
_ = Tint64(tsr) // ERROR "cannot convert.*Tint64"
_ = string(tsi) // ERROR "cannot convert.* string"
_ = []byte(tsi) // ERROR "cannot convert.*\[\]byte"
_ = []rune(tsi) // ERROR "cannot convert.*\[\]rune"
_ = []int64(tsi)
_ = Tstring(tsi) // ERROR "cannot convert.*Tstring"
_ = Tbyte(tsi) // ERROR "cannot convert.*Tbyte"
_ = Trune(tsi) // ERROR "cannot convert.*Trune"
_ = Tint64(tsi)
}

View File

@ -54,12 +54,12 @@ var _ = []byte(ss)
var _ []rune = ss // ERROR "cannot use|incompatible|invalid" var _ []rune = ss // ERROR "cannot use|incompatible|invalid"
var _ []byte = ss // ERROR "cannot use|incompatible|invalid" var _ []byte = ss // ERROR "cannot use|incompatible|invalid"
// named slice is not // named slice is now ok
type Trune []rune type Trune []rune
type Tbyte []byte type Tbyte []byte
var _ = Trune("abc") // ERROR "convert|incompatible|invalid" var _ = Trune("abc") // ok
var _ = Tbyte("abc") // ERROR "convert|incompatible|invalid" var _ = Tbyte("abc") // ok
// implicit is still not // implicit is still not
var _ Trune = "abc" // ERROR "cannot use|incompatible|invalid" var _ Trune = "abc" // ERROR "cannot use|incompatible|invalid"

View File

@ -57,5 +57,5 @@ func main() {
_, b = <-c // ERROR "cannot .* bool.*type Bool" _, b = <-c // ERROR "cannot .* bool.*type Bool"
_ = b _ = b
asString(String(slice)) // ERROR "cannot .*type Slice.*type String" asString(String(slice)) // ok
} }