Die Wahl zwischen make und new in Go
Daniel Hayes
Full-Stack Engineer · Leapcell

Go bietet zwei häufig verwendete Möglichkeiten, Speicher zu reservieren: make
und new
. Obwohl beide zur Speicherreservierung verwendet werden, sind ihre Rollen und Anwendungsszenarien recht unterschiedlich. Das Verständnis der Unterschiede zwischen diesen beiden ist entscheidend für das Schreiben von effizientem und wartbarem Go-Code. Dieser Artikel analysiert gründlich die Unterschiede zwischen make
und new
, ihre geeigneten Szenarien und bietet einige Anwendungstipps.
Grundlegende Unterschiede zwischen make
und new
new: Erstellt Nullwert von Zeigertypen
new
ist ein Schlüsselwort in Go, das für die Speicherreservierung verwendet wird. Seine Funktion besteht darin, einen Speicherblock für einen Typ zu reservieren und einen Zeiger auf diesen Speicher zurückzugeben. Der von new
reservierte Speicher wird mit dem Nullwert des Typs initialisiert.
Beispiel: Verwendung von new
package main import "fmt" func main() { var p *int = new(int) fmt.Println(*p) // Ausgabe: 0, der von `new` für int reservierte Speicher wird mit dem Nullwert initialisiert }
- Rückgabetyp:
new
gibt einen Zeiger auf den Typ zurück. - Nullwert-Initialisierung:
new
initialisiert den reservierten Speicher mit dem Nullwert des Typs. Für den Typint
ist der Nullwert beispielsweise0
; für den Typstring
ist es ein leerer String""
.
make: Initialisiert Slices, Maps und Channels
make
ist eine spezielle integrierte Funktion in Go, die speziell zur Initialisierung von drei integrierten Datentypen verwendet wird: Slice, Map und Channel. Im Gegensatz zu new
gibt make
keinen Zeiger auf den reservierten Speicher zurück, sondern das initialisierte Objekt selbst.
Beispiel: Verwendung von make
package main import "fmt" func main() { // Slice initialisieren s := make([]int, 5) fmt.Println(s) // Ausgabe: [0 0 0 0 0] // Map initialisieren m := make(map[string]int) m["age"] = 30 fmt.Println(m) // Ausgabe: map[age:30] // Channel initialisieren ch := make(chan int, 2) ch <- 1 fmt.Println(<-ch) // Ausgabe: 1 }
- Rückgabetyp:
make
gibt das Objekt selbst (Slice, Map oder Channel) zurück, nicht einen Zeiger. - Speicherreservierung und -initialisierung:
make
reserviert nicht nur Speicher, sondern initialisiert auch die Datenstruktur selbst. Bei der Initialisierung eines Slices reserviertmake
beispielsweise das zugrunde liegende Array und legt seine Länge und Kapazität fest.
Zusammenfassung der wichtigsten Unterschiede
-
Zweck:
new
: Reserviert Speicher und gibt einen Zeiger auf den Typ zurück.make
: Initialisiert und gibt ein Slice-, Map- oder Channel-Objekt zurück.
-
Rückgabewert:
new
: Gibt einen Zeiger auf den Typ zurück.make
: Gibt das initialisierte Objekt selbst zurück.
-
Anwendbare Typen:
new
: Alle Typen.make
: Slice, Map und Channel.
-
Initialisierung:
new
: Gibt Nullwert zurück.make
: Initialisiert gemäß dem Typ der Datenstruktur.
Tipps zur Verwendung von make
und new
Tipps zur Verwendung von new
Geeignet für Struct-Typen:
new
wird oft verwendet, um Speicher für Structs zu reservieren und Zeiger auf diese zurückzugeben. Es ist wichtig zu beachten, dass der Anfangswert eines Struct-Zeigers der Nullwert des Structs ist.
Beispiel: Verwenden von new
, um Speicher für einen Struct zu reservieren
type Person struct { Name string Age int } func main() { p := new(Person) fmt.Println(p) // Ausgabe: &{ 0} fmt.Println(p.Name) // Ausgabe: leerer String fmt.Println(p.Age) // Ausgabe: 0 }
- Von
new
erstellte Zeiger: Danew
einen Zeiger auf den Struct zurückgibt, können Sie seine Felder direkt mitp.Name
oderp.Age
ändern.
Tipps zur Verwendung von make
Initialisieren von Slices mit angegebener Kapazität:
make
kann verwendet werden, um einen Slice mit einer angegebenen Länge und Kapazität zu initialisieren. Mit make
können Sie das zugrunde liegende Array effizient reservieren und den Slice initialisieren.
Beispiel: Verwenden von make
, um einen Slice mit Kapazität zu initialisieren
// Initialisiert einen Slice mit der Länge 5 und der Kapazität 10 s := make([]int, 5, 10) fmt.Println(len(s), cap(s)) // Ausgabe: 5 10
- Angeben der Map-Kapazität während der Initialisierung: Wenn Sie eine Map mit
make
erstellen, können Sie ihre anfängliche Kapazität angeben. Dies hilft, die Leistung zu optimieren, indem mehrere Speichererweiterungen beim Einfügen von Elementen vermieden werden.
Beispiel: Verwenden von make
zum Initialisieren einer Map
m := make(map[string]int, 10) // Legt die anfängliche Kapazität auf 10 fest m["age"] = 30 m["height"] = 175 fmt.Println(m) // Ausgabe: map[age:30 height:175]
- Initialisieren von gepufferten Channels: Verwenden Sie
make
, um einen gepufferten Channel zu erstellen und die Puffergröße des Channels anzugeben. Dies ist in der Concurrent-Programmierung sehr nützlich.
Beispiel: Verwenden von make
zum Erstellen eines gepufferten Channels
ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<-ch) // Ausgabe: 1
Auswählen der geeigneten Speicherreservierungsmethode
- Anwendungsszenario für Structs: Wenn Sie nur einen Zeiger auf einen Struct benötigen und keine besonderen Anforderungen während der Initialisierung haben, ist die Verwendung von
new
ein einfacher und üblicher Ansatz. - Anwendungsszenario für Slices, Maps und Channels: Wenn Sie einen Slice, eine Map oder einen Channel initialisieren und deren Inhalt möglicherweise ändern müssen, ist
make
die bessere Wahl – insbesondere, wenn Sie die Kapazität im Voraus angeben müssen.
Leistungsüberlegungen zu make
und new
- Overhead bei der Speicherreservierung: Beim Initialisieren von Slices, Maps und Channels reserviert
make
nicht nur Speicher, sondern führt auch eine Typinitialisierung durch, was zu zusätzlichem Overhead führen kann. Im Gegensatz dazu reserviertnew
nur Speicher und initialisiert ihn mit dem Nullwert, sodass der Overhead relativ gering ist. - Vermeiden unnötiger Speicherreservierungen: Für Typen wie Slices, Maps oder Channels wird empfohlen, bei Verwendung von
make
eine geeignete Kapazität anzugeben, um die Anzahl der Speicherneuzuweisungen zu reduzieren.
Häufige Fehlverwendungen
- Falsche Verwendung von
new
zum Erstellen von Slices oder Maps: Wennnew
mit Slices, Maps oder Channels verwendet wird, gibt es nur den Nullwert des Typs zurück und führt keine Initialisierung durch. Wenn Sie alsonew
verwenden, um einen Slice, eine Map oder einen Channel zu erstellen, und versuchen, direkt auf deren Inhalt zuzugreifen, führt dies zu einem Laufzeitfehler.
Falsches Beispiel: Falsche Verwendung von new
zum Erstellen einer Map
m := new(map[string]int) // Falsch: gibt einen Zeiger zurück, keine initialisierte Map m["age"] = 30 // Laufzeitfehler: m ist nil
Korrekte Beispiel: Sie sollten make
verwenden, um eine Map zu initialisieren.
m := make(map[string]int) m["age"] = 30
Zusammenfassung
In Go sind make
und new
beides Schlüsselwörter für die Speicherreservierung, und obwohl ihre Funktionen ähnlich sind, weisen sie deutliche Unterschiede auf.
new
wird verwendet, um Speicher für einen Typ zu reservieren und einen Zeiger zurückzugeben, und ist für die meisten Typen geeignet;
während make
hauptsächlich zum Initialisieren von Slices, Maps und Channels verwendet wird und stärkere Initialisierungsfunktionen bietet.
new
: Geeignet zum Erstellen von Zeigern auf Struct-Typen und andere grundlegende Typen und initialisiert den Speicher mit dem Nullwert.make
: Wird zum Initialisieren von Slices, Maps und Channels verwendet, unterstützt das Angeben der Kapazität und schließt die interne Initialisierung ab.
Das Verständnis der verschiedenen Anwendungsszenarien und Leistungsauswirkungen dieser beiden wird Ihnen helfen, effizienteren und wartbareren Go-Code zu schreiben.
We are Leapcell, your top choice for hosting Go projects.
Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:
Multi-Language Support
- Develop with Node.js, Python, Go, or Rust.
Deploy unlimited projects for free
- pay only for usage — no requests, no charges.
Unbeatable Cost Efficiency
- Pay-as-you-go with no idle charges.
- Example: $25 supports 6.94M requests at a 60ms average response time.
Streamlined Developer Experience
- Intuitive UI for effortless setup.
- Fully automated CI/CD pipelines and GitOps integration.
- Real-time metrics and logging for actionable insights.
Effortless Scalability and High Performance
- Auto-scaling to handle high concurrency with ease.
- Zero operational overhead — just focus on building.
Explore more in the Documentation!
Follow us on X: @LeapcellHQ