Эта статьяЭти ямы в языке Goтри.
Не делайте никаких предположений о том, когда выполняются параллельные функции Go.
См. следующий список:
import (
"fmt"
"runtime"
"time"
)
func main(){
names := []string{"lily", "yoyo", "cersei", "rose", "annei"}
for _, name := range names{
go func(){
fmt.Println(name)
}()
}
runtime.GOMAXPROCS(1)
runtime.Gosched()
}
Подскажите, пожалуйста, что на выходе?
Отвечать:
annei
annei
annei
annei
annei
Зачем? Вы немного удивлены? Вывод «annei», а «annei» — последний элемент «names», что означает, что программа печатает значение последнего элемента, а name — внешнее значение для анонимных функций. Отсюда можно сделать вывод: хотя в каждом цикле включена сопрограмма, но эти сопрограммы ссылаются на внешние переменные, при создании сопрограммы и выполнении действия печати значение имени неизвестно, что изменилось, т.к. тоже работает сопрограмма функции, все работают параллельно, но здесь из-за того, что длина массива имен слишком мала, при создании сопрограммы основной цикл функции уже закончился, поэтому печатаются все пройденные имена. элемент «анней». Как подтвердить вышеизложенный вывод? На самом деле это очень просто: после каждого цикла делайте небольшую паузу и ждите, пока сопрограмма напечатает текущее имя.
import (
"fmt"
"runtime"
"time"
)
func main(){
names := []string{"lily", "yoyo", "cersei", "rose", "annei"}
for _, name := range names{
go func(){
fmt.Println(name)
}()
time.Sleep(time.Second)
}
runtime.GOMAXPROCS(1)
runtime.Gosched()
}
распечатать результат:
lily
yoyo
cersei
rose
annei
Выше мы приходим к выводу, не делайте никаких предположений о времени выполнения «функции go», если вы действительно не можете дать гарантии, которые делают это предположение абсолютным фактом.
Предположим, что получатель по методу типа T имеет обаT
тип и*T
тип указателя, то его нельзя вызвать для неадресуемого значения T*T
метод приемника
Пожалуйста, посмотрите на код, он нормально компилируется?
import (
"fmt"
)
type Lili struct{
Name string
}
func (Lili *Lili) fmtPointer(){
fmt.Println("poniter")
}
func (Lili Lili) fmtReference(){
fmt.Println("reference")
}
func main(){
li := Lili{}
li.fmtPointer()
}
Отвечать:
能正常编译通过,并输出"poniter"
Чувствуя себя немного удивленным, посмотрите на следующий код, как его можно скомпилировать и передать?
import (
"fmt"
)
type Lili struct{
Name string
}
func (Lili *Lili) fmtPointer(){
fmt.Println("poniter")
}
func (Lili Lili) fmtReference(){
fmt.Println("reference")
}
func main(){
Lili{}.fmtPointer()
}
Отвечать:
不能编译通过。
“cannot call pointer method on Lili literal”
“cannot take the address of Lili literal”
Это немного странно? Почему это? На самом деле в первом примере кода «li» в функции main является переменной.Хотя li имеет тип Lili, li является адресным, а тип &li —*Lili
, поэтому методы *Lili можно назвать.
Интерфейс, содержащий нулевой указатель, не является нулевым интерфейсом.
Взгляните на следующий код, что он возвращает?
import (
"bytes"
"fmt"
"io"
)
const debug = true
func main(){
var buf *bytes.Buffer
if debug{
buf = new(bytes.Buffer)
}
f(buf)
}
func f(out io.Writer){
if out != nil{
fmt.Println("surprise!")
}
}
Ответ на выходе: удивление.
хорошо, давайтеdebug
выключить, иdebug
значение становитсяfalse
. Итак, каков результат? Ничего не выводит?
import (
"bytes"
"fmt"
"io"
)
const debug = false
func main(){
var buf *bytes.Buffer
if debug{
buf = new(bytes.Buffer)
}
f(buf)
}
func f(out io.Writer){
if out != nil{
fmt.Println("surprise!")
}
}
Ответ таков: все-таки выходное удивление.
Почему это?
Это включает в себя концепцию, которая касается значений интерфейса. Концептуально значение интерфейса делится на две части: одна — тип, а другая — значение, соответствующее типу, которые называются: динамический тип и динамическое значение. Системы типов предназначены для скомпилированных языков, тип — это концепция времени компиляции, поэтому тип не является значением.
В приведенном выше коде параметру out функции f присваивается значение*bytes.Buffer
является нулевым указателем, поэтому динамическое значение out равно нулю. Однако его динамический тип — *bytes.Buffer, что означает: «Интерфейс, отличный от nil, содержащий нулевой указатель», поэтому результат «out!=nil» по-прежнему верен.
Однако для прямого*bytes.Buffer
Обнуление типов не имеет этой проблемы.
import (
"bytes"
"fmt"
)
func main(){
var buf *bytes.Buffer
if buf == nil{
fmt.Println("right")
}
}
Или вывод: правильно
Вышеупомянутая яма появится только тогда, когда указатель интерфейса будет передан в параметр интерфейса функции.
Это также очень легко изменить,*bytes.Buffer
изменить наio.Writer
Достаточно.
import (
"bytes"
"fmt"
"io"
)
const debug = false
func main(){
var buf io.Writer //原来是var buf *bytes.Buffer
if debug{
buf = new(bytes.Buffer)
}
f(buf)
}
func f(out io.Writer){
if out != nil{
fmt.Println("surprise!")
}
}
При преобразовании карты в строку json порядок в строке json не имеет ничего общего с порядком назначения карты
Пожалуйста, посмотрите на следующий код, каков результат? Если это строка json, каков порядок ключей в строке json?
func main() {
params := make(map[string]string)
params["id"] = "1"
params["id1"] = "3"
params["controller"] = "sections"
data, _ := json.Marshal(params)
fmt.Println(string(data))
}
Ответ: вывод{"controller":"sections","id":"1","id1":"3"}
Использование пакета преобразования json, который поставляется с Golang, изменит порядок ключей на карте на алфавитный порядок вместо порядка назначения карты. Даже если используется структура картыfor range
При обходе ключи в нем тоже неупорядоченные.Можно понять, что карта представляет собой неупорядоченную структуру, которую следует отличать от массива в php.
Json десериализует числа в значения типа interface{}, по умолчанию анализируемые как float64
См. следующую программу, программа хочет вывести целое число в данных json.id
плюс3
значение, программа сообщит об ошибке?
func main(){
jsonStr := `{"id":1058,"name":"RyuGou"}`
var jsonData map[string]interface{}
json.Unmarshal([]byte(jsonStr), &jsonData)
sum := jsonData["id"].(int) + 3
fmt.Println(sum)
}
Ответ заключается в том, что будет сообщено об ошибке, и вывод будет таким:
panic: interface conversion: interface {} is float64, not int
При использовании Golang для анализа данных формата JSON, если данные получены в интерфейсе{}, они будут проанализированы в соответствии со следующими правилами:
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
Следует изменить на:
func main(){
jsonStr := `{"id":1058,"name":"RyuGou"}`
var jsonData map[string]interface{}
json.Unmarshal([]byte(jsonStr), &jsonData)
sum := int(jsonData["id"].(float64)) + 3
fmt.Println(sum)
}
Даже если имеется несколько переменных, некоторые переменные существуют, а некоторые переменные не существуют, и эти переменные назначаются вместе, их нельзя использовать.:=
чтобы присвоить значение глобальной переменной
:=
Он часто используется для объявления локальных переменных.Когда присваивается несколько переменных и существуют некоторые значения,:=
Его также можно использовать для присваивания, например:
msgStr := "hello wolrd"
msgStr, err := "hello", errors.New("xxx")//err并不存在
Однако, если глобальные переменные также назначены подобным образом, будут проблемы.Пожалуйста, посмотрите на следующий код.Вы можете его скомпилировать?
var varTest string
func test(){
varTest, err := function()
fmt.Println(err.Error())
}
func function()(string, error){
return "hello world", errors.New("error")
}
func main(){
test()
}
Ответ: никак. вывод:
varTest declared and not used
Но если вы измените его на следующий код, вы можете пройти:
var varTest string
func test(){
err := errors.New("error")
varTest, err = function()
fmt.Println(err.Error())
}
func function()(string, error){
return "hello world", errors.New("error")
}
func main(){
test()
}
вывод:
error
Что является причиной этого?
Ответ на самом деле довольно прост, вtest
метод, если вы используетеvarTest, err := function()
Таким образом, это эквивалентно определению других и глобальных переменных в функции.varTest
Локальная переменная с таким же именем, и эта локальная переменная не используется, поэтому компилироваться не будет.
* интерфейс - это тип указателя на интерфейс, а не тип интерфейса
Можно ли скомпилировать следующий код?
import (
"fmt"
)
type Father interface {
Hello()
}
type Child struct {
Name string
}
func (s Child)Hello() {
}
func main(){
var buf Child
buf = Child{}
f(&buf)
}
func f(out *Father){
if out != nil{
fmt.Println("surprise!")
}
}
Ответ: он не может быть скомпилирован. вывод:
*Father is pointer to interface, not interface
Примечание. Переменная типа интерфейса может быть присвоена экземпляру структуры, реализующей интерфейс, но это не означает, что указатель на интерфейс может быть присвоен экземпляру указателя структуры, реализующей интерфейс. который:
var buf Father = Child{}
Да, но
var buf *Father = new(Child)
Но это неправильно. Следует изменить на:
var buf Father = Child{}
var pointer *Father = &buf
Чтобы скомпилировать код в начале проблемы, изменив приведенный выше код на:
import (
"fmt"
)
type Father interface {
Hello()
}
type Child struct {
Name string
}
func (s Child)Hello() {
}
func main(){
var buf Father
buf = Child{}
f(&buf)
}
func f(out *Father){
if out != nil{
fmt.Println("surprise!")
}
}