ویژگی های Go 1.16

توسط mrbardia72
زمان خواندن 2 دقیقه
ویژگی های Go 1.16

Go 1.16 ، در واقع ۱۷امین نسخه اصلی زبان برنامه نویسی Go می باشد ، به تازگی منتشر شده است

در این مقاله ، برخی از ویژگی های برجسته این نسخه را بررسی خواهیم کرد

Native support for Apple silicon

درواقع Go از زمان تأسیس ،روی قابلیت حمل و نقل  در بین سیستم عامل ها و معماری های مختلف را در اولویت خود قرار داده است و در  این پشتیبانی  از طیف گسترده ای از ترکیبات سیستم عامل و معماری منعکس می شود

در چند ماه گذشته ، انتشار اولین 64 ARM Mac اپل به دلیل جهش چشمگیر در پردازنده ، پردازنده گرافیکی و عملکرد باتری ، یکی از موضوعات غالب در میان توسعه دهندگان بوده است. پروژه Go بلافاصله با افزودن پشتیبانی محلی برای ARM Mac ها از طریق متغیرهای محیطی GOOS = darwin و GOARCH = arm64 پاسخ داده است

اگر Mac M1 دارید ، اکنون می توانید برنامه های Go را بطور طبیعی در رایانه خود بسازید و اجرا کنید ، و اگر از سیستم عامل دیگری یا Mac مبتنی بر Intel استفاده می کنید ، می توانید با تنظیم زیر برنامه را اجرا کنید

GOARCH=arm64 GOOS=darwin go build myapp

Native embedding of static files

یکی از بهترین موارد در مورد Go این است که برنامه های کامپایل شده می توانند به صورت یک فایل باینری بدون وابستگی توزیع  اجرا شوند. این برتری وقتی برنامه به فایلهای static مانند الگوهای HTML ، پرونده های انتقال پایگاه داده ، دارایی های برنامه وب مانند JavaScript یا پرونده های تصویری مانند این پرونده ها متکی باشد ، باید تا حدی با باینری توزیع شود ، مگر اینکه در باینری تعبیه شده باشد. به کمک بسته شخص ثالث مانند pkger یا packr. با انتشار Go 1.16 ، اکنون می توان فایلهای static را از طریق بسته جدید تعبیه شده در یک باینری Go قرار داد.

در اینجا اساسی ترین مثال برای نحوه عملکرد این ویژگی وجود دارد. با فرض اینکه شما یک فایل sample.txt دارید که محتوای آن در زیر نشان داده شده است

Hello from text file

و یک فایل main.go در همان دایرکتوری با محتوای زیر وجود دارد

package main

import (
    _ "embed"
    "fmt"
)

//go:embed sample.txt
var text string

func main() {
    fmt.Print(text)
}

دستورالعمل go: embed که در بالای متغیر متن قرار گرفته است ، به کامپایلر دستور می دهد تا محتویات فایل sample.txt را به عنوان رشته ای در متغیر متن قرار دهد. اگر برنامه را با go build بسازید و باینری حاصل را به مکان دیگری منتقل کنید ، متوجه خواهید شد که با اجرای آن محتویات فایل جاسازی شده را به خروجی استاندارد چاپ می کنید. به این دلیل که تمام محتویات فایل sample.txt در داخل باینری گنجانده شده است تا بتواند به صورت زیر توزیع شود:

برای یک مثال واقعی تر ، بگذارید بگوییم ما یک پروژه برنامه وب با ساختار دایرکتوری زیر داریم

.
├── assets
│   ├── css
│   │   └── style.css
│   └── js
│       └── script.js
├── go.mod
├── index.html
├── main.go
└── random 

ما می توانیم همه پرونده ها را در پوشه دارایی ها و پرونده index.html به این صورت جاسازی کنیم

package main

import (
    "embed"
    "net/http"
)

//go:embed assets/*
var assets embed.FS

//go:embed index.html
var html []byte

func main() {
    fs := http.FileServer(http.FS(assets))
    http.Handle("/assets/", fs)
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("Content-Type", "text/html")
        w.Write(html)
    })
    http.ListenAndServe(":8080", nil)
}

نوع FS برای embedding درختی از پرونده ها ، مانند دایرکتوری های وب سرور ، مثل مثال فوق هستند. برای embedding یک فایل single مانند index.html ، یک متغیر از نوع رشته یا [] بایت بهترین است. اگر برنامه را ایجاد و اجرا کنید و به http: // localhost: 8080 بروید ، محتوای فایل HTML را با assets های static که به درستی اعمال شده اند مشاهده خواهید کرد

Some gotchas

قبل از استفاده از دستورالعمل // go: embed ، باید بسته تعبیه شده را وارد کنید. در صورت عدم موفقیت در این مورد ، با خطا مواجه خواهید شد:

$ go run main.go
# command-line-arguments
./main.go:8:3: //go:embed only allowed in Go files that import "embed"

اگر مستقیماً از شناسه های صادر شده استفاده نمی کنید ، آن را با یک خط زیر پیشوند قرار دهید:

import (
    _ "embed"
)

نکته دیگری که باید از آن آگاه باشید این است که // go: embed فقط روی متغیرهای سطح بسته کار می کند. اگر سعی کنید از آن در داخل یک تابع استفاده کنید ، کد شما کامپایل نمی شود:

package main

import (
    _ "embed"
    "fmt"
)

func main() {
    //go:embed index.html
    var html string
    fmt.Println(html)
}
$ go run main.go
# command-line-arguments
./main.go:9:4: go:embed cannot apply to var inside func

Module-aware mode is enabled by default

معرفی ماژول های Go در Go 1.11 دور شدن و از مفهوم  GOPATH  برای مدیریت وابستگی استفاده می کردند. در آن نسخه اولیه و Go 1.12 ، ماژول ها هنوز آزمایشی بودند و باید با متغیر محیط GO111MODULE  فعال میشدند. Go 1.13 اطمینان حاصل کرد که حالت آگاه از ماژول به طور خودکار فعال می شود هر زمان که یک پرونده go.mod در فهرست  فعلی یا یک فهرست اصلی وجود داشته باشد حتی اگر فهرست درون GOPATH باشد و این حالت در Go 1.14 و 1.15 همچنان ادامه دارد.

با انتشار Go 1.16 ، متغیر GO111MODULE اکنون به طور پیش فرض روشن هست که این بدان معنی است که حالت آگاه از ماژول به طور پیش فرض فعال است بدون توجه به اینکه یک فایل go.mod در فهرست فعلی وجود دارد.

در سایر تغییرات مرتبط ، go build و go test دیگر به طور پیش فرض فایل های go.mod و go.sum را اصلاح نمی کند. در عوض ، در صورت نیاز به افزودن یا به روزرسانی یک ماژول یک خطا گزارش خواهد شد. سپس می توانید از go mod tidy استفاده کنید یا بروید تا شرایط را بر اساس آن تنظیم کنید.

دستور go install هم اکنون از ماژول آگاه است و این بدان معناست که اگر پرونده go.mod را در پوشه فعلی یا هر پوشه اصلی تحت تأثیر قرار ندهد ، تأثیری نخواهد داشت. همچنین ، اکنون می تواند شماره نسخه را به عنوان پسوند در نظر بگیرد. مثلا:

$ go install github.com/example@v3.5.0

Package authors can now retract old versions

از Go 1.16 ، دستورالعمل جدید جمع آوری در پرونده های go.mod در دسترس خواهد بود. این به نویسندگان بسته اجازه می دهد نسخه های بسته قدیمی را به عنوان بسته های  ناامن یا خراب یا اگر نسخه ای ناخواسته منتشر شده باشد ، علامت گذاری کنند. در اینجا نحوه استفاده از آن آمده است:

module example

go 1.16

retract v1.1.1 // retract single version
retract [v1.1.1, v1.3.2] // closed interval, so anything between v1.1.1 and v1.3.2

The io/ioutil package is now deprecated

تمام بسته ioutil اکنون در Go 1.16 منسوخ شده و عملکردهای آن به بسته های دیگر منتقل شده است. برای اینکه واضح باشد ، کد موجودی که از این بسته استفاده می کند همچنان به کار خود ادامه می دهد ، اما از شما خواسته می شود که به تعاریف جدید در بسته های io و os مهاجرت کنید.

انتقال کد با استفاده از ioutil باید ساده باشد. یک روش محبوب در این بسته روش ReadAll () است که اغلب برای خواندن کل متن پاسخ از درخواست HTTP در یک تکه بایت استفاده می شود. این روش به بسته io منتقل شده است:

resp, err := http.Get(url)
if err != nil {
    return err
}

defer resp.Body.Close()

// old way: body, err := ioutil.ReadAll(resp.Body)
body, err := io.ReadAll(resp.Body)
if err != nil {
    return err
}

لیست کامل روش های جدید  io / ioutil  در زیر نشان داده شده است:

ioutil.Discard => io.Discard

ioutil.NopCloser => io.NopCloser

ioutil.ReadAll => io.ReadAll

ioutil.ReadDir => os.ReadDir

ioutil.ReadFile => os.ReadFile

ioutil.TempDir => os.MkdirTemp

ioutil.TempFile => os.CreateTemp

ioutil.WriteFile => os.WriteFile

The io/fs package

بهبودهای کتابخانه استاندارد Go با افزودن بسته های io / fs و test / testfs از این نسخه خارج نمی شوند. این بسته های جدید تجزیه سیستم فایل را در آزمایشات آسان تر می کنند که بدون در نظر گرفتن سیستم عاملی که در حال اجرا هستند ، به راحتی قابل تکرار هستند. دسترسی به پرونده ها نیز بسیار سریعتر خواهد بود و پس از آن مجبور به پاک سازی پرونده های موقت نخواهید شد.

رابط FS ارائه شده توسط بسته io / fs کاملاً ساده است:

type FS interface {
    Open(name string) (File, error)
}

برای پیاده سازی این رابط تنها کاری که شما باید انجام دهید یک روش Open است که می تواند یک فایل را در یک مسیر باز کند و یک شی را که از رابط fs.File پیاده سازی می کند برگرداند که در زیر نشان داده شده است:

type File interface {
    Stat() (FileInfo, error)
    Read([]byte) (int, error)
    Close() error
}

چیزی که از رابط فوق متوجه خواهید شد کمبود روش هایی است که به شما امکان می دهد پرونده ها را اصلاح کنید. این به این دلیل است که بسته io / fs برخلاف Afero که از این نظر کاملتر است ، فقط یک رابط فقط خواندنی برای سیستم های فایل فراهم می کند. دلیل این تصمیم این است که انتزاع خواندن در مقایسه با نوشتن که بیشتر درگیر است آسان تر است.

Notable mentions

ابزار vet یک هشدار دهنده ای را هنگام تماس نامعتبر به testing.T یا testing.B ارائه می دهد. روش های Fatal ، Fatalf یا FailNow از درون یک مجموعه خاص ایجاد شده در طی یک آزمون یا معیار ساخته می شوند. دلیل این امر این است که این روشها به جای عملکرد آزمایشی یا معیار از Goroutine خارج می شوند:

package main

import "testing"

func TestFoo(t *testing.T) {
    go func() {
        if true {
            t.Fatal("Test failed") // exits the goroutine instead of TestFoo
        }
    }()
}

فراخوانی روشهای فوق را می توان با t.Error () جایگزین کرد تا نشانه عدم موفقیت در آزمون و بیانیه بازگشت برای خروج از Goroutine باشد:

package main

import "testing"

func TestFoo(t *testing.T) {
    go func() {
        if true {
            t.Error("Test failed.")
            return
        }
    }()
}