Apa gunanya memiliki petunjuk di Go?

100

Saya tahu bahwa pointer di Go memungkinkan mutasi argumen fungsi, tetapi bukankah akan lebih sederhana jika mereka hanya mengadopsi referensi (dengan const yang sesuai atau qualifier yang dapat berubah). Sekarang kami memiliki pointer dan untuk beberapa tipe bawaan seperti peta dan saluran melalui referensi implisit.

Apakah saya melewatkan sesuatu atau apakah petunjuk di Go hanya merupakan komplikasi yang tidak perlu?

segera
sumber
1
Berikut adalah pertanyaan yang dapat membantu memperjelas: stackoverflow.com/questions/795160/… Ada perbedaan antara melewatkan referensi berdasarkan nilai dan meneruskan referensi dengan benar.
R. Martinho Fernandes
1
Catatan: pertanyaannya adalah tentang Java, tetapi ini juga berlaku di sini.
R. Martinho Fernandes
1
"dan untuk beberapa tipe bawaan seperti peta dan saluran melalui referensi implisit." Tidak, semuanya bernilai pass-by-value di Go. Beberapa jenis adalah (secara informal dijelaskan sebagai) jenis referensi, karena memiliki status internal yang dapat berubah.
newacct
Masalah dengan pertanyaan ini adalah bahwa "referensi" bukanlah satu hal dengan properti yang didefinisikan dengan baik. Istilah "referensi" sangat kabur. Kita dapat melihat dalam jawaban berapa banyak orang yang membaca berbagai hal menjadi kata "referensi". Jadi pertanyaan ini harus menjelaskan tentang perbedaan apa yang ada antara petunjuk Go dan referensi yang ada dalam pikiran pertanyaan tersebut.
mtraceur

Jawaban:

36

Saya sangat suka contoh yang diambil dari http://www.golang-book.com/8

func zero(x int) {
    x = 0
}
func main() {
    x := 5
    zero(x)
    fmt.Println(x) // x is still 5
}

sebagai kontras dengan

func zero(xPtr *int) {
    *xPtr = 0
}
func main() {
    x := 5
    zero(&x)
    fmt.Println(x) // x is 0
}
Piotr Kochański
sumber
42
Pertanyaannya adalah "mengapa kita memiliki petunjuk alih-alih referensi " dan saya tidak mengerti mengapa contoh ini tidak akan berfungsi dengan referensi.
AndreKR
@AndreKR Karena kita dapat memilih apakah akan lewat referensi atau lewat nilai. Ada beberapa contoh di mana keduanya bisa diinginkan.
JDSweetBeat
9
@DJMethaneMan Ini adalah "petunjuk vs. referensi", bukan "petunjuk vs. nilai yang lewat"!
AndreKR
Sebagai komentar samping, referensi lewat ditambahkan di C # 2.0 melalui kata kunci "ref". Tentu saja pointer masih lebih nyaman dalam kasus tertentu, karena kita dapat memiliki pointer ke pointer ke pointer ...
robbie fan
Saya tidak mengerti bagaimana Go seharusnya menjadi salah satu bahasa populer termudah, namun mereka mengandung "fitur" seperti itu ... Ini membingungkan dan tampaknya tidak perlu, setidaknya bagi orang-orang yang menunjukkan ini di sini.
Akito
33

Pointer berguna karena beberapa alasan. Pointer memungkinkan kontrol atas tata letak memori (memengaruhi efisiensi cache CPU). Di Go kita dapat mendefinisikan struktur di mana semua anggota berada dalam memori yang berdekatan:

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

Dalam hal ini Pointstruktur disematkan di dalam LineSegmentstruct. Tetapi Anda tidak selalu dapat menyematkan data secara langsung. Jika Anda ingin mendukung struktur seperti pohon biner atau daftar tertaut, Anda perlu mendukung beberapa jenis penunjuk.

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode
}

Java, Python, dll. Tidak memiliki masalah ini karena tidak memungkinkan Anda untuk menyematkan tipe komposit, jadi tidak perlu membedakan secara sintaks antara penyematan dan penunjuk.

Masalah dengan struktur Swift / C # diselesaikan dengan petunjuk Go

Alternatif yang mungkin untuk mencapai hal yang sama adalah dengan membedakan antara structdan classseperti yang dilakukan C # dan Swift. Tetapi ini memang memiliki batasan. Meskipun Anda biasanya dapat menentukan bahwa suatu fungsi menggunakan struct sebagai inoutparameter untuk menghindari penyalinan struct, fungsi tersebut tidak memungkinkan Anda untuk menyimpan referensi (pointer) ke struct. Ini berarti Anda tidak akan pernah bisa memperlakukan struct sebagai tipe referensi ketika Anda merasa berguna misalnya untuk membuat pengalokasi kumpulan (lihat di bawah).

Alokasi Memori Kustom

Dengan menggunakan pointer, Anda juga dapat membuat pengalokasi pool Anda sendiri (ini sangat disederhanakan dengan banyak pengecekan dihapus untuk hanya menunjukkan prinsip):

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode

  nextFreeNode *TreeNode; // For memory allocation
}

var pool [1024]TreeNode
var firstFreeNode *TreeNode = &pool[0] 

func poolAlloc() *TreeNode {
    node := firstFreeNode
    firstFreeNode  = firstFreeNode.nextFreeNode
    return node
}

func freeNode(node *TreeNode) {
    node.nextFreeNode = firstFreeNode
    firstFreeNode = node
}

Tukar dua nilai

Pointer juga memungkinkan Anda untuk mengimplementasikan swap. Itu menukar nilai dari dua variabel:

func swap(a *int, b *int) {
   temp := *a
   *a = *b
   *b = temp
}

Kesimpulan

Java tidak pernah dapat sepenuhnya menggantikan C ++ untuk pemrograman sistem di tempat-tempat seperti Google, sebagian karena kinerja tidak dapat disetel ke tingkat yang sama karena kurangnya kemampuan untuk mengontrol tata letak dan penggunaan memori (cache miss mempengaruhi kinerja secara signifikan). Go bertujuan untuk menggantikan C ++ di banyak area dan karenanya perlu mendukung petunjuk.

Erik Engheim
sumber
7
C # memungkinkan untuk melewatkan struct dengan referensi. Lihat kata kunci "ref" dan "keluar".
olegz
1
Oke jadi seperti Swift. Saya akan memikirkan cara untuk memperbarui contoh saya.
Erik Engheim
29

Referensi tidak dapat dialihkan, sementara pointer bisa. Ini saja membuat petunjuk berguna dalam banyak situasi di mana referensi tidak dapat digunakan.

zildjohn01
sumber
17
Apakah referensi dapat dialihkan merupakan masalah implementasi khusus bahasa.
crantok
28

Go dirancang untuk menjadi bahasa yang singkat dan minimalis. Oleh karena itu, ini dimulai hanya dengan nilai dan petunjuk. Kemudian, jika perlu, beberapa jenis referensi (irisan, peta, dan saluran) ditambahkan.


Bahasa Pemrograman Go: Desain Bahasa FAQ: Mengapa peta, irisan, dan saluran menjadi referensi sedangkan array adalah nilai?

"Ada banyak sejarah tentang topik itu. Awalnya, peta dan saluran adalah penunjuk secara sintaksis dan tidak mungkin untuk mendeklarasikan atau menggunakan contoh non-penunjuk. Selain itu, kami berjuang dengan cara kerja array. Akhirnya kami memutuskan bahwa pemisahan yang ketat pointer dan nilai membuat bahasa lebih sulit untuk digunakan. Memperkenalkan jenis referensi, termasuk irisan untuk menangani bentuk referensi array, menyelesaikan masalah ini. Jenis referensi menambahkan beberapa kompleksitas yang disesalkan ke bahasa tetapi mereka memiliki pengaruh besar pada kegunaan: Go menjadi a bahasa yang lebih produktif dan nyaman saat diperkenalkan. "


Kompilasi cepat adalah tujuan desain utama dari bahasa pemrograman Go; itu ada biayanya. Salah satu korban tampaknya adalah kemampuan untuk menandai variabel (kecuali untuk konstanta waktu kompilasi dasar) dan parameter sebagai tidak dapat diubah. Sudah diminta, tapi ditolak.


golang-kacang: pergi bahasa. Beberapa umpan balik dan keraguan.

"Menambahkan const ke sistem tipe memaksanya untuk muncul di mana-mana, dan memaksa seseorang untuk menghapusnya di mana-mana jika ada perubahan. Meskipun mungkin ada beberapa manfaat untuk menandai objek yang tidak dapat diubah dalam beberapa cara, kami tidak berpikir kualifikasi tipe const adalah cara yang tepat. untuk pergi."

peterSO
sumber
FWIW, "tipe referensi" di Go juga dapat ditetapkan ulang. Mereka lebih seperti petunjuk implisit?
Matt Joiner
1
Mereka hanyalah sintaks khusus untuk struct yang berisi pointer (dan panjang, kapasitas, ...).
mk12