Google Engineers Unveil Go Language to Accelerate Software Builds

The Go Programming Language — Google TechTalks

Google TechTalks

By decoupling interfaces from type implementations and enforcing strict dependency tracking, Go eliminates compiler drag to deliver near-instantaneous build times alongside safe, lightweight concurrency.

AI-assisted summary of The Go Programming Language — original source. Check the original for full context.

Source published:

Key ideas

  • Go was developed at Google by Robert Griesemer, Ken Thompson, Rob Pike, Ian Lance Taylor, and Russ Cox to resolve the cognitive overhead, sprawling dependencies, and slow build times of modern systems programming. (0:23)
  • The language achieves near-instantaneous builds through strict dependency management, compiling a 130,000-line standard library and runtime repository in roughly 8 seconds on a single laptop core. (6:54)
  • Go introduces CSP-style concurrency via lightweight, dynamically stacked goroutines multiplexed over OS threads, demonstrated by managing 100,000 communicating goroutines in roughly 1.5 seconds. (42:54)
  • Interfaces are satisfied entirely implicitly without explicit 'implements' declarations, separating behavioral contracts from structural type definitions. (30:29)
  • Two distinct compiler toolchains were launched at public release: Ken Thompson's fast C-based suite (6g/8g/5g) and Ian Lance Taylor's optimizing GCC frontend (gccgo). (46:56)

Detailed notes

## Background Published by Google TechTalks on YouTube on November 10, 2009, this presentation features Rob Pike delivering an early preview of the Go programming language shortly before its open-source release. The description frames Go as a new, experimental systems programming language engineered to make development fast—enabling massive binaries to compile in seconds—while providing native concurrency, garbage collection, and a streamlined type system.

## TL;DR Rob Pike unpacks why Go was conceived by himself, Ken Thompson, and Robert Griesemer to resolve the crippling compile times, dependency bloat, and multicore friction found in modern C++ and Java environments. He demonstrates Go's foundational design choices, including strict dependency trees for instantaneous compilation, untyped arbitrary-precision constants, and implicit interface satisfaction that discards rigid inheritance hierarchies. The talk highlights Go's CSP-style concurrency model, demonstrating both client-server request multiplexing and a live 100,000-goroutine daisy chain executing on a laptop in 1.5 seconds. Pike concludes by surveying the dual compiler implementations (Ken Thompson's suite and GCC-based `gccgo`), developer tools like `gofmt` and `godoc`, and why features like generics were deferred pending further design work.

## Atmosphere & style The lecture carries the measured, unpretentious confidence of veteran Unix architects unveiling a pragmatic solution to everyday engineering pain. Pike delivers his remarks in a dry, conversational tone, balancing candid humor about enduring hours-long C++ builds with genuine excitement about making programming feel fun and nimble again. The room has the intimate, focused hum of an internal engineering seminar, punctuated by terminal-based live benchmarks that quietly subvert contemporary expectations of systems-level compilation.

## Bulletin - [00:23](https://www.youtube.com/watch?v=rKnDgT73v8s&t=23s) Go was developed at Google by Robert Griesemer, Ken Thompson, Rob Pike, Ian Lance Taylor, and Russ Cox to resolve the cognitive overhead, sprawling dependencies, and slow build times of modern systems programming. - [06:54](https://www.youtube.com/watch?v=rKnDgT73v8s&t=414s) The language achieves near-instantaneous builds through strict dependency management, compiling a 130,000-line standard library and runtime repository in roughly 8 seconds on a single laptop core. - [42:54](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2574s) Go introduces CSP-style concurrency via lightweight, dynamically stacked goroutines multiplexed over OS threads, demonstrated by managing 100,000 communicating goroutines in roughly 1.5 seconds. - [30:29](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1829s) Interfaces are satisfied entirely implicitly without explicit 'implements' declarations, separating behavioral contracts from structural type definitions. - [46:56](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2816s) Two distinct compiler toolchains were launched at public release: Ken Thompson's fast C-based suite (6g/8g/5g) and Ian Lance Taylor's optimizing GCC frontend (gccgo).

## Key takeaways - [06:54](https://www.youtube.com/watch?v=rKnDgT73v8s&t=414s) Go bridges the gap between static systems languages and dynamic scripting languages, offering compile-time type and memory safety with rapid iteration. - [03:08](https://www.youtube.com/watch?v=rKnDgT73v8s&t=188s) Hardware scaling has shifted toward multicore architectures while single-thread clock speeds have flatlined, leaving legacy systems languages ill-equipped for modern concurrent demands. - [10:17](https://www.youtube.com/watch?v=rKnDgT73v8s&t=617s) Go rejects traditional object-oriented inheritance hierarchies in favor of orthogonal language concepts and type composition. - [13:05](https://www.youtube.com/watch?v=rKnDgT73v8s&t=785s) Transitive dependency metadata is embedded directly into compiled package objects, eliminating the need to re-parse indirect upstream source files and ensuring linear compilation scaling. - [21:01](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1261s) Numeric constants in Go are arbitrary-precision and untyped at compile time, eliminating unnecessary type casts and precision truncation errors. - [25:09](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1509s) Go enforces visibility strictly through Unicode capitalization of top-level identifiers and fields, removing the need for public or private keywords. - [27:32](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1652s) Methods can be defined on any user-defined type, including primitive-based types like custom integers, using explicit receiver parameters without an implicit 'this' or 'self'. - [33:14](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1994s) Interface satisfaction is completely implicit, allowing small abstractions like io.Writer to achieve widespread composability across the standard library. - [36:15](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2175s) Channels are first-class typed synchronization primitives that can be passed as arguments, stored in data structures, and sent across other channels. - [40:27](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2427s) The select control structure coordinates asynchronous events and teardown routines by waiting on multiple channel operations simultaneously. - [15:27](https://www.youtube.com/watch?v=rKnDgT73v8s&t=927s) Garbage collection is considered an absolute requirement for safe, ergonomic concurrency because it removes manual tracking of object ownership across threads. - [52:18](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3138s) The gofmt tool enforces a single canonical formatting standard across all Go code, eliminating developer debates over code style. - [54:15](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3255s) Generics were deliberately omitted from the initial release because the team prioritized finding a sound design over rushing an unproven specification, with built-in slices, maps, and empty interfaces covering immediate needs. - [56:30](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3390s) Pointer arithmetic is intentionally excluded from the language to guarantee complete memory safety and runtime stability.

## Detailed walkthrough ### [00:23](https://www.youtube.com/watch?v=rKnDgT73v8s&t=23s) Introducing Go: Origins and Philosophy

Rob Pike introduces the Go programming language, giving an early preview shortly before its planned public open-source release on November 10th. Go is framed as a new, experimental, concurrent, garbage-collected systems language. It is marked as experimental because the team does not claim it is an answer to everything, but rather a set of ideas mature enough to present to the world. It is geared for systems work—such as writing high-performance web servers—providing deeper control than languages like Java or Python while remaining flexible enough for front-ends and general application development. Presenting a standard "Hello, world" snippet featuring Japanese characters, Pike notes that while the syntax looks vaguely C-like, it harbors intentional design departures.

```mermaid flowchart LR accTitle: Evolution of the Go Team accDescr: Timeline showing the initial founders of Go followed by later key contributors. subgraph S1["Late 2007: Conception"] RG["Robert Griesemer"] KT["Ken Thompson"] RP["Rob Pike"] end subgraph S2["Mid 2008: Core Expands"] IT["Ian Lance Taylor"] RC["Russ Cox"] end S1 --> S2 ```

The effort originated late in 2007 when Robert Griesemer, Ken Thompson, and Rob Pike began brainstorming solutions to the software development friction they experienced daily. After several months of preliminary design, they had constructed a toy compiler by mid-2008. At that stage, Ian Lance Taylor and Russ Cox joined the project, forming the core five-person team that drove the initial implementation with broader internal collaboration. The primary motivation was that developing software had simply become too slow: builds took too long, toolchains crawled, and the cognitive overhead was stifling. As Pike puts it:

> “We want a program fast. We want a program fun. And to make programming fun, you have to find ways to speed things up and make things work better, not just the code itself but the process of writing the code needs to be efficient.”

- Designed as an open-source systems language suited for web infrastructure and systems tooling. - Core design team: Robert Griesemer, Ken Thompson, Rob Pike, Ian Lance Taylor, and Russ Cox. - Timeline: Conceived in late 2007; first working prototype compiler completed by mid-2008; targeted public release on November 10, 2009.

---

[03:08](https://www.youtube.com/watch?v=rKnDgT73v8s&t=188s) The Breakdown of Modern Systems Programming

Pike observes that no major systems language had emerged in over a decade, even though computing environments had changed radically over that same period. Modern programming has degraded into an ever-expanding layering process where libraries have exploded in size and formed tangled thickets of uncontrolled dependencies. Meanwhile, computing shifted decisively to ubiquitous networking, client-server models, massive clusters, and multicore processors. Despite these shifts, developers still rely on single-processor legacy languages that offer little direct help for dealing with concurrency or distributed architectures.

Compounding the problem, the hardware scaling that historically bailed out inefficient workflows has ground to a halt. While processor counts per machine continue to climb, single-thread clock speeds have largely flatlined as Moore's law peters out. In response to rigid, compile-heavy languages like C, C++, and Java, developers increasingly flee toward dynamic languages like Python, Ruby, and JavaScript. As Robert Griesemer observed, dynamic typing restores programming fun because its type systems do not obstruct day-to-day workflow. Yet dynamic languages lack static compile-time guarantees, creating an artificial trade-off where developers feel forced to pick between being productive or being safe. Pike argues that features like `const` in C/C++ or rigid object-oriented class hierarchies often create more friction than they resolve:

> “And in short, therefore, with the way type systems work today, you can be productive or you can safe, but you can’t really be both. And that seems like a shame; we should be able to fix that.”

- Computing shifted to massive clusters, multicore processors, and distributed networks without commensurate improvements in systems languages. - Hardware clock speeds have leveled off, eliminating automatic single-thread performance gains while codebases continue to balloon. - Standard OOP taxonomies force developers into endless refactoring of inheritance trees that reflect language constraints rather than real problem domains. - Well-intentioned features, such as `const` qualifiers in C++, illustrate how theoretical safeguards often introduce excessive practical complexity.

---

[06:54](https://www.youtube.com/watch?v=rKnDgT73v8s&t=414s) Design Goals and Compilation Speed Demo

Rather than trying to patch existing languages by piling on more library layers, the team decided to start from scratch with a clean slate. Go aims to combine the raw execution efficiency, safety, and control of a true compiled language with the expressive convenience and fluidity of an interpreted dynamic language. Type safety and memory safety are treated as absolute prerequisites to eliminate dangerous pointer arithmetic errors and bad memory addresses. Concurrency and communication primitives are built directly into the language syntax, backed by an efficient, latency-free garbage collector that automates tedious manual memory management.

```mermaid flowchart TD accTitle: Go Design Philosophy Balance accDescr: Visualizing how Go combines dynamic language ease with compiled language performance. subgraph GO["Go Language Architecture"] direction TB EASE["Dynamic Simplicity<br/>• Rapid iterations<br/>• Lightweight types<br/>• Automated GC"] PERF["Systems Rigor<br/>• Direct compilation<br/>• Strict type & memory safety<br/>• Concurrency primitives"] end EASE --- PERF ```

A paramount goal was restoring instantaneous build times so developers no longer waste time waiting on compilers. Pike demonstrates Go's compilation performance live on his laptop:

```bash # Library compilation benchmark $ make clean $ time make # Output: ~200ms real execution time for a 1,300-line library

# Complete Go repository build $ make clean # Compiling 120,000 to 130,000 lines of source code # (Includes runtime, regular expressions, flag parsers, GC, and concurrency primitives) # Builds on a single laptop processor in roughly 8 seconds ```

Pike stresses that these numbers are not the result of distributed build systems running across internal Google server farms; they represent a single laptop core compiling, linking, and emitting native code. Routine local development builds regularly finish within 100 to 200 milliseconds, validating the team's thesis that smart dependency modeling can eliminate compiler drag entirely.

- **Primary Goal:** Retain the execution speed and safety of static compilation with the development speed of dynamic scripting languages. - **Safety Guarantees:** Comprehensive type safety and memory safety with no unchecked bad address dereferencing. - **Garbage Collection:** Systems-level automated memory reclamation designed to eliminate manual bookkeeping without introducing latency spikes. - **Build Metrics:** Full library compiles (~1,300 LOC) run in roughly 200 milliseconds; the full repository (~130,000 LOC of standard libraries, runtime, and tools) builds end-to-end in approximately eight seconds on a single core.

[10:17](https://www.youtube.com/watch?v=rKnDgT73v8s&t=617s) Fast Compilation and Core Design Principles

Reflecting on the compilation demo, Rob Pike notes that even a few seconds feels long once you are accustomed to true instantaneous builds. The ideal compilation cycle should happen in "carriage-return time"—the moment you press Enter, the software should already be compiled and ready. Pike jokes about the productivity shift this brings to engineers at Google, where long C++ builds previously dominated office culture: “Sometimes hours and hours would go by, then people finally showed up to my office and said, 'Hey, you didn't answer my mail.' I'd have to look in the mail, but I was actually getting work done.”

```mermaid flowchart TD accTitle: Go Design Philosophy accDescr: The foundational principles guiding Go's design choices.

subgraph Principles["Core Principles"] Orth["Orthogonal Concepts<br/>(Interface separated from implementation)"] Grammar["Regular, Conflict-Free Grammar<br/>(Parsable without a symbol table)"] Typing["Reduce Typing<br/>(Type inference + avoid rigid type hierarchies)"] end

subgraph Outcomes["Practical Outcomes"] Tools["Fast IDEs, linters, and debuggers"] Simplicity["Cleaner, concise source code"] Modularity["Deeply object-oriented without inheritance overhead"] end

Orth --> Modularity Grammar --> Tools Typing --> Simplicity ```

To create Go, the team established several foundational principles that distinguish it from mainstream contemporary languages:

- **Orthogonality of Concepts:** Rather than combining ideas into heavy features, Go keeps concepts strictly orthogonal. In particular, type implementation and interface definitions are decoupled entirely: “They're two separate things, they don't interact and you don't define them together, you define them in separate objects and that makes a lot of stuff cleaner.” A compact set of orthogonal features covers the language problem space far better than a sprawl of overlapping rules. - **Regular, Conflict-Free Grammar:** Go’s grammar can be parsed cleanly without requiring a symbol table, and it is free of grammatical conflicts. This regularity dramatically simplifies writing developer tooling, including IDEs, syntax highlighters, editor plugins, and debuggers. - **Reducing "Typing":** Pike emphasizes this principle as a double meaning: reducing keystrokes and reducing type clutter. Developers in modern object-oriented languages spend far too much time typing repetitive declarations such as `Foo *f = new Foo()`, when the type system ought to infer the type automatically while preserving strict type safety. - **Ditching Type Hierarchies:** Go rejects the premise that programming requires constructing deep inheritance trees: “We just think that programming by constructing type hierarchies is not the way to go.” Despite abandoning traditional inheritance, Pike stresses that Go remains a deeply object-oriented language—arguably more so than Java or C++.

---

[13:05](https://www.youtube.com/watch?v=rKnDgT73v8s&t=785s) The Type System, Untyped Constants, and Dependency Scaling

Go adopts a concise syntax paired with a lightweight type system. One major design choice was the total elimination of implicit type conversions. In languages like C, the "usual arithmetic conversions" frequently cause subtle bugs and unexpected behavior. In Go, conversions must be explicit. To prevent this requirement from forcing developers to write repetitive manual casts, Go introduces a fundamentally different constant model. Numbers in Go source code are arbitrary-precision, untyped values:

> “You won't see `0x80ULL` in a Go program. The ULL is completely pointless, you can't even say it, numbers are just numbers.”

The language handles packages and external dependencies through strict rules. Every dependency must be explicitly imported, and the compiler immediately rejects a program if an unused import is left in the code. This strictness keeps builds guaranteed-minimal and allows build tools to map dependency graphs without ambiguity.

```mermaid flowchart LR accTitle: Transitive Dependency Propagation in Go accDescr: How Go compilers pull exported metadata up the dependency chain so leaf files never need to be read.

C["C.go<br/>(Compiled first)"] --> B["B.go<br/>(Imports C; compiler embeds C's exports into B's object)"] B --> A["A.go<br/>(Imports B; compiler reads ONLY B's object)"] ```

To accelerate large-scale builds, the Go compiler pulls transitive dependency information directly up the compilation tree:

- When compiling a chain where `A.go` imports `B.go`, which in turn imports `C.go`, packages must be compiled in bottom-up order (`C`, then `B`, then `A`). - When compiling `B.go`, the compiler embeds all exported interface and type information required from `C` directly into `B`'s compiled package metadata. - When it is time to compile `A.go`, the compiler only opens `B`’s compiled package object. It never opens, parses, or consults `C.go` or `C`'s object file. - At massive code scales, skipping the inspection of transitively depended-on files turns quadratic or exponential file I/O operations into linear, instantaneous compilation times.

---

[15:27](https://www.youtube.com/watch?v=rKnDgT73v8s&t=927s) Concurrency, Goroutines, and Garbage Collection

For systems and network servers, Go provides a concurrency model based on concurrent processes called **goroutines**, communicating over typed channels. The designers intentionally chose the new word *goroutine* because they are neither standard OS threads, nor heavyweight processes, nor traditional coroutines:

- **Multiplexed Execution:** Goroutines are lightweight execution contexts managed and multiplexed onto operating system threads entirely by the Go runtime. - **Segmented / Dynamic Stacks:** Goroutine stacks grow and shrink dynamically as execution requires. Developers never have to guess or configure fixed stack sizes in advance; small tasks consume minimal memory, while deep recursions allocate space transparently. - **Garbage Collection as a Concurrency Enabler:** Pike highlights that safe concurrent programming is virtually impossible without garbage collection. When concurrent processes pass data structures across communication channels, tracking object ownership and deciding which thread is responsible for deallocating memory creates immense cognitive overhead and bug-prone bookkeeping. Automatic garbage collection resolves the ownership question completely. - **Co-Design of Runtime and Language:** To ensure garbage collection can run efficiently without unpredictable latency, the Go language syntax and type system were designed specifically to support accurate, low-overhead garbage collection from day one.

---

[17:07](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1027s) Syntax, Declarations, and the `echo` Program

While Go's lexical appearance resembles C, it departs from it intentionally to eliminate historical syntax traps. The language specifies that source files must be encoded in UTF-8, providing native Unicode support for string constants and identifiers. Keywords like `const` and `var` introduce declarations cleanly. In a declaration like `const N = 1024`, `N` is an untyped numeric constant rather than an explicit `int` or `float`.

```go // Variable declarations: type follows the identifier var x, y float var t0 *T = new(T)

// Short variable declaration (declare and initialize) t1:= new(T) ```

Pike acknowledges that Go’s variable syntax—putting the variable name before the type—often looks backwards to longtime C programmers:

- In C, declaring `float x, y;` works, but pointer declarations like `int* x, y;` create an asymmetric surprise where `x` is a pointer while `y` is an integer. - In Go, putting the identifier first guarantees that both `x` and `y` share the specified type unambiguously, avoiding awkward pointer-star repetition. - To reduce verbosity, Go adopts the `:=` short variable declaration operator from earlier research languages Pike worked on. A statement like `t1:= new(T)` deduces the type of `t1` directly from the right-hand initialization expression, completely replacing the redundant `var t0 *T = new(T)`. - Control structures (such as `if` and `for`) dispense with parentheses around conditional expressions to streamline parsing, while curly braces around statement blocks are made strictly mandatory.

Pike walks into a real Go program: an implementation of the Unix `echo` command. The file starts with `package main`, declaring it as a standalone executable package containing a `func main()`, which serves as the entry point (`main.main`) after package-level initialization completes and imports command-line options using the standard `flag` library.

[19:50](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1190s) The Echo Example: Flags, Initialization, and Standard Output

Rob Pike walks through the implementation of an `echo` program, explaining how command-line flag handling and global initializations function in Go. While it is not strictly identical to traditional UNIX `echo` because it includes real flag parsing rather than simple manual argument traversal, it accomplishes the same core task. The program imports two standard packages: `os`, which provides foundational operating system primitives, and `flag`, which handles command-line option processing. Pike highlights how Go manages initialization order: any global variable—such as the `nFlag` variable returned by the flag package—is guaranteed to be initialized before `main` begins executing, even when that initialization involves arbitrarily complex expressions.

Once inside the `main` function, the program invokes `flag.Parse()` to separate the flags from the remaining positional command-line arguments. It then loops over the non-flag arguments, concatenating each to an output string using the `+=` operator. Pike points out that flags returned by the `flag` package are pointers; therefore, checking whether the newline flag was passed requires dereferencing the pointer via `*nFlag`. If false, a newline character is appended before writing the final result out using a method call on `os.Stdout`. Pike emphasizes that despite the unfamiliarity of the surface syntax, the structure remains instantly readable:

> “So, although, pretty much every line in here is different from what you’ve seen before, it should be perfect and clear what’s going on.”

- Global variables are evaluated and initialized before `main()` executes, accommodating arbitrarily complex initialization logic. - Command-line flags parsed by Go's `flag` package return pointers (e.g., `*bool` for a boolean flag). - Standard output operations are invoked directly as methods on `os.Stdout`.

---

[21:01](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1261s) Untyped Constants and the iota Enumerator

Pike transitions to explaining constants in Go, emphasizing that their behavior fundamentally differs from constants in C-like languages. Go constants are conceptually typeless and sizeless; while string and boolean constants exist, numeric constants represent pure numbers without pre-allocated bit widths. Pike illustrates this by declaring a user-defined integer type, `type TZ int`, and grouping the definitions of constants like `UTC` and `EST` inside parentheses following a single `const` keyword. This syntax distributes the `const` declaration across all enclosed identifiers, avoiding repetitive declarations.

```mermaid flowchart TD accTitle: The iota Enumerator in Go accDescr: Diagram showing how iota increments at each line inside a grouped constant block.

subgraph ConstBlock["const (...)"] C0["bit0, mask0 = 1 << iota, 1 << iota - 1"] C1["bit1, mask1 = 1 << iota, 1 << iota - 1"] C2["... subsequent declarations..."] end

I0["iota = 0"] --> C0 I1["iota = 1"] --> C1 I2["iota = 2"] --> C2 ```

This grouped declaration syntax becomes essential when combined with `iota`, Go’s built-in enumerating constant. Pike explains that `iota` evaluates to the current element index within a `const` declaration block, beginning at zero and incrementing at each semicolon or newline. In an example defining bitmasks, `bit0` and `mask0` are defined as `1 << iota` and `(1 << iota) - 1`, resolving with `iota` at zero. On the subsequent line, `iota` automatically becomes one. Furthermore, Go eliminates repetitive typing by allowing subsequent lines to omit both the type and the expression: a trailing line with an identifier and a semicolon repeats the expression from the previous line while incrementing `iota`. As Pike summarizes:

> “And again, to reduce typing, if you want, you can just say, declaration, like the third line there with the semicolon and what that says is, ‘Just keep doing what I did.’”

---

[23:04](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1384s) Arbitrary Precision and Language Types: Slices, Maps, and Structs

A critical property of numeric constants in Go is their arbitrary precision. Pike displays a constant representing the natural logarithm of 2 written out to 40 or 50 decimal places—far beyond what an IEEE 754 `float64` can represent. Go accepts this literal without issue because numeric constants exist in an ideal mathematical realm. When computing `log2E` as `1.0 / Ln2`, the division is evaluated at compile time in arbitrary precision rather than floating-point space. Pike notes that this eliminates constant truncation errors, underflows, and casts:

> “And that kind of stuff is nice too, because you don’t see casts and conversions and worries about overflows and underflows.”

Pike then surveys composite and structured types in Go:

| Language Construct | Syntax Example | Key Behavioral Detail | |:--- |:--- |:--- | | **Slice** | `[]string{"Saturday", "Sunday"}` | Reference type representing an array view; used in place of raw fixed-length arrays | | **Map** | `map[string]TZ` | Built-in associative hash map mapping key types to value types | | **Function Type** | `type Op func(int, int) int` | First-class function type signature matching functions like `add(a, b int) int` | | **Struct Literal** | `&Point{3, 4}` or `RPC{a, b, op, &res}` | Composite literal specifying values for fields; prefixed with `&` to allocate a pointer |

Pike remarks that while the declaration syntax places types after identifiers—which may initially appear backwards to C programmers—the composite literals provide a straightforward, uniform syntax for initializing both values and pointers.

---

[25:09](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1509s) Package Visibility and Method Declarations

Pike addresses identifier visibility in Go, presenting the rule governing public and private access control. Rather than relying on explicit keywords like `public`, `private`, or `protected`, Go determines visibility purely through capitalization. If a top-level variable, type, constant, function, or struct field begins with an uppercase Unicode letter, it is exported and visible to external packages; if it begins with a lowercase letter, it remains package-private. Pike underlines the simplicity of this model:

> “Now, those are upper case and the reason is, in Go, the complete decision about whether an identifier is visible outside the package or not, is whether or not it starts with an uppercase Unicode letter.”

```mermaid flowchart LR accTitle: Go Method Receiver Syntax accDescr: Flowchart illustrating the receiver parameter in Go method declarations.

subgraph ReceiverDecl["Receiver Syntax"] R["func (p *Point)"] end

subgraph MethodIdent["Method Definition"] M["Scale(s float64)"] end

subgraph Body["Method Execution"] B["p.X *= s<br/>p.Y *= s"] end

ReceiverDecl --> MethodIdent --> Body ```

From visibility, Pike transitions to method declarations, noting that methods in Go are declared outside struct bodies. Because methods can be associated with types other than structs, nesting them within struct definitions would be conceptually flawed. A method is declared using the `func` keyword followed by a receiver specification enclosed in parentheses before the method name. This receiver parameter is explicit—Go provides no implicit `this` or `self` pointer.

For instance, defining `func (p *Point) Scale(s float64)` attaches `Scale` to pointers of type `Point`, with `p` serving as the explicit reference inside the body. Calling the method on an allocated point (`x:= &Point{3, 4}; x.Scale(5)`) functions exactly as object-oriented programmers expect, despite the clean decoupling of data structures from behavioral attachments.

---

[27:32](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1652s) Methods on Any Type: Custom Types and the Stringer Pattern

Pike demonstrates the true power of Go's method system: methods can be attached to *any* user-defined type, not just structs. To illustrate this, he constructs a complete program centered around `type TZ int`. The program defines constants for an `Hour` (`60 * 60` seconds) along with specific time offsets like `UTC` (0 hours) and `EST` (-5 hours), stored in a map mapping zone name strings to `TZ` values (`map[string]TZ`). Pike then attaches a `String()` method directly onto the underlying integer type: `func (tz TZ) String() string`. Notably, this receiver is passed by value (`TZ`) rather than as a pointer (`*TZ`), illustrating that value types can freely possess method sets.

Within `String()`, the code iterates over the `timeZones` map using a `for name, zone:= range timeZones` loop. This loop performs a reverse lookup, comparing each map value against `tz` and returning the matching string name if found. If no exact match exists, the function falls back to formatting the offset via `fmt.Sprintf`. Pike highlights how this integrates seamlessly with Go's standard formatting routines:

> “And notice that Println(EST) knows under the covers by mechanisms that are described at length in the website, how that the timeZone type has a string method and that’s how you should pretty print it if you get a value of that type.”

- Methods can be bound to primitive-based custom types (e.g., attaching methods directly to `type TZ int`). - Receivers can be values (`(tz TZ)`) when mutation is unnecessary, avoiding pointer overhead or dereferencing. - Go's `for... range` syntax extracts both keys and values directly from collections and maps. - `fmt.Sprintf` generates and returns a formatted `string` value directly, rather than populating an externally provided character buffer as in C. - Built-in printers like `fmt.Println` inspect values at runtime; if an argument implements a `String() string` method, it is automatically invoked to display the custom textual representation.

[30:08](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1808s) Methods on General Types and the Philosophy of Separation

Rob Pike wraps up an earlier example demonstrating how methods can be attached to user-defined types, noting that while the demonstration is fairly contrived, it highlights a crucial architectural decision in Go. In Smalltalk, having methods on general types has long been recognized as standard and convenient, but Go takes this concept in a fresh direction. Pike points out: “By separating them methods away from structural classes, we actually introduce some new ideas that you can play with and that’s pretty fun.” Rather than binding methods tightly into an inheritance hierarchy or class definition, Go treats methods as independent operations attached to types, setting the stage for a much more flexible type system.

[30:29](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1829s) Interfaces and Implicit Satisfaction

Moving to what he calls arguably the most novel feature of the language, Pike introduces Go interfaces. An interface in Go serves as a formalization of a set of methods. To demonstrate, he defines a `Magnitude` interface requiring a single method, `Abs() float64`. Previously, a 2D `*Point` type had an `Abs` method defined on it; a variable `m` of type `Magnitude` can immediately take an existing `*Point` value `x` because `*Point` implements the required method. Pike momentarily catches himself while reviewing the slide code—laughing, “No, I got it right. Sorry, sorry, I confused myself”—before showing how `m.Abs()` computes the vector length. He then defines a 3D coordinate type `Point3` with its own `Abs` method and assigns it to `m`, invoking `m.Abs()` again. Finally, he defines a `Polar` struct with fields `R` and `Theta` (highlighting that the capital `T` in `Theta` makes it public). Because `Polar` does not need to be a pointer, a plain value `p` of type `Polar` is assigned to `m`, returning its radius.

Crucially, none of these concrete types explicitly declare that they implement `Magnitude`. There are no `implements` declarations anywhere in the source code; the relationship is satisfied entirely implicitly by implementing the required method signature. Pike explains that the interface variable allows a developer to express: “I can work with anything that implements these methods and anything at all will do.”

```mermaid flowchart LR accTitle: Implicit Interface Satisfaction in Go accDescr: Three distinct concrete types satisfy the Magnitude interface implicitly by providing an Abs method returning a float.

subgraph Interface["Interface"] M["Magnitude<br/>Abs() float64"] end

subgraph Implementations["Concrete Types"] P1["*Point<br/>Abs() float64"] P2["Point3<br/>Abs() float64"] P3["Polar<br/>Abs() float64"] end

P1 -.->|"implicitly satisfies"| M P2 -.->|"implicitly satisfies"| M P3 -.->|"implicitly satisfies"| M ```

- Concrete types need zero metadata or forward declarations binding them to an interface. - Interface satisfaction is determined purely at compile time by the method set of the type. - Both pointer receivers (like `*Point`) and direct value receivers (like `Polar`) can satisfy interfaces based on how their methods are defined.

[33:14](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1994s) The Power of Small Interfaces: `io.Writer` and Composability

Pike explores the broader design implications of Go's interface model: because methods and interfaces share a many-to-many relationship, any type can implement multiple interfaces, allowing programmers to define remarkably small, powerful abstractions. The canonical demonstration lives in the `io` package with `io.Writer`. A writer is simply any type implementing a standard `Write` method that takes a byte slice and returns the number of bytes written along with an error. Because a slice already contains its own length, the method does not even need a separate byte count parameter.

This tiny definition unlocks massive composability throughout the standard library. Pike highlights `fmt.Fprintf`, emphasizing that unlike its C counterpart, it does not expect a concrete file pointer: “It takes an io.Writer as its first argument.” Because of that single interface, `fmt.Fprintf` can write transparently to standard output, a network socket, an in-memory buffer, a pipe, or a cryptographic pipeline. Furthermore, Go has no dedicated "buffered I/O" subsystem; it simply provides buffering wrappers. Calling `bufio.NewWriter(os.Stdout)` wraps standard output in a buffered writer, which itself implements `io.Writer`. A program can pass that buffered instance straight back to `fmt.Fprintf` and flush it when complete, demonstrating how cleanly decoupled abstractions stack together without rigid class hierarchies.

[36:15](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2175s) Concurrency with CSP, Channels, and Goroutines

Pike transitions to Go's concurrency primitives, noting that while interfaces are novel, communication channels are grounded in Tony Hoare's Communicating Sequential Processes (CSP). However, Go departs from Hoare's original 1978 paper by treating channels as first-class values that can be stored, passed around, and sent over other channels. Channels act like typed pipes: declaring a channel of type `string` allows goroutines to send and receive strings synchronously. The left-pointing arrow operator (`<-`) serves as both an infix send operation (`c <- "hello"`) and a prefix receive operation (`greeting:= <-c`).

To run computations concurrently, Go introduces the `go` keyword. Rather than blocking execution waiting on a long-running calculation, a program can create a channel, wrap the calculation in a function that transmits its result across that channel, and launch it concurrently with `go wrapper(17, c)`. The main thread continues running other tasks unimpeded, eventually executing a receive (`<-c`) only when it is ready to consume the completed result. Because channels are synchronized by default, communication and coordination happen seamlessly without explicit locks or condition variables.

[38:36](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2316s) Multiplexing Servers and Channels of Channels

To illustrate the expressive power of first-class channels, Pike presents the architecture of a multiplexed server where channels themselves are transmitted inside request payloads. A `Request` struct packages two integer arguments, an operator, and a dedicated `replyc` channel of type `chan int`. A worker function, `run`, computes the binary operation on the arguments and sends the resulting value directly back over `req.replyc`.

The server loop itself listens on a incoming request channel, pulling requests in an infinite loop and launching each handler concurrently via `go run(op, req)`. Pike remarks: “The server is never blocked. It’s always ready to receive another call no matter how much is going on.” While acknowledging that production code must account for rate limiting and backpressure, he points out how minimal the core mechanics are. To expose this cleanly to clients, an exported constructor function, `StartServer`, allocates the request channel, launches the server goroutine in the background, and returns the channel handle directly to the caller.

[40:27](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2427s) Client-Server Pattern and Teardown with Select

Rob Pike illustrates how to consume an asynchronous service on the client side. By calling `startServer`, the client receives a channel to talk to, establishing the core paradigm for building services in Go. Because Go supports full closures, functions can be declared inline as expressions. In Pike's client example, a server routine is launched that performs addition, returning a channel stored in `server`. The client creates requests containing distinct response channels, fires them off without blocking, and retrieves the results. Because execution is concurrent, the client does not need to care about the order of delivery; it can send request one first and request two second, and even if request two takes a long time, the program simply waits on its channel before retrieving the result for request one.

```mermaid flowchart LR accTitle: Client-Server Channel Communication accDescr: The client launches a server via startServer, receives a request channel, sends requests with dedicated reply channels, and can terminate the server with a quit channel.

subgraph ClientSide["Client Routine"] C["Client"] end

subgraph ServerSide["Server Goroutine"] S["Select Loop"] end

C -->|"Send Request (op, a, b, replyChan)"| S S -->|"Send Result"| C C -->|"Send Quit Signal (bool)"| S ```

Pike then addresses two unresolved issues from the server implementation: rate limiting and shutting the server down cleanly. To solve the termination problem, Go provides the `select` control structure. “Select lets you choose, let's you to actually wait for any one of them to be become ready and once it does become ready, let that single thing communicate.” Functioning like a switch statement for communication, `select` allows the server loop to multiplex between incoming requests and a dedicated `quit` channel. When a shutdown is requested, the client sends a boolean value across `quit`, causing that branch of the `select` to fire and the server function to return, gracefully terminating the goroutine.

- Go closures allow passing inline function expressions directly into server launchers. - Channels embedded inside request structs allow servers to reply to clients independently. - The `select` statement coordinates communication by unblocking whenever any one of its channel operations becomes ready. - Sending any boolean value across a `quit` channel triggers immediate return and goroutine exit.

---

[42:54](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2574s) Language Capabilities and the 100,000 Goroutine Daisy Chain

Acknowledging the rapid pace of the presentation, Pike catalogs several major features of Go that he lacks time to demonstrate in depth. These include package construction, initialization routines capable of standing up RPC servers with minimal boilerplate, full reflection across functions, channels, and maps, optional dynamic typing, type embedding (which acts as a simpler, more general alternative to inheritance), range-based iterators, and integrated testing tools. Returning to the design rationale of the language, Pike emphasizes that modern servers suffer under the traditional thread-per-request model because it is resource-intensive and error-prone. Go was designed to make spinning up tens or hundreds of thousands of concurrent units of execution natural and cheap.

```mermaid flowchart LR accTitle: Goroutine Daisy Chain Communication accDescr: A value enters the right end of a chain of 100,000 goroutines, each adding one, and exits the left end.

In(["Input Value: 0"]) --> G100k["Goroutine #100,000 (adds 1)"] G100k --> Dots["..."] Dots --> G1["Goroutine #1 (adds 1)"] G1 --> Out(["Output Value: 100,000"]) ```

To demonstrate this capability, Pike shows a program that stitches together 100,000 goroutines like "pearls on a string." Each goroutine runs a helper function `f` that reads an integer from its right neighbor's channel, increments it by one, and sends it to the left. Once the chain is wired together, a single zero is dropped into the rightmost channel; it ripples through all 100,000 stages, incrementing at every hop, and prints out exactly 100,000 on the left. Pike runs the program live on a lightweight MacBook Air, completing the entire cycle of creation, communication, and teardown in approximately 1.5 seconds.

- Go supports structural embedding instead of class-based type inheritance. - The standard runtime multiplexes goroutines over operating system threads so blocking I/O is handled automatically. - “That's a hundred thousand threads of control--I won’t say threads because that’s not how it is done—-but just incase you’re wondering, it takes about 1.5 seconds to manage a hundred thousand goroutines.” - The daisy-chain benchmark performs genuine end-to-end allocation, message passing, and cleanup rather than immediate termination.

---

[46:56](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2816s) Compiler Toolchains, Runtime Architecture, and Garbage Collection

Pike transitions to the current status of the Go project, highlighting two distinct, complete implementations. Ken Thompson authored a toolchain written in C from scratch, loosely based on Plan 9 compiler conventions, which includes the `6g` compiler for 64-bit x86, `8g` for 32-bit x86, and `5g` for ARM. While Ken's compiler emits code that does not directly link with GCC objects, it compiles at blinding speed and already supports C foreign function calls, with SWIG support underway for C++. Concurrently, Ian Lance Taylor created `gccgo`, a C++ front end for the GCC backend. While `gccgo` compiles four to five times slower than Ken's toolchain, it produces high-quality GCC optimizations, and both compilers are complemented by an ARM port by Kai Backman that is nearing completion.

| Implementation | Author | Implementation Language | Backend / Platform | Performance Characteristics | |:--- |:--- |:--- |:--- |:--- | | **Ken's Compiler Suite (`6g`, `8g`, `5g`)** | Ken Thompson | C (for bootstrapping) | Custom Plan 9-style backend; x86 (32/64-bit) & ARM | Extremely rapid compilation; generated code ~20% slower than C. | | **`gccgo`** | Ian Lance Taylor | C++ | GCC backend; x86 (32/64-bit) | Slower compilation (4–5x slower than `6g`); generates optimized code ~10% slower than C. |

The underlying Go runtime manages dynamic memory allocation, channels, slices, maps, reflection, and segmented stacks. Segmented stacks keep goroutine memory footprints minimal at creation while permitting them to grow on demand. In terms of raw execution speed, tight loops in Go typically run within 10% to 20% of optimized C code. Regarding memory reclamation, `6g` currently employs a straightforward mark-and-sweep collector, but the team is actively targeting low-latency concurrent garbage collection. “We believe, with multi-core machines, you can actually do concurrent garbage collection with essentially zero latency and very little cost and overhead.” To achieve this, the team is looking toward advanced garbage collection technologies, such as research originating from IBM, to avoid classic GC latency traps on multicore hardware.

- Ken's suite employs historical Plan 9 architecture numbering: `6g` (AMD64 / 64-bit x86), `8g` (386 / 32-bit x86), and `5g` (ARM). - Kai Backman's ARM port had achieved 97% test suite compliance by late October 2009 in preparation for the open-source release. - Execution speed for statically compiled Go code sits comfortably at 1.1x to 1.2x of C execution time. - Segmented stacks allow `6g` to keep initial per-goroutine overhead tiny while scaling stack size dynamically.

[50:57](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3057s) Runtime, Garbage Collection, and Standard Libraries

Addressing the ongoing runtime work, Rob Pike explains that the team is tackling the latency and pause problems that garbage collectors traditionally suffer from. Having implemented collectors in other languages before, the team is confident that with modern multi-core machines, the problem can be genuinely solved. While Gccgo currently lacks a garbage collector, work is underway on a unified runtime so that both the Ken Thompson compiler toolchain and Gccgo will share a true garbage collector. Pike notes that although an enormous amount of work remains, the standard library already offers a rich foundation. This includes OS and I/O primitives, a simple math package, strings, comprehensive Unicode support, and a functional regular expression engine.

The standard library also features runtime reflection, command-line flag parsing, logging, full hashing and cryptography suites, a robust testing tool with supporting libraries, and standard networking implementations, including a native RPC package. Highlighting user-facing web utilities, Pike credits Andy Chu for the design of the data-driven template library used for HTML generation. As Pike puts it: “It’s part of the general run-time so both compilers will have real garbage collectors inside of them.”

- **Core packages:** OS, I/O, math, strings, Unicode, and regular expressions. - **System utilities:** Runtime reflection, flag parsing, logging, hashes, and cryptography. - **Networking and tools:** Native RPC, standard socket networking, unit testing tooling, and Andy Chu’s data-driven template package.

---

[52:18](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3138s) Tooling: Godoc, Gofmt, and Debugging

Pike highlights two interconnected programs that share a significant amount of code: `godoc` and `gofmt`. Functioning similarly to Javadoc, `godoc` serves documentation on demand to show what packages and functions do. In fact, the official website (`golang.org`) is served entirely by `godoc`, providing the landing page, language specifications, design editorials, FAQs, auto-generated package documentation, and browsable source code. When viewing source files through this pipeline, the code is formatted using `gofmt`. By acting as a strict pretty-printer across the entire repository, `gofmt` eliminates stylistic bikeshedding entirely. Rather than arguing over style guidelines in text documents, developers must accept what the tool outputs. Robert Griesemer receives the credit for making the tool so effective.

```mermaid flowchart TD accTitle: Godoc and Gofmt Documentation Pipeline accDescr: Diagram illustrating how source files and repository code pass through Gofmt and Godoc to serve package documentation. subgraph Repo["Repository & Formatting"] A["Go Source Code"] --> B["gofmt Engine"] B --> C["Canonical Formatted Source"] end subgraph Server["Godoc Server (golang.org)"] C --> D["Package Parser & Extractor"] D --> E["Interactive HTML Documentation"] D --> F["Source Code Browser"] end ```

On the debugging front, Pike acknowledges that tooling is still catching up. A dedicated debugger was being developed by summer intern Austin Clements, primarily targeting the `6g` compiler, with hopes of delivering a functional release by year's end. While developers compiling with Gccgo can use standard GDB, GDB treats Go programs as ordinary C binaries, has trouble parsing the symbol tables generated by Ken Thompson’s compiler suite, and remains completely unaware of Go’s concurrent runtime semantics. Resolving these issues remains an active priority.

> “So, rather than set a bunch of style rules, we have a program that says this is what a code looks like, and you just run through Gofmt when you check it in and that’s, just settles all those arguments.” — Rob Pike

---

[54:15](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3255s) Generics and Missing Language Features

Pike confronts the ubiquitous question regarding generics, admitting bluntly that Go does not have them yet because the design team does not fully understand how they should work within Go’s type architecture. While trivial examples are simple to sketch out, establishing a sound, complete specification is exceedingly difficult. Although the absence of type inheritance simplifies the problem, Go’s ability to attach methods to values of arbitrary sizes introduces subtle implementation complexities. Pike emphasizes that built-in slices, associative maps, and interfaces already cover the vast majority of day-to-day use cases where developers in other languages reach for generics. Programmers can construct generic collections today using the empty interface (`interface{}`), though Pike acknowledges this is imperfect because it requires manual type assertions and sacrifices compile-time type safety. He references Josh Bloch’s writings on Java generics to highlight how difficult the feature is to get right.

Turning to other missing capabilities, Pike explains why certain familiar features are deliberately omitted. Go includes `iota` for enumerations, but it deliberately lacks traditional enums. More importantly, pointer arithmetic was excluded entirely to maintain absolute memory safety; while compiler optimizations must work harder to eliminate bounds-checking overhead on slice indexes, safety remains paramount. Pike reveals that features like union types are being considered, and he encourages developers to consult the language design FAQ to understand the reasoning behind what was excluded:

> “And we just don’t think we understand it well enough to do them yet.” — Rob Pike

> “A really good example is inum but there is no iota. And iota's an interesting thing, but it's not an inum.” — Rob Pike

- **Generics design:** The team wants to avoid hasty implementations; built-in slices and maps mitigate the immediate need. - **Empty interface (`interface{}`):** Operates somewhat like Java's `Object` because all types satisfy it, but requires manual unboxing. - **Pointer arithmetic:** Excluded by design to ensure memory safety and runtime integrity. - **Future considerations:** Union types have been discussed with promising design proposals.

---

[57:53](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3473s) Conclusion and Call for Open-Source Collaboration

Concluding the presentation, Pike expresses his personal enthusiasm for the language, remarking that he is having more fun programming than he has had in years because the language allows him to be genuinely productive. Even though Go is in its early stages and will continue to evolve, the core language fundamentals are locked down, secure, and fast to develop in. The project is launching as an open-source release backed by extensive documentation: a formal language specification, beginner tutorials, an evolving guide called *Effective Go* to explain idioms, and a comprehensive FAQ.

Pike invites the community to download the toolchains—both the `6g`/`8g`/`5g` compilers and Gccgo—and contribute to the ecosystem. There is substantial work needed to flesh out libraries, implement better developer tools, and refine the environment. Inviting outside engineers to participate directly in Go’s growth, Pike closes the talk by opening the doors to community collaboration.

> “I’m having more fun programming than I’ve had in a long time because I feel like I’m getting work done, because I’m getting work done.” — Rob Pike

> “We really are welcoming people to come in and talk about stuff, especially if they want to help us do some interesting library work or build new tools, there’s plenty of room for things to happen.” — Rob Pike

## Notable quotes > [10:17](https://www.youtube.com/watch?v=rKnDgT73v8s&t=617s) We just think that programming by constructing type hierarchies is not the way to go. — Rob Pike

> [46:56](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2816s) We believe, with multi-core machines, you can actually do concurrent garbage collection with essentially zero latency and very little cost and overhead. — Rob Pike

## Mentioned Andy Chu, Austin Clements, C, C++, Effective Go, GCC, GDB, Gccgo, Godoc, Gofmt, Google, IBM, Ian Lance Taylor, Java, JavaScript, Josh Bloch, Kai Backman, Ken Thompson, MacBook Air, Plan 9, Python, Rob Pike, Robert Griesemer, Ruby, Russ Cox, SWIG, Smalltalk, The Go Programming Language, Tony Hoare, YouTube

## Video description Google Tech Talks October 30, 2009

ABSTRACT

Presented by Rob Pike

What is Go?

Go is a new experimental systems programming language intended to make software development fast. Our goal is that a major Google binary should be buildable in a few seconds on a single machine. The language is concurrent, garbage-collected, and requires explicit declaration of dependencies. Simple syntax and a clean type system support a number of programming styles.

For more on Go including FAQs, source code, libraries, and tutorials, please see: http://golang.org

[Source](https://www.youtube.com/watch?v=rKnDgT73v8s)

中文简报

谷歌工程师发布Go语言以加速软件构建 通过将接口与类型实现解耦并强制执行严格的依赖项跟踪,Go消除了编译器拖累,在提供近乎即时的构建时间的同时,实现了安全、轻量级的并发。 Rob Pike详细阐述了他与Ken Thompson和Robert Griesemer构想Go语言的初衷,旨在解决现代C++和Java环境中令人困扰的编译缓慢、依赖膨胀以及多核协作摩擦等问题。他展示了Go的基础设计选择,包括实现瞬间编译的严格依赖树、无类型任意精度常量,以及摒弃僵化继承层次结构的隐式接口满足机制。演讲重点介绍了Go的CSP风格并发模型,不仅展示了客户端-服务器请求多路复用,还现场演示了在笔记本电脑上仅用1.5秒执行包含10万个goroutine的菊花链。Pike最后概述了双编译器实现(Ken Thompson套件和基于GCC的`gccgo`)、`gofmt`和`godoc`等开发者工具,并解释了为何泛型等特性因有待进一步设计而推迟引入。 Go由谷歌的Robert Griesemer、Ken Thompson、Rob Pike、Ian Lance Taylor和Russ Cox共同开发,旨在解决现代系统编程中的认知开销、庞大依赖和缓慢构建问题。 (0:23) 该语言通过严格的依赖管理实现近乎即时的构建,在单个笔记本电脑核心上仅需约8秒即可编译包含13万行代码的标准库和运行时代码库。 (6:54) Go通过多路复用在操作系统线程之上的轻量级、动态栈goroutine引入了CSP风格并发,并通过在约1.5秒内管理10万个相互通信的goroutine进行了演示。 (42:54) 接口的满足完全是隐式的,无需显式的“implements”声明,从而将行为契约与结构化类型定义分离开来。 (30:29) 公开发布时推出了两个截然不同的编译器工具链:Ken Thompson基于C的快速套件(6g/8g/5g)和Ian Lance Taylor基于GCC的优化前端(gccgo)。 (46:56)

Reading summary

Rob Pike introduces Go as an experimental systems programming language designed to combine the execution speed and safety of compiled languages with the development velocity of dynamic scripting. Developed at Google by Robert Griesemer, Ken Thompson, Ian Lance Taylor, Russ Cox, and Pike, Go aims to solve modern development bottlenecks caused by slow builds, tangled dependencies, and multicore concurrency challenges. Resolving Build Drag and Dependency Overhead (3:08) Pike argues that modern systems programming stagnated over a decade while codebases ballooned and hardware clock speed gains flattened. Developers increasingly turned to dynamic languages to regain productivity, trading away compile-time safety. Go addresses this trade-off by streamlining compilation to eliminate developer downtime. In a live demonstration on a single laptop processor core, a 1,300-line library compiled in roughly 200 milliseconds, and the entire 120,000-to-130,000-line Go repository built in approximately 8 seconds. This compilation speed is achieved through strict dependency management and orthogonal language syntax. Unused imports trigger compiler errors, keeping build graphs minimal. Furthermore, Go passes transitive dependency metadata up the compilation chain: when compiling package A that imports B, which imports C, the compiler reads only B's compiled object file and completely ignores C's source and object files. The grammar is conflict-free and parsable without a symbol table, simplifying developer tooling while reducing redundant declarations through type inference. Decoupled Types and Implicit Interfaces (30:29) Go rejects deep object-oriented inheritance hierarchies in favor of orthogonal type concepts. Instead of defining classes, developers can attach methods to any user-defined type—including primitive aliases like integer-based time offsets—using explicit receiver parameters rather than an implicit `this` pointer. Type visibility is governed entirely by capitalization: uppercase identifiers are public to other packages, while lowercase identifiers remain private. Interfaces are satisfied implicitly without explicit declarations like `implements`. Any type that defines the required method signatures automatically satisfies the interface at compile time. This allows for small, composable abstractions like `io.Writer`, whose single `Write` method allows functions like `fmt.Fprintf` to write interchangeably to standard output, sockets, memory buffers, or encryption pipelines without rigid class hierarchies. First-Class Concurrency and Dynamic Runtime (36:15) To exploit multicore hardware without the cognitive overhead of thread management and locks, Go implements concurrency based on Communicating Sequential Processes (CSP). Concurrency is built on goroutines—lightweight execution contexts multiplexed onto OS threads by the Go runtime—and typed channels. Goroutines utilize segmented, dynamic stacks that start small and grow on demand, making them lightweight enough for a live daisy-chain benchmark to create, pass an incremented integer through, and tear down 100,000 goroutines in approximately 1.5 seconds on a MacBook Air. Channels act as first-class typed communication pipes that can be stored, passed across other channels, and coordinated using the `select` statement for non-blocking multiplexing and shutdown signaling. Pike stresses that safe concurrent communication relies on automated garbage collection, which resolves the complex object ownership tracking that traditionally plagues multi-threaded programming. Toolchains, Ecosystem, and Deliberate Omissions (46:56) At its release preview, Go featured two distinct compilers: Ken Thompson's custom C-bootstrapped toolchain (`6g` for 64-bit x86, `8g` for 32-bit x86, and `5g` for ARM), which emphasizes rapid compilation and yields code roughly 20% slower than C; and Ian Lance Taylor's `gccgo`, which compiles four to five times slower but emits optimized code within 10% of C. The language ships with standardized developer tooling, including `gofmt` to strictly format code and eliminate style debates, and `godoc` to serve package documentation and powers the project's website. Go deliberately omits features to preserve simplicity and runtime integrity. Pointer arithmetic is excluded to guarantee memory safety, and traditional enums are replaced by `iota` block enumerations. Pike acknowledges that generics were left out because the design team did not yet fully understand how to specify them correctly within Go's type architecture without introducing excess complexity, noting that built-in slices, maps, and the empty interface (`interface{}`) handle most common generic requirements. Go was prepared for its November 10, 2009 open-source release as a stable core language, supported by documentation such as Effective Go and standard libraries for networking, encryption, and templates. Pike noted that ongoing work remained to unify the runtime, develop concurrent low-latency garbage collection to eliminate stop-the-world pauses, and deliver mature debugging tools aware of Go's concurrency model.

精炼阅读摘要

Rob Pike在Google TechTalks演讲中指出,现代系统级软件开发长期受到工具链编译缓慢、依赖关系失控以及对多核并发环境支持不足的困扰;Go语言通过创新的依赖模型、轻量级类型系统与基于CSP的并发原语,力求在保留静态编译高效与安全的同时,重获动态语言的敏捷开发体验。 现代系统编程困境与极速构建设计 (3:08) Pike回顾指出,在Go诞生前的十余年间未曾出现主流的新系统级编程语言,而计算环境已彻底转向分布式集群、多核处理器与网络化架构。传统单线程CPU主频提升因摩尔定律放缓而停滞,开发者在C++或Java中不得不面对深层类继承体系的认知开销,或是转向Python等动态语言以换取开发效率,从而牺牲了编译期类型安全。为解决构建缓慢这一核心痛点,Robert Griesemer、Ken Thompson与Rob Pike于2007年底启动Go项目,随后Ian Lance Taylor与Russ Cox加入核心团队,将开发效率与正交简洁确立为核心基准。 在构建性能方面,Go消除了传统语言中头文件重复解析的缺陷。Go编译器采用自底向上的传递性依赖模型:当包A引用包B、包B引用包C时,B在编译时会将C的导出元数据直接内嵌到自身的编译产物中;编译A时只需读取B的目标文件,无需解构或打开C文件。Pike在现场演示中展示,一个1,300行代码的库仅需约200毫秒即可完成编译;而包含运行时、垃圾回收器、正则表达式库及配套工具链的12万至13万行标准代码库,在单核笔记本上完成全量构建仅耗时约8秒。这种确定性的无冲突正交语法与依赖剪枝,彻底消除了大规模工程中的编译等待开销。 极简类型系统与隐式接口抽象 (30:29) Go语言在语法上移除了隐式类型转换,但通过无类型常量机制避免了繁琐的手动类型强制转换。数值常量在编译期拥有任意精度,诸如自然对数常数的除法运算均在数学理想值下求值,避免了浮点溢出与截断错误。针对常量枚举,Go引入了按行自增的`iota`枚举器。在标识符可见性上,Go摒弃了`public`和`private`等显式关键字,统一以首字母是否大写作为包外导出的唯一判定规则。此外,方法可以自由绑定至包括基础类型在内的任何自定义类型(如为`type TZ int`定义`String()`方法),且方法的接收者必须显式声明,不存在隐式的`this`或`self`指针。 Pike将接口设计视为Go最独特的创新。Go接口是一组方法签名的集合,类型与接口之间无需编写任何`implements`声明,只要某个类型的公开方法集完全覆盖了接口要求,即在编译期自动满足该接口。例如二维点、三维点与极坐标结构体只需各自实现`Abs() float64`方法,即可直接赋值给`Magnitude`接口变量。小颗粒度接口(如`io.Writer`仅包含一个`Write`方法)在标准库中被广泛组合,使`fmt.Fprintf`能够无缝写入标准输出、网络连接、内存缓冲或加密流,彻底摆脱了传统面向对象深层继承树带来的僵化分层。 CSP并发机制与运行时内存管理 (36:15) Go的并发模型根植于Tony Hoare的通信顺序进程(CSP)理论,将通道(Channel)设计为一等公民,支持作为参数传递或嵌套于其他通道中。通过`go`关键字,程序可以并发启动由Go运行时统一调度的轻量级执行单元——goroutine。goroutine并不等同于操作系统线程或协程,其初始栈极小并采用分段动态扩展机制,由运行时多路复用到底层线程上,从而大幅削减了高并发下的内存与切换开销。Pike特别强调,安全的并发编程必须依赖自动垃圾回收机制,否则在跨通信通道传递复杂数据结构时,多线程间追踪对象所有权与内存释放职责将引发极高的复杂度和内存缺陷。 在通信与协调模式上,Go提供了类似通信多路复用开关的`select`语句,使并发进程能够非阻塞地监听多个通道的就绪状态,并通过接收专用信号(如退出布尔值)实现服务的优雅停机。在服务端架构中,请求结构体内嵌专属回复通道(`replyc`),使并发工作协程能够直接向客户端异步回传结果。为了直观展示其并发性能,Pike在MacBook Air笔记本上现场演示了一个串联100,000个goroutine的“菊花链”程序:数值从链条末端输入并在每一级递增,整套系统的创建、十万次管道消息中继与资源回收在约1.5秒内完整执行完毕。 双编译器体系、开发工具与特性权衡 (46:56) 在实现生态方面,Go同时推进了两套编译器套件:Ken Thompson编写的C语言实现版本(包括用于AMD64的`6g`、x86的`8g`及ARM的`5g`),具备极高的编译速度,生成代码性能约为C语言的80%至90%;Ian Lance Taylor开发的`gccgo`则依托GCC成熟的优化后端,编译速度较`6g`慢4至5倍,但生成代码性能已达到C语言的90%。为保证生态一致性,Go推出文档引擎`godoc`直接支撑官方站点及离线文档查询,并由Robert Griesemer主导开发了源码格式化工具`gofmt`,通过机器自动化强制执行标准代码排版,终结了开发团队内部关于编码风格的争议。 针对语言功能的取舍,Pike坦承Go在发布初期未包含泛型支持,原因在于设计团队尚未在Go的类型架构下找到完全清晰、完备且不破坏语言简洁性的泛型方案;但内置的切片、关联映射以及空接口(`interface{}`)已能覆盖绝大多数日常通用需求。此外,为确保彻底的内存安全与指针有效性,Go严格禁止了指针算术。尽管专用调试工具(当时由实习生Austin Clements主导)和低延迟并发垃圾回收器仍在持续迭代中,但语言的基础语法和系统底座已全面确立并向全球开源社区开放协作。 Rob Pike总结强调,尽管Go在初期仍有类库扩充、低延迟垃圾回收完善以及泛型机制探索等未决问题,但其紧凑的正交语法、极速的构建反馈与原生并发支持,成功在严谨的系统控制与流畅的开发体验之间建立了平衡,旨在为多核与网络化时代的系统编程提供一条切实可行的新路径。

Detailed notes — zh

## Background 该视频由 Google TechTalks 频道于 2009 年 11 月 10 日发布在 YouTube 平台上。视频记录了 Rob Pike 于 2009 年 10 月 30 日在 Google 内部的技术演讲,内容聚焦于当时全新推出的实验性系统级编程语言 Go,旨在通过简洁语法、正交类型系统与原生并发支持,实现数秒内构建大型软件的高效开发体验。 ## TL;DR Rob Pike 深入剖析了传统系统级语言在多核架构与大规模网络服务下面临的构建缓慢、依赖繁冗及类型系统笨重等痛点,并阐明了 Go 语言从零设计的初心。演讲详细介绍了 Go 的核心特性,包括极速编译机制、无类型常量系统、基于隐式接口的解耦多态,以及由 Goroutine 与 Channel 支撑的 CSP 并发模型。此外,Pike 还展示了单机 1.5 秒跑通 10 万 Goroutine 的吞吐测试,客观解释了首发版本暂缓引入泛型与摒弃指针算术的哲学权衡,并宣布全线开源编译器工具链与标准库。 ## Atmosphere & style 整场演讲洋溢着资深系统黑客重拾编程乐趣的真挚热情与从容自信,语气平实严谨且不乏幽默风趣。现场敲击终端展示毫秒级构建与 10 万并发管道吞吐时,呈现出极具震撼力的技术底气;而在剖析语言特性取舍与设计留白时,Rob Pike 展现了兼具克制与远见的工程哲学,让听众深刻感受到一门划时代现代化语言初次面世时的沉稳与惊艳。 ## Bulletin - [00:23](https://www.youtube.com/watch?v=rKnDgT73v8s&t=23s) Google 正式公开系统级编程语言 Go,由 Robert Griesemer、Ken Thompson 和 Rob Pike 于 2007 年末联合发起,旨在解决庞大依赖构建极慢、多核网络并发复杂以及静态类型系统冗长笨拙等工业级软件开发困境。 - [06:54](https://www.youtube.com/watch?v=rKnDgT73v8s&t=414s) Go 凭借清晰的显式包依赖以及传递依赖元数据“上卷”(pull-up)机制,实现了数量级的编译提速,在普通笔记本单核环境下,全量重新编译包含标准库与运行时的约 13 万行完整源码树仅需约 8 秒。 - [43:44](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2624s) 语言采用以 Goroutine 与 Channel 为核心的 CSP 通信并发模型,结合轻量级分段栈与自动化垃圾回收,现场演示仅用 1.5 秒即完成 10 万个串联 Goroutine 的链式通信与有序销毁。 - [30:08](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1808s) Go 摒弃了传统的类继承体系与隐式类型转换,引入非侵入式的“隐式接口”机制与无类型数值常量系统,并配套官方格式化工具 Gofmt 彻底终结代码排版争议。 ## Key takeaways - [00:23](https://www.youtube.com/watch?v=rKnDgT73v8s&t=23s) Go 语言定位于兼顾静态编译机器性能与动态脚本开发敏捷性的实验性系统语言,专为多核多芯片架构、网络化服务与大规模代码构建而设计。 - [05:21](https://www.youtube.com/watch?v=rKnDgT73v8s&t=321s) 传统面向对象语言繁复的类型继承层级与笨拙的类型系统迫使开发者转向动态语言,Go 旨在重新兼顾高开发生产力与绝对的类型安全、内存安全。 - [13:05](https://www.youtube.com/watch?v=rKnDgT73v8s&t=785s) 通过传递依赖上卷机制,上层包在编译时无需重新分析底层间接依赖,使依赖构建在大规模代码库中展现出指数级的构建提速。 - [21:01](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1261s) 彻底废除隐式类型转换,数值常量在未赋值给确定类型前均作为任意精度的无类型抽象数学量存在,消除了复杂的位宽后缀与溢出截断顾虑。 - [25:09](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1509s) 严格基于首字母大小写判定符号可见性(大写导出、小写包内私有),消除了 public 与 private 等样板关键字,简化语法解析。 - [27:32](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1652s) 方法与结构体概念彻底解耦,开发者可以为包括基本类型在内的任意命名类型绑定显式接收者方法,且支持按值传递或指针传递。 - [30:08](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1808s) 接口作为方法集合采用“隐式实现”,类型无需声明 `implements`,只要具备对应方法即自动满足接口,实现了高度正交的多态与代码解耦。 - [33:40](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2020s) 标准库 `io.Writer` 极简抽象使得输出流能够正交适配文件、网络与内存缓冲,缓冲 I/O 仅作为装饰器包装而无需专用类继承树。 - [38:36](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2316s) Channel 作为头等公民值,支持作为参数甚至在 Channel 自身中传递,结合 `go` 关键字可在十几行代码内搭建永不阻塞的异步多路复用服务。 - [41:42](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2502s) `select` 语句作为面向通信的分支控制器,支持多路监听 Channel 读写就绪事件,并提供了优雅的服务退出与控制机制。 - [46:56](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2816s) 工程实现分为两条路线:追求极速编译的 Ken Thompson 编译器系列(6g/8g/5g)与注重优化深度的 GCC 前端(Gccgo),生成代码性能已达到原生 C 的 1.1 至 1.2 倍。 - [52:18](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3138s) 官方提供 Gofmt 自动化格式工具强制规范源码风格,直接从工具链层面杜绝团队内部在代码排版层面的主观争论。 - [54:15](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3255s) Go 暂缓引入泛型并非疏忽,而是因其语义极其微妙;目前优先通过内置的 map、slice 和空接口 `interface{}` 满足通用容器需求。 - [56:11](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3371s) 坚决不妥协支持指针算术,通过编译器优化边界检查来换取严格的内存安全与稳健的并发运行时支持。 ## Detailed walkthrough ### [00:23](https://www.youtube.com/watch?v=rKnDgT73v8s&t=23s) 什么是 Go 语言与其应用定位 Rob Pike 开门见山地宣布了 Go 语言项目,并透露团队希望在短短几周后的 2009 年 11 月 10 日将其正式开源。由于演讲录制时间略早于开源发布日,幻灯片中的部分链接与表述还带着“未来时态”,但他保证等到公开上线时一切都会就绪,并且官方网站域名也已搭建完毕。关于 Go 到底是什么,Pike 将其定义为一门全新的、实验性的、并发的、带垃圾回收的系统级编程语言。所谓“实验性”,是因为团队并不狂妄地认为它能解决世界上所有的问题,但经过一段时间的探索,他们觉得“这门语言已经到了应该向全世界公布的阶段”。 谈及特性,并发在现代计算环境中至关重要;垃圾回收则是团队深思熟虑后的明确立场;而将其定位于“系统级语言”,是因为团队最初的设想就是用它来编写 Web 服务器以及底层的系统组件。与 Java 或 Python 等高级语言相比,Go 赋予程序员更多的底层控制权,在实际使用时具备系统编程语言的质感。不过,团队在实践中惊喜地发现,Go 在编写前端服务或通用程序时同样表现卓越,其实际适用范围远远超出了最初设定的系统级领域。随后,Pike 按照技术演讲的传统展示了一段 “Hello, world” 代码,其中的 “world” 一词特意替换成了日文,他风趣地表示自己就不尝试去发音念出来了。整段代码在结构上依稀带有 C 语言的影子,但也包含一些初看略显怪异的特性。 ```mermaid flowchart TD accTitle: Go 语言的核心设计定位 accDescr: 展示 Go 语言结合系统级控制力与通用编程灵活性的双重定位 subgraph CoreAttrs ["核心属性"] A["并发支持 (Concurrent)"] B["垃圾回收 (Garbage-collected)"] C["系统级语言 (Systems language)"] end subgraph AppScenarios ["应用场景"] D["底层与网络系统<br>(Web 服务器、系统级组件)"] E["广义通用开发<br>(前端服务、日常应用程序)"] end CoreAttrs --> AppScenarios ``` - **开源时间表**:演讲录制于 2009 年 10 月底,计划于 2009 年 11 月 10 日正式开源。 - **语言定位**:实验性、并发、带垃圾回收的静态系统级编程语言,兼顾底层控制与通用开发效率。 --- ### [01:56](https://www.youtube.com/watch?v=rKnDgT73v8s&t=116s) 核心团队与项目缘起 虽然 Pike 站在台上代表团队宣讲,但他明确声明这绝不是他一个人的独角戏,而是一项高度协同的成果。项目最初的构想始于大约两年前的 2007 年底,当时 Robert Griesemer、Ken Thompson 和 Rob Pike 三人开始频繁讨论,试图寻找办法解决他们在现有开发环境中共同经历的痛点。随后的几个月里,他们大部分时间都在白板前勾勒想法、推演方案;到了 2008 年中,他们构建出了一个玩具级的编译器原型,各项特性开始展现出令人兴奋的潜力,三人随即投入了真正的全力研发。 不久之后,Ian Lance Taylor 与 Russ Cox 相继加入团队。他们五个人承担了 Go 语言早期绝大部分的核心设计与实现工作,同时在 Google 内部也得到了许多未能在单页幻灯片上一一列出姓名的同事的鼎力相助。回顾最初动笔写下第一行代码的原因,Pike 感叹当时开发中的一切都变得太慢了:编译太慢、构建太慢、连思考逻辑都变得过于冗长。谈及开发团队创新的初心时,Rob Pike 强调:“我们想要快速编程,我们想要享受编程的乐趣,而要让编程变得有趣,你就必须找到加速开发、让事物更顺畅运转的方法——不仅是代码本身的运行速度,编写代码的过程本身也必须极为高效。” - **创始成员**:Robert Griesemer、Ken Thompson、Rob Pike(2007 年末启动讨论与白板设计)。 - **早期关键成员**:Ian Lance Taylor、Russ Cox(2008 年中玩具编译器成型后加入)。 - **核心动机**:消除过长的编译与构建等待,让系统级编程重获效率与乐趣。 --- ### [02:40](https://www.youtube.com/watch?v=rKnDgT73v8s&t=160s) 过去十年软件开发环境的巨变与困境 Pike 提出了一个引人深思的疑问:编程曾经是一件充满乐趣的事情,究竟是哪里出了差错?事实上,在过去至少十年甚至更长的时间里,业内从未诞生过一门新的主流系统级编程语言,但这十年间硬件与软件的生态却发生了翻天覆地的剧变。软件工程在很大程度上演变成了一层又一层的代码堆叠过程:基础库变得越来越庞大繁杂,依赖关系盘根错节,如同茂密晦暗的灌木丛,让开发者深陷其中难以脱身。与此同时,网络计算全面普及,Sun Microsystems 当年提出的“网络即计算机”在今天已成现实,现代系统充斥着大量的客户端/服务器交互与超大规模计算集群,可现有的老牌系统语言最初全都是针对单机、单处理器环境设计的,在分布式与集群模型面前显得力不从心。 更雪上加霜的是硬件架构的底层变革。如今市场上已经很难买到单核的高端计算机,多核乃至多芯片多核架构早已成为标配,而主流语言的并发支持往往只是事后添加的修补机制。雪上加霜的是,构建软件的速度不仅没有随硬件性能的暴涨而加快,反而变得极其迟钝。依赖关系失控且缺乏语言级别的管控手段,导致开发者常常花费大量时间去重复编译根本没有用到的组件。与此同时,处理器的单核主频在过去几年中几乎停滞不前,摩尔定律正在逐渐放缓,但软件体积却仍在不受控地疯狂膨胀。面对这种硬件红利见顶、构建周期失衡的恶性循环,Rob Pike 严肃地指出:“不知何故,人们总感觉如果不采取行动,软件构建的过程就会变得慢到令人难以忍受,我们必须重新思考如何让这一过程重新变快。” ```mermaid flowchart LR accTitle: 软件构建困境与硬件演进的矛盾 accDescr: 分析软件体量激增与硬件主频停滞如何导致软件开发流程日益缓慢 subgraph HardwareChange ["硬件环境演变"] H1["单核主频停滞<br>(摩尔定律放缓)"] H2["多核/多芯片普及"] H3["大规模集群与网络化"] end subgraph SoftwareCrisis ["软件工程痛点"] S1["类库层叠与依赖失控"] S2["现有语言缺乏并发原生支持"] S3["构建与编译极度缓慢"] end HardwareChange -.->|"传统语言无法适配"| SoftwareCrisis SoftwareCrisis --> Out["软件开发流程日益痛苦沉重"] ``` - **时代脱节**:至少十年以上缺乏全新的主流系统级语言,无法匹配网络化与多核计算。 - **性能悖论**:单核主频增长停滞,软件体积持续膨胀,工具链依赖失控导致构建速度严重倒退。 --- ### [05:21](https://www.youtube.com/watch?v=rKnDgT73v8s&t=321s) 类型系统的两难与面向对象的局限 谈及语言特性的瓶颈,Robert Griesemer 曾敏锐地观察到:近几年来编程语言领域最受关注的热点,很大程度上是因为开发者在使用 C、C++ 甚至 Java 这类标准系统语言时,普遍觉得它们的类型系统异常笨拙、束手束脚。为了逃离这种压抑感,大量程序员转向了 Python、Ruby 甚至 JavaScript 等动态语言,并在那里重新找回了编程的乐趣,因为至少在程序正常运行期间,类型系统不会频繁地跳出来制造阻碍。然而,静态类型本身并不是敌人,强类型对于大型工程的健壮性至关重要,问题在于现有的类型系统实现方式往往走向了极端。 Pike 进一步剖析了许多“理论上很美好、实践中却糟糕透顶”的语言特性。典型代表就是 C 和 C++ 中的 `const` 关键字——它的初衷非常崇高,旨在提高代码的安全性,但在实际使用中却引发了极具传染性的语法麻烦与代码冗余,开发者为之付出的心智代价远远超过了它所带来的实际收益。面向对象编程(OOP)中将万事万物皆纳入单一继承树的理念也是如此。在大型真实系统里,类型之间并不存在天然严格的层级树状关系;强行削足适履的结果,就是程序员耗费海量精力在重构类型树、搬运基类与派生类上,而这些折腾与实际的业务逻辑实现毫无干系,纯粹是被语言的思维枷锁牵着鼻子走。面对效率与健壮性被割裂的现状,Rob Pike 总结道:“简而言之,以当今类型系统的运作方式来看,你可以在高生产力与类型安全之间二选一,但你根本无法两者兼得;这实在是一种遗憾,我们理应解决这个问题。” - **动态语言走红的反思**:动态语言的流行并非类型不重要,而是既有静态类型系统设计过于笨拙阻碍了流畅开发。 - **`const` 的教训**:良好的理论初衷在实际工业实践中可能引发极高的维护成本与代码摩擦。 - **类型层级批判**:大系统的抽象很难自然归纳为树状继承体系,机械化的继承树导致无谓的代码重构。 --- ### [06:54](https://www.youtube.com/watch?v=rKnDgT73v8s&t=414s) 从零打造新语言的核心诉求与编译速度实测 面对现有系统的固有缺陷,通过编写新类库来打补丁的方式完全行不通,因为代码库的无节制膨胀本就是问题根源的一部分,盲目增加抽象层只会让依赖雪上加霜。唯一的出路是“退后一步、另起炉灶”,从根本上重新审视软件构建与类型组织的方式。Pike 阐述了 Go 语言的核心愿景:既要拥有完全静态编译型语言的机器级运行效率,又要具备动态脚本语言般轻松惬意的编程体验。在安全维度上,语言必须坚守严格的类型安全与内存安全底线,杜绝指针非法运算和野指针引发的内存破坏。针对多核与分布式网络,Go 必须在语言底层原生支持轻量级并发与通信原语;在内存管理上,Go 坚信现代垃圾回收算法已经进化到可以实现超低延迟乃至近乎无感,完全能够将程序员从 C/C++ 繁琐手动的内存簿记泥潭中解放出来。最后,编译器必须快如闪电。 为了直观证明 Go 的速度,Pike 切换到终端进行了现场编译演示。他首先进入一个包含 1,300 行 Go 代码的库目录,执行 `make clean` 彻底清理后运行构建,结果仅用了大约 200 毫秒(0.2 秒)就生成了完全优化编译的高效机器码;Pike 随性地解释道,这一年在内部使用 Go 的同事们早已习惯了在单机上体验 100 毫秒级别的飞速构建,而且这完全是在他的个人笔记本单核处理器上跑出来的成绩,没有任何分布式编译集群的辅助。紧接着,他执行了更具说服力的演示:在笔记本上全量重新编译整个 Go 语言的官方源代码树。这套源码树包含了 Go 语言用自身编写的全部标准库(涵盖正则表达式、命令行参数解析、并发原语、底层运行时乃至垃圾回收器本身),总量大约在 12 万到 13 万行代码之间。Pike 刚向台下观众列举完这些模块,终端屏幕上的构建就已经宣告结束,单核编译全程仅仅耗时约 8 秒钟。 ```mermaid flowchart TD accTitle: Go 语言的核心设计目标 accDescr: 展示 Go 语言在性能、易用性、并发与安全性上的核心诉求 Goal["Go 语言核心设计目标"] subgraph DevExp ["开发效率与性能"] G1["静态编译的高性能"] G2["动态语言般的编写流畅度"] G3["极致的编译器构建速度"] end subgraph Robustness ["健壮性与现代体系"] G4["绝对的类型安全与内存安全"] G5["语言级并发与通信原语"] G6["低延迟自动化垃圾回收 (GC)"] end Goal --> DevExp Goal --> Robustness ``` - **核心设计平衡**:兼具静态编译的执行速度与动态语言的开发敏捷性。 - **底层安全保障**:杜绝裸地址推导与悬垂指针,确保严格的类型安全和内存安全。 - **现代化运行时**:原生并发与通信机制支持多核/网络,自动化低延迟垃圾回收消除手动内存簿记。 - **极速编译数据**: - 1,300 行 Go 库模块:笔记本单核耗时约 **200 毫秒**。 - 120,000 ~ 130,000 行完整 Go 标准库与运行时源码树:单核全量编译仅耗时约 **8 秒**。 ### [10:17](https://www.youtube.com/watch?v=rKnDgT73v8s&t=617s) 极致编译速度与核心设计原则 在演示编译构建时,仅仅耗时几秒钟,Rob Pike 便打趣道现在连这几秒对他来说都觉得漫长了,因为在这几秒里,其实已经把底层的全部软件都编译了一遍。他强调,开发人员真正追求的编译体验应当是“像敲下回车键一样瞬时完成”:“You want to have character return time for your compilations, and it can be done.”(你想要的是像敲击回车一样瞬间完成编译,而这是完全可以做到的)。极速编译对开发生活品质的改善是革命性的,过去长时间的编译往往让工程师陷入发呆甚至被琐事打断,而秒级构建让开发者能真正专注于工作本身。 为了达成这门语言的愿景,Go 的设计确立了几项核心原则。首先是高度保持概念的“正交性”(Orthogonality)。以接口系统为例,实现(Implementation)与接口(Interface)在设计上是完全正交、彻底解耦的;它们互不干涉,不需要在同一个对象中强行绑定定义,这让整体设计变得极为干净利落。Rob Pike 总结道:“And in practice, this is because a few orthogonal features that cover the space work a lot better in practice than a lot of overlapping lines.”(在实践中,用少数几个能够覆盖全局的正交特性,效果远胜于堆砌大量相互重叠的概念)。 其次,Go 的语法极其规整且简单,具有无冲突(Conflict-free)的特性,甚至无需符号表(Symbol table)即可完成语法解析。这一特性极大方便了 IDE、编辑器插件以及调试器等配套工具链的编写。在类型系统与输入负担上,Go 致力于“减少代码键入”(Reduce typing)并“弱化类型的存在感”。现代面向对象语言中充斥着类似 `Foo foo = new Foo()` 的机械重复样板,Go 希望类型系统能自动推断这些信息,在保持严格类型安全的同时消除繁文缛节。更深层次的转变在于摒弃传统的继承与类型层级,Rob Pike 直言:“We just think the program by constructing type hierarchies, is not the way to go.”(我们认为通过构建类型层级来编程并不是正确的方向)。相反,Go 采用了一种截然不同、更为轻量但本质上更深刻的面向对象方式。 ### [13:05](https://www.youtube.com/watch?v=rKnDgT73v8s&t=785s) 类型系统、常数与极速依赖解析 从全局架构来看,Go 拥有简洁凝练的语法与轻量级的类型系统。语言彻底废除了隐式类型转换(Implicit conversions),因为 C 语言中那些所谓的“常规转换规则”极易在实际编码中引发意想不到的混淆与错误。在消除隐式转换后,为了避免程序员被迫写满冗长繁琐的手工类型转换(Cast),Go 对常数系统进行了颠覆性设计。在 Go 中,常数是完全无类型的抽象数值,程序员再也不用在代码中写下诸如 `0x80ULL` 这样毫无意义的后缀:“The ULL is completely pointless, you can’t even say it, numbers are just numbers.”(ULL 完全毫无意义,你甚至念不顺它,数字就应该仅仅是数字)。 在工程架构层面,Go 引入了极其严格的显式包依赖模型(Package model)。程序所依赖的外部包必须显式声明,Rob Pike 特别强调了编译器的铁腕机制:“Compiler rejects your program if you put in the dependency you don’t need.”(如果你引入了不需要的依赖,编译器会直接拒绝编译你的程序)。这种严苛的设计保证了构建依赖关系的最小化,使工具分析变得轻而易举。 更为关键的是编译器在处理传递依赖时的“上卷”(Pull-up)机制。传统的构建工具在处理 `A.go` 依赖 `B.go`、`B.go` 依赖 `C.go` 的调用链时,往往必须在编译 A 时重新读取并分析 C 的符号或中间产物。而 Go 编译器在编译 B 时,就已经把 B 从 C 中获取的所有元数据直接上卷整合到了 B 的导出信息中。因此,编译 A 时甚至完全不需要触碰 C 的源码或目标文件。 ```mermaid flowchart TD accTitle: Transitive Dependency Pull-up in Go accDescr: Diagram illustrating how Go compiler pulls transitive dependencies from C into B, allowing A to compile without touching C. subgraph Step1 ["传统底层依赖"] C["包 C (C.go)"] end subgraph Step2 ["编译中间包 B"] B["包 B (B.go)"] C -.->|"导出元数据被上卷拉取"| B end subgraph Step3 ["编译顶层包 A"] A["包 A (A.go)"] B ==>|"仅需读取 B 的导出信息"| A C -.-x|"无需读取 C 的源码或目标码"| A end ``` 这种机制在单机小项目上看似不起眼,但在超大规模代码库中展现出惊人的威力。Rob Pike 强调:“And that doesn’t sound like a big deal, but when you’re at scale, it’s, literally, exponentially faster at building software when you get in to this huge dependents situation.”(这听起来似乎没什么大不了,但在超大规模构建下,面对庞大的依赖树,它让软件构建速度产生了字面意义上指数级的提升)。 ### [15:27](https://www.youtube.com/watch?v=rKnDgT73v8s&t=927s) 并发模型哲学:Goroutine 与垃圾回收的必然共生 针对服务器与系统级编程,Go 提出了一套基于垃圾回收的并发处理模型——`goroutine`。团队特意避开了“线程”(Thread)或“进程”(Process)等传统术语,创造了新词,因为它们的行为机制截然不同。Goroutine 由语言和运行时进行统筹管理,其调用栈具有动态自动伸缩能力:程序不需要预先设定栈的大小,需要更多空间时栈会自动增长,使用完毕后则自动收缩以节省内存,底层所有 goroutine 均被高效透明地多路复用在操作系统的物理线程之上。 这一并发模型能够稳定运转的关键支柱是垃圾回收(Garbage Collection, GC)。在多并发实体协同工作时,内存对象会频繁在不同的并发单元之间流转传递。如果没有自动化 GC,开发者将陷入“到底由谁持有内存、谁来负责释放”的噩梦级账务追踪中,手动管理的复杂度会迅速失控。Rob Pike 明确指出了两者的共生逻辑:“So you need garbage collection to make concurrency work well.”(因此你需要垃圾回收来让并发编程真正顺畅运转)。反过来,要让垃圾回收在高吞吐场景下高效工作,又反向要求底层语言在设计之初就为 GC 提供友好可行的内存模型,这也是 Google 必须从零打造一门全新语言的核心驱动力之一。 ### [16:48](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1008s) 基础语法剖析:声明方式与 Echo 程序实战 在词法表现上,Go 延续了类似 C 语言的风格,但引入了鲜明的改进。Go 的源文件强制要求采用 UTF-8 编码,天然无缝支持 Unicode 字符集。变量声明采用显式关键字引导,如 `const` 和 `var`。这里的 `const` 与 C 语言截然不同,例如声明常量 `const N = 1024` 时,N 只是一个纯粹的抽象数值,既不是固定位宽的 int 也不是 float。 在变量声明语法上,Go 采用了类型后置的设计(如 `var x, y float`)。虽然这与 C 语言中将类型写在前面的传统相反,但它彻底消除了 C 语法中多变量指针声明必须重复写 `*` 的语法瑕疵,让多个同类型变量的排布更加统一清晰。 ```go // 传统完整声明 vs 短变量声明推导 var t0 *T = new(T) // 语法显得冗余,重复出现类型 T t1:= new(T) // 使用:= 操作符,声明并根据右侧表达式自动推导初始化 ``` 为了彻底摆脱样板代码的束缚,Go 引入了源自旧时探索语言的 `:=` 语法(短变量声明并初始化)。在 `t1:= new(T)` 的写法中,类型会根据右侧表达式自动推导,直接省去了繁琐的手工类型注明。在日常编写 Go 代码时,`:=` 的出现频率远高于传统的 `var`。 在控制结构方面,Go 保持了与 C 相似的核心逻辑,但在语法细节上做出了关键裁剪与规范: - 代码块(Blocks)的花括号 `{}` 在所有控制语句中均是强制要求的,不允许单行省略。 - 条件判断语句(如 `if`)内部的判定表达式彻底去除了外围的小括号 `()`,这让整个语法树更加纯粹干净。 随后,Rob Pike 展示了一个替代经典“Hello, World”的完整小型程序——命令行回显工具 `echo`。程序以 `package main` 作为包声明开头,并以下方的 `func main()` 作为程序运行的唯一入口(即 `main.main`),同时引入了用于解析命令行参数的 `flag` 包,以此展开对实际代码组织方式的具体解析。 ### [19:50](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1190s) 命令行回显程序解析与变量初始化机制 Rob Pike 正在逐步解析一个用 Go 编写的类似 UNIX `echo` 的命令行工具。虽然它因为包含了真正的命令行标志(flag)解析而“不完全算是一个严格意义上的 UNIX echo”,但二者在本质功能上完全一致。代码顶部的 `import` 语句引入了两个关键包:负责底层操作系统交互的 `os` 包,以及负责处理命令行参数的 `flag` 包。 Pike 重点解释了 Go 程序的初始化生命周期。在 `main` 函数开始执行之前,诸如 `nFlag` 这样的全局变量就会完成初始化工作。Go 允许在 `main` 启动前通过任意复杂的逻辑来完成所有全局变量的求值与初始化。进入 `main` 函数后,程序调用标志解析函数,将命令行传入的参数拆解完毕。紧接着,程序通过一个常规的 `for` 循环遍历剩余参数,并使用 `+=` 操作符对字符串进行拼接累加。 在处理逻辑的末尾涉及了指针与输出。Go 的 `flag` 包所返回的标志变量实际上是指针类型,因此代码通过解引用判断 `*nFlag` 这个布尔值是否为 `false`;如果是,程序就会在末尾追加换行符。最终,程序调用 `os.Stdout` 相关的方法完成输出。Pike 笑着总结道,尽管这里的每一行语法都可能与大家习惯的旧语言有所不同,但整体逻辑非常直观透明:“虽然这里几乎每一行都和你以前见过的代码不同,但它在做什么应该是完全清晰明了的。我不认为你会觉得这个程序有什么神秘之处。” - `flag` 包解析出的标志变量均为指针类型(例如布尔标志返回 `*bool`)。 - Go 允许在 `main` 执行前对全局变量赋予包含任意复杂求值逻辑的初始值。 - 字符串支持使用 `+=` 直接进行追加操作。 --- ### [21:01](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1261s) 无类型与高精度常量系统(Type-less Constants & iota) 随后 Pike 转向了 Go 语言极具特色的常量设计。在 Go 中,常量被设计为“无类型”(type-less)且“无固定大小”的概念,它们本质上是纯粹的数值或字面量。尽管常量也可以是布尔值或字符串,但真正有趣且强大的是数值常量。Pike 通过定义一个名为 `TZ`(底层为 `int`)的时区类型展开演示:在圆括号包围的 `const (...)` 块中,同时声明了具有 `TZ` 类型的常量 `UTC` 和 `EST`,这种括号语法将 `const` 关键字一次性分发给了内部的所有声明项。 ```mermaid flowchart TD accTitle: Go 常量组与 iota 递增机制 accDescr: 展示 const 括号块中 iota 从 0 开始逐行递增并支持隐式复制上一行表达式的过程 subgraph ConstBlock ["const (...) 声明组"] direction TB C0["bit0, mask0 = 1<<iota, 1<<iota - 1<br/>(iota = 0)"] C1["bit1, mask1 = 1<<iota, 1<<iota - 1<br/>(iota = 1)"] C2["... 重复上一行表达式...<br/>(iota = 2, 3...)"] end C0 --> C1 C1 --> C2 ``` 这种常量组语法之所以至关重要,是因为 Go 引入了枚举计数器 `iota`。`iota` 在每个 `const` 声明组中都会将计数重置为 0,并且在遇到分号或换行时逐行递增(第一行是 0,下一行是 1,再下一行是 2)。Pike 给出了位掩码的构造范例:使用 `1 << iota` 声明 `bit0`,使用 `(1 << iota) - 1` 声明 `mask0`,随着行数推移即可自动生成 `bit1`、`mask1`。此外,Go 为了极力减少键盘敲击,允许在后续行仅写分号或标识符:这表示“继续重复上一行的声明与初始化表达式”,从而能够极为简洁地实现从 0 开始向下排列的一长串枚举常量声明。 更重要的是 Go 数值常量具备“任意精度”(arbitrary precision)的物理属性。Pike 展示了一个保留了 40 到 50 位小数的常数 $\ln 2$。这个精度不仅远远超出了 64 位浮点数(`float64`)的表示上限,但它在 Go 中完全是合法的常量声明,因为未定型的常量只是纯数学数值。当紧接着计算以 2 为底 $e$ 的对数(即 $\log_2 e = 1 / \ln 2$)时,除法运算直接在任意精度的数字空间内发生,而不是截断在有限浮点数空间内,从而得出了极其精准的倒数结果。Pike 强调:“像这种特性非常棒,因为你完全不需要做类型转换,也不用担心溢出或下溢,常量会自动为你搞定一切。” - `iota` 在每个 `const` 声明块中从 0 计数,每经过一次分号/换行自增 1。 - 常量组中若省略表达式,会自动沿用上一行的声明模式与表达式。 - 数值常量拥有无限精度的内部表示,在未绑定具体类型前不发生浮点截断或整数溢出。 --- ### [23:49](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1429s) 复合类型:切片、映射、函数类型与结构体字面量 Pike 随后梳理了 Go 中常见的复合值类型。他特别指出,尽管 Go 存在底层数组,但数组有其特殊的局限性,在日常编码中大家极少直接使用数组,而是广泛使用名为切片(slice)的引用类型。例如,代码中声明了一个包含 `"Saturday"` 和 `"Sunday"` 的字符串切片(`[]string`)。紧接着,代码声明了一个从字符串到 `TZ` 类型的映射表 `timeZones`(`map[string]TZ`),其声明和键值初始化一目了然。 在函数与结构体方面,Pike 展示了一个普通的加法函数 `add` 的声明,并定义了一个名为 `Op` 的函数类型(接收两个 `int` 并返回一个 `int`),因此 `add` 函数可以直接赋值为 `Op` 类型变量。他补充说,虽然当天不会展开讲,但 Go 的函数在多返回值等特性上还有很多精妙的设计值得大家去查阅。随后他展示了名为 `RPC` 的结构体声明: ```go type RPC struct { A, B int op Op result *int } ``` Pike 指出,即使对习惯了 C 语言的程序员来说这些类型后置的语法看起来有些倒装,但逻辑结构极其清晰。最后,Go 支持极其轻量的结构体字面量(struct literal)初始化方式:只需要写出类型名称,跟上一对大括号,在大括号内部直接列出各个字段的初始化元素即可,无需编写专门的构造函数就能快速构建出一个结构体实例或将其赋给新变量。 --- ### [25:09](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1509s) 标识符可见性规则与结构体方法 Pike 进一步深入 Go 的面向对象设计,明确指出了方法(methods)的独特性。在探讨方法之前,他首先揭示了 Go 语言极其简洁的可见性规则:代码中定义了一个包含 `X` 和 `Y` 的 `Point` 结构体,这两个字段之所以大写,是因为 Go 完全抛弃了诸如 `public`、`private` 之类的关键字。Pike 斩钉截铁地说明:“在 Go 中,决定一个标识符在包外部是否可见的唯一依据,就是它是否以大写 Unicode 字母开头。仅此而已。”包顶层的大写变量或类型对外部客户端可见,小写则对外不可见;结构体内部的方法或字段若是大写,包外客户端就能访问,小写则被隐藏。 ```mermaid flowchart LR accTitle: Go 接收者与方法定义语法 accDescr: 展示 func 关键字、接收者声明与方法名、参数列表的组合关系 subgraph MethodSignature ["方法签名解构"] F["func"] --> R["(p *Point)"] R --> M["Scale(s float)"] M --> B["{ p.X *= s; p.Y *= s }"] end R -.->|"显式接收者<br/>(无隐式 this)"| M ``` 关于方法的声明位置,细心的人会发现方法并没有写在 `struct` 声明的花括号内部。Pike 解释道,这是因为在 Go 语言的设计哲学里,“方法和结构体是彼此独立的两个概念”。正因为方法可以绑定在结构体以外的其他任何类型上,如果强行把方法塞进结构体内部声明就会变得不合逻辑。在语法上,声明方法只需在 `func` 和方法名之间加上一个括号,填入类似单元素参数列表的接收者(receiver)声明,指代被调用的主体。例如 `func (p *Point) Scale(s float)` 表示为 `*Point` 指针类型绑定 `Scale` 方法。Pike 强调,Go 没有自动隐式的 `this` 指针,接收者参数是完全显式声明的,若要访问结构体字段必须通过该接收者名称调用。 紧接着 Pike 演示了求模长的方法 `Abs`,内部调用了 `math` 包中的平方根函数来计算向量距离。在创建实例时,可以使用 `&Point{3, 4}` 语法,通过取地址符结合字面量快速在堆上分配并初始化一个指向该结构体的新指针,这起到了类似构造函数的作用,随后便能自然地发起 `x.Scale(...)` 这样的方法调用。 - 符号首字母大写即为公开(exported/public),首字母小写即为包内私有(unexported/private)。 - 方法未内嵌在结构体中,二者是解耦的概念。 - 接收者必须显式命名,Go 不存在隐式的 `this` 或 `self`。 - `&TypeName{...}` 提供了类似构造函数直接分配并初始化指针实例的简洁手段。 --- ### [27:32](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1652s) 在基本自定义类型上定义方法与 String 接口初探 方法不仅限于指针或结构体,Go 允许为用户定义的任意类型绑定方法。Pike 展示了一个完整的小程序来印证这一点。该程序首先引入了包含格式化打印功能的 `fmt` 包,随后重新把 `type TZ int` 拿出来作为基础类型。代码接着定义了几个以小时为单位的 `TZ` 常量(例如 1 小时等于 `60 * 60` 秒,以及 `UTC = 0` 小时、`EST = -5` 小时),并且建立了一个名为 `timeZones` 的映射,用于从时区名称映射到时区数值。 关键之处在于,Pike 直接为这个本质上只是整数的自定义类型 `TZ` 绑定了一个名为 `String` 的方法:`func (tz TZ) String() string`。Pike 特别提醒听众注意:“请注意它不是指针,这非常重要。它完全不必是指针。”在 `String` 方法内部,程序使用 `for name, zone:= range timeZones` 遍历映射表中的元素。这一逻辑相当于反向查找映射,一旦找到某个元素的时区数值等于当前接收者 `tz` 的值,就立即返回对应的时区名称。如果遍历结束后仍未命中已知的固定时区,方法最后通过 `fmt.Sprintf` 格式化并返回一个由计算生成的全新字符串(Pike 强调 Go 的 `Sprintf` 与标准 C 不同,它不会写入预先准备的缓冲区,而是直接构造并返回一个动态字符串)。 这种设计的神奇之处在于其与打印系统的无缝融合。在 `main` 函数中,开发人员只需要直接写出 `fmt.Println(EST)`,无需提供任何格式化占位符。打印系统能够在底层自动探测到 `TZ` 类型实现了一个大写的 `String` 方法,并在输出时自动调用该方法把对象优美地打印成 `"EST"`。正如 Pike 所描述的那样,通过这种机制,类型本身便掌握了自我打印的规则,使得代码在调用端变得极其优雅而简洁。 - 方法可以挂载在任何非指针、非接口的自定义命名类型上,哪怕其底层仅仅是基础整型 `int`。 - 接收者可以按值传递(值接收者),并不强制要求写成指针形式。 - `for... range` 能够同时提取出映射的键与值,配合 `:=` 快速声明循环迭代变量。 - 实现了 `String() string` 方法的类型会被 `fmt.Println` 等函数自动识别并用于漂亮的字符串格式化输出。 ### [30:08](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1808s) 接口(Interfaces):Go 语言最具创新性的设计 Rob Pike 总结了为常规类型添加方法的讨论,指出任何 Smalltalk 程序员都会深知在通用类型上绑定方法是极其便利的。然而,Go 语言将方法从结构化的类(Classes)概念中彻底剥离出来,引入了一种崭新且趣味十足的设计范式。随后,Rob Pike 切入了整个语言设计中最具革命性的部分。他直言: > “Okay. So now, we come to something that I will not be able to describe in full in the time available, but they are pretty interesting and they are arguably the most noble about Go, and that is the concept of an Interface.” Go 语言中的接口本质上是对“一组方法集合”的形式化定义。例如,声明一个名为 `Magnitude` 的接口类型,规定凡是实现了返回 `float` 类型的 `Abs()` 方法的任何类型,都属于实现了 `Magnitude` 接口。回顾前文为指针类型 `*Point` 声明的 `Abs` 方法,用户可以直接将包含坐标 `(3, 4)` 的 `Point` 变量 `x` 赋值给类型为 `Magnitude` 的接口变量 `m`。赋值之所以合法,纯粹是因为 `x` 具备该接口所需的方法实现。 紧接着,Rob Pike 演示了接口的多态特性。如果另行定义一个具有三维坐标的 `Point3` 类型并同样为其实现 `Abs()` 方法,那么接口变量 `m` 也能直接接收 `Point3` 的实例,底层对象自动切换,调用 `m.Abs()` 便能获取三维向量长度。随后他又声明了一个极坐标结构体 `Polar`,包含公有字段 `R` 和 `Theta`(由于公有导出规则,首字母大写)。针对 `Polar` 结构体,无需使用指针,直接为值类型实现返回半径 `R` 的 `Abs()` 方法,并将该值赋给 `m`。从二维笛卡尔坐标、三维坐标到极坐标,这三者在数据结构上截然不同,但都可以无缝赋值给同一个接口变量。 这种设计的杀手锏在于“隐式实现”(Implicit Implementation): > “And the key point is that nowhere does point three or polar have to tell you that it implements the magnitude interface.” 类型和接口之间不存在任何显式的声明依赖,类型实现接口属于水到渠成的既定事实。这催生了极大的通用性——类型与接口是多对多的自由组合关系,一个结构体可根据使用上下文同时满足多个不同的简单接口。 ```mermaid flowchart TD accTitle: Go 接口隐式实现示意图 accDescr: 展示 Point 指针、Point3 和 Polar 类型如何无需显式声明即自动满足 Magnitude 接口。 subgraph InterfaceDef["接口契约"] M["interface Magnitude<br/>Abs() float"] end subgraph ConcreteTypes["具体类型实现"] P1["*Point (x, y)<br/>实现 Abs() float"] P2["Point3 (x, y, z)<br/>实现 Abs() float"] P3["Polar (R, Theta)<br/>实现 Abs() float"] end P1 -.->|"隐式满足"| M P2 -.->|"隐式满足"| M P3 -.->|"隐式满足"| M ``` - **多态赋值机制**:接口变量内部封装了具体的实际对象及其方法表,外层调用统一派发至底层类型的具体实现。 - **解耦声明**:无需像 Java 等语言那样编写 `implements Magnitude`,杜绝了接口与其实现者之间的强耦合与库依赖。 --- ### [33:40](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2020s) `io.Writer` 与正交的 I/O 缓冲架构 为了证明极简通用接口的强大威力,Rob Pike 重点剖析了 `io` 标准库中的核心接口——`io.Writer`。`Writer` 接口非常纯粹,只要求实现一个标准的 `Write` 方法,该方法接收一个字节切片(切片内含长度信息,因此不再需要显式传递字节计数参数),并返回写入字节数和错误对象。 > “The writer said anything that implements this standard form of the write call can be used to write it to. It makes sense. This is just--it's almost a tautology.” 正因为这一极简抽象,任何只需写入功能的调用方都可以消费任何实现了 `Write` 的对象。例如,标准库中的 `fmt.Fprintf` 并不像 C 语言那样限定第一个参数必须是文件指针,它的第一个参数仅仅是一个 `io.Writer`。这意味着 `fmt.Fprintf` 能够直接向网络连接、管道、文件描述符、内存缓冲区、甚至加密流水线中输出格式化数据。 这一设计还彻底重塑了缓冲 I/O 的组织方式:在 Go 中并不存在所谓专有的“缓冲 I/O”,而只有通用的“缓冲层包装”。若要为某个目标提供缓冲写入功能,只需将原本的 `io.Writer` 传递给 `bufio.NewWriter`,后者会返回一个新的包装对象,而该对象本身同样实现了 `io.Writer`。Rob Pike 展示了一段完整代码:程序先通过 `fmt.Fprintf(os.Stdout, "hello")` 直接向标准输出写入;接着把 `os.Stdout` 送入 `bufio.NewWriter` 生成缓冲写入器 `buf`,随后再次调用 `fmt.Fprintf(buf,...)` 进行缓冲写入,最后执行 `buf.Flush()`。两次调用向 `fmt.Fprintf` 传入了两种截然不同的类型,但在编译期类型检查中全部合法,因为两者在底层无一例外都满足 `io.Writer` 契约。 - **`fmt.Fprintf` 的签名设计**:第一个参数为 `io.Writer`,实现对文件、网络、内存缓存等所有输出载体的正交支持。 - **缓冲即装饰器**:`bufio.NewWriter` 接受一个 `io.Writer` 并返回一个带缓冲特性的新 `io.Writer`,消除了传统 I/O 库中复杂的类继承层级。 --- ### [36:09](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2169s) 通道(Channels)与 Goroutine 并发基石 在梳理完接口之后,Rob Pike 将目光转向了并发模型。Go 的通信通道(Channels)虽然源自 Tony Hoare 经典的通信顺序进程(CSP)理论,但与 Hoare 最初的 CSP 构想不同,Go 将通道设计为了“头等公民值”(First-Class Values)。 通道具有严格的类型约束。例如,类型为 `chan string` 的通道只能在其上传递字符串,其行为类似于一个类型安全的管道。Go 引入了左向箭头 `<-` 作为通信操作符:当中缀使用时(如 `c <- "hello"`),表示向通道 `c` 发送字符串;当作为前缀使用时(如 `greeting:= <-c`),表示从通道 `c` 接收数据。默认情况下通道是同步阻塞的,这天然为不同并发执行流提供了协调和同步能力。更重要的是,作为头等公民值的通道不仅可以通过变量传递,甚至可以通过通道自身发送给其他实体,从而实现极具表现力的动态通信拓扑。 随后,Rob Pike 结合关键字 `go` 介绍了 Goroutine 的使用。假设某个计算任务极其耗时,主执行流不希望被长期阻塞。传统的解决方式在此被优雅重塑: > “What you can do instead is create a channel to get the answer back on. And then declare a function, wrapper, that takes the value of the parameter for the calculation, and a channel invokes the calculation and then when it is done, sends the result back.” 定义一个包装函数 `wrapper(a int, c chan int)`,在内部执行重度计算并将结果写入通道 `c`。调用时只需在函数调用前加上关键保留字 `go`(即 `go wrapper(17, c)`),该计算就会立刻脱离当前流程转入后台并发运行。主流程在处理完其他业务后,只需执行 `result:= <-c` 从通道接收数据即可,整个流程自然、直接且易于推理。 ```mermaid flowchart LR accTitle: Goroutine 与通道异步计算模型 accDescr: 展示主流程通过 go 关键字派发后台 Goroutine,并通过 Channel 异步取回结果的过程。 subgraph MainFlow["主流程 (Main Goroutine)"] A["创建通道 c"] --> B["派发 go wrapper(17, c)"] B --> C["执行其他独立业务"] C --> D["等待并读取: <-c"] end subgraph WorkerFlow["后台任务 (Background Goroutine)"] W1["执行重度计算: longCalculation(17)"] W2["写回通道: c <- result"] W1 --> W2 end B -.->|"后台启动"| W1 W2 -.->|"数据同步传输"| D ``` - **类型安全管道**:通道严格受类型系统约束(如 `chan string`、`chan int`),保障并发通信安全。 - **操作符语义**:中缀 `c <- v` 代表发送数据,前缀 `<-c` 代表阻塞接收数据。 - **并发启动成本极低**:只需单一保留字 `go` 加函数调用,即可开辟后台轻量级并发单元。 --- ### [38:36](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2316s) 高并发多路复用服务端的实现 为了展示通道作为头等公民的高阶威力,Rob Pike 演示了一个支持高并发的多路复用服务端(Multiplexed Server)骨架代码。服务端要解决的核心问题在于:如何让多个客户端并发发起请求,且各自从独立的通道中收回对应的计算结果? Go 的解法是利用通道传递通道。首先定义一个通用的 `Request` 结构体,内部包含待计算的操作数以及一个专供服务端回传结果的响应通道 `replyc`: ```go type Op func(int, int) int type Request struct { a, b int replyc chan int } ``` 接着定义辅助函数 `run`,它接收运算符 `op` 与请求对象 `req`,执行具体运算并将结果送入请求内部自带的通道:`req.replyc <- op(req.a, req.b)`。服务端的常驻循环 `server(op Op, service chan *Request)` 则持续从接收通道读取外部发来的请求。为了避免服务被耗时任务卡死,服务端在调度计算时直接启动新的 Goroutine: > “And it’s just sits in a loop reading request from the service channel and then saying run of Op request, but it puts a Go keyword at the beginning. So, it runs all these requests in parallel in the background. The server is never blocked.” 最后,服务端暴露一个首字母大写的公有构造函数 `StartServer`。该函数内部申请通道内存,使用 `go server(...)` 启动后台服务循环,并将外部可写入的请求通道作为返回值直接回传给调用方。即便没有涉及复杂的限流与线程池管理,这种结构也用区区十几行代码构建出了一个永不阻塞、天生并发的异步多路复用服务模型。 - **通道作为字段传递**:请求体 `Request` 内部自带结果通道 `replyc`,服务端计算完成后“原路投递”,实现了请求与响应的天然多路复用。 - **无阻塞派发循环**:主分发循环通过 `go run(op, req)` 将每一个到来的任务瞬时甩到后台执行,维持监听通道的敏捷响应。 - **极简封装**:`StartServer` 函数在启动并发服务的同时将服务入口通道以返回值的形式交付客户端,对外屏蔽了内部并发细节。 ### [40:27](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2427s) 基于 Channel 与闭包构建服务端并发模型 Rob Pike 继续讲解基于 Channel 的通信机制。通过调用 `startServer` 函数,调用方可以获得一个 Channel 句柄,从而拥有了与服务端通信的途径。Rob Pike 强调,这是利用 Go 语言特性构建服务的最核心理念:“So, you call a function you get a channel back, now you got a thing you can talk to and that is the fundamental idea on how you use this stuff to build services, okay?” 在客户端代码的编写中,Go 语言支持直接内联编写函数,这意味着完整的闭包(closure)支持与表达式级写法。调用方可以启动一个后台运行并负责加法运算的服务端,并将该服务对应的 Channel 存入局部变量 `server` 中。随后,客户端创建若干请求对象,并在每个请求内部各自独立分配专用的响应 Channel,以便接收独立的回执。 发送请求的过程完全非阻塞,客户端无需关心发送顺序。由于底层执行完全是并行的,即便先发送请求 1 再发送请求 2,客户端也可以选择先等待请求 2 的返回结果;即便请求 2 耗时极长,程序也会一直阻塞等待,直到请求 2 结果返回并打印后,再去打印请求 1 的结果——哪怕请求 1 实际上更早计算完毕。 ```mermaid flowchart LR accTitle: Client-Server Channel Pattern accDescr: Client allocates response channels inside requests, sends them to server, and awaits replies asynchronously. subgraph Client["客户端流程"] C1["构造 Request 1 (含 Chan 1)"] C2["构造 Request 2 (含 Chan 2)"] W2["等待 Chan 2 结果"] W1["等待 Chan 1 结果"] end subgraph Server["服务端 Goroutine"] S["处理计算任务"] end C1 -->|"发送"| S C2 -->|"发送"| S S -->|"响应结果"| W2 S -->|"响应结果"| W1 ``` ### [41:42](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2502s) Select 控制结构与未展开的语言特性 针对此前服务端存在的“速率限制缺陷”以及“一旦启动便永久运行、无法正常退出”的问题,Rob Pike 引入了 Go 语言中功能丰富的 `select` 控制结构。`select` 充当了“面向通信的 switch 语句”,其核心逻辑是监听一组 Channel 操作,阻塞等待其中任意一个准备就绪;一旦有通道可读或可写,就执行该分支。 通过在服务端的内层循环中引入 `select`,并额外传入一个名为 `quit` 的 Channel,即可优雅地控制退出流程:平时循环正常接收请求并派生 Goroutine 进行处理;一旦外部希望停止服务,只需向 `quit` Channel 发送一个任意值的布尔信号,`select` 中对应的分支就会立即触发并退出函数,从而安全终结该 Goroutine。 Rob Pike 对讲授节奏过快表示歉意,并提及 Go 还有许多设计精巧的特性因时间关系无法展开: - **包构建与初始化(Package Construction & Initialization)**:Go 的初始化机制非常强大,隐式做了大量工作,可以用极少量的样板代码搭建起 RPC 服务。 - **全功能反射(Full Reflection)**:支持对函数、Channel、Map 等任意类型进行运行时反射探测与动态类型操作。 - **结构体嵌入(Embedding)**:类似于面向对象中的继承,但在概念上更为简洁通用。 - **迭代机制与测试支持**:基于 `for... range` 的通道与切片迭代,以及语言内置的单元测试软件套件。 ### [43:44](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2624s) 10 万个 Goroutine 链式通信吞吐测试 Rob Pike 阐述了设计 Go 语言的核心初衷之一:服务于高并发网络服务的构建。现代工业界面临的经典困境是“单请求单线程(Thread per request)”模型开销沉重、资源消耗极大,且在绝大多数主流语言中极易出错。Go 的目标是让开发者能轻松驾驭成千上万、乃至数十万个轻量级执行流:“And we want to make it easy to think about having tens of thousands or even hundreds of thousands of threads—-whatever they are-—working on your behalf.” 为了证明这一能力,Rob Pike 现场演示了一个创建 10 万(100,000)个 Goroutine 的基准测试程序。程序定义了一个函数 `f`,其逻辑极其单纯:从右侧 Channel 读取一个整型数值,加 1 后,写入左侧 Channel。在 `main` 函数中,循环将 10 万个 Goroutine 串联在一起,就像在一条线上串起 10 万颗珍珠(“like pearls on a string”),每一个节点都在等待上游喂给它一个数值。随后,程序向链条最右侧注入初始值,数值瞬间沿着管道层层击穿传递至最左侧输出,最终结果恰好等于 100,000。 现场演示运行在一台轻薄的 MacBook Air 上。程序从初始化、通信传递到完整拆除销毁,耗时仅约 1.5 秒。Rob Pike 特别强调,这不是瞬间粗暴杀进程的把戏,而是实打实的一套合法流程:“It’s kind of a minimum thing you can do that, but still, hundred thousand is an interesting number and we could go more but it start to use up memory.” 他补充道,底层运行时在遇到阻塞式 I/O 时会自动调度到底层真实系统线程上,开发者只需保留极简的心智模型即可。 ```mermaid flowchart LR accTitle: 100,000 Goroutine Pipeline accDescr: Data flows from rightmost goroutine to leftmost goroutine, incrementing by 1 at each step. In["输入值 0"] --> G99999["Goroutine #99,999 (n+1)"] G99999 --> Dots["... 串联通信..."] Dots --> G1["Goroutine #1 (n+1)"] G1 --> G0["Goroutine #0 (n+1)"] G0 --> Out["最终输出 100,000"] ``` ### [46:56](https://www.youtube.com/watch?v=rKnDgT73v8s&t=2816s) 双编译器路线、运行时架构与 GC 演进规划 关于 Go 语言的工程落地现状,Rob Pike 透露团队目前维护着由两种不同编译器技术构建的完整实现: 1. **Ken Thompson 的编译器套件(6g/8g/5g 系列)**: - 架构基于 Plan 9 编译工具链思想从零编写,当前用 C 语言实现以解决自举(bootstrapping)问题。 - 命名采用数字代号规则:`6g` 对应 64 位 x86(AMD64),另有 32 位 x86 实现(`8g`,音频口误作 3D 61)以及 ARM 编译器(`5g`)。 - 特点是编译极速,生成的机器码质量虽然略逊于 GCC,但远超一般快速编译器的表现。该编译器具有独立的链接规则,无法与 GCC 输出直接混编,但其 C 语言外部函数接口(FFI/cgo)推进顺利,同时正借助 SWIG 探索调用 C++ 代码的能力。 - 由 Kyle 负责的 ARM 后端测试套件符合度已达到 97%,赶在开源发布前即可就绪。 2. **Ian Lance Taylor 的 GCC 前端(GCC Go)**: - 采用 C++ 编写作为 GCC 的前端实现,完整支持 Go 规范。 - 依赖 GCC 成熟的后端优化,生成的机器码质量卓越,但编译过程相比 6g 要慢 4 到 5 倍。 - Rob Pike 指出,Go 真正的编译体验优势来自于整体编译速度的设计(依赖模型清晰),而非单纯依赖单编译器进程的吞吐。 在生成代码的运行性能方面,排除部分尚未优化的类库开销,在典型的内层循环与标准业务逻辑下,Go 相比原生 C 语言的性能损失仅在 10% 到 20% 之间(GCC Go 慢约 10%,Ken 的 6g 慢约 20%,即达到 C 的 1.1x 至 1.2x 耗时)。Rob Pike 对此非常满意:“So, 1.1x to 1.2x, but this is for a type save, statistically compiled garbage collection language. It’s pretty nice.” 最后,Rob Pike 介绍了强大的底层运行时(Runtime)。该运行时负责内存分配、动态类型反射、Channel、Slice、Map 以及分段栈(Segmented Stacks)。`6g` 已经利用分段栈技术实现了 Goroutine 初始极小内存占用与按需动态增长;GCC Go 也计划在年底前补齐该能力。在垃圾回收(GC)方面,当前 `6g` 搭载了一个基础但有效的 Mark-and-Sweep(标记-清除)收集器。团队正致力于下一代多核并发垃圾回收技术,参考 IBM 的低延迟 GC 技术,力争在多核环境下实现接近零延迟、低开销的并发回收,彻底规避传统托管语言的延迟陷阱。 ### [50:57](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3057s) 运行时垃圾回收与标准库建设 在深入探讨 Go 的系统底层时,Rob Pike 指出团队正在全力攻关垃圾回收器(Garbage Collector)的停顿问题。团队过去在其他语言上的实践经验表明该问题完全能够攻克,随着当下多核多处理器机器的普及,他们更有信心将其彻底解决。目前 Gccgo 尚未配备垃圾回收器,但团队正在构建一个通用的运行时系统(Common Run-time),未来这套垃圾回收器将设计成能够同时配合 Ken Thompson 的专用编译器和 Gccgo 运行,“It’s part of the general run-time so both compilers will have real garbage collectors inside of them.”。 在库的生态建设方面,Go 已经打下了非常坚实的基础,尽管未来仍有海量工作要做。Go 已经提供了完整的操作系统交互与 I/O 支持、简洁高效的数学计算库(math)、强大的字符串处理与 Unicode 支持,以及一个初步成型但完全可用的正则表达式实现。此外,运行时反射(Reflection)、命令行参数解析(flags)和日志系统(logging)使用体验极佳;散列与加密算法(crypto)、高质量的单元测试工具及其配套库、标准网络通信库一应俱全。Go 还自带了一套非常优雅的原生 RPC 实现,以及一个基于 Andy Chu 工作成果的模板库,能够通过极简的数据驱动模式生成 HTML 或任意格式文本。 - **运行时支持**:规划中的垃圾回收器将作为通用运行时组件,同时服务于 6g/8g 系列与 Gccgo 两套编译体系。 - **已就绪的核心库**:包含 OS、I/O、数学运算、Unicode 字符串、正则表达式、反射、命令行 flags、日志、加密哈希、网络通信及原生 RPC。 - **模板引擎**:由 Andy Chu 的设计启发而来,支持以数据驱动方式生成 HTML 或其他结构化文本。 --- ### [52:18](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3138s) 文档与代码规范的终结者:Godoc、Gofmt 与调试器 Go 语言中最引人瞩目的程序是一对共享大量底层代码的兄弟工具:Godoc 与 Gofmt。Godoc 的定位类似于 Java 的 Javadoc,用于检索并提供包和函数的文档。官方网站 golang.org 本身就是由 Godoc 驱动运行的,它不仅直接呈现着网站首页,还挂载了语言规范、技术文章和 FAQ。更精妙的是其包目录系统,能够自动提取并生成极其丰富的包级文档;而在源码浏览路径下,展示的代码均已经过了 Gofmt 的美化与排版。Gofmt 是一个代码格式化工具,Go 官方代码仓库中的所有代码均必须经由它格式化。Rob Pike 强调,团队不打算制定冗长烦琐的代码风格守则,而是直接提供一个程序来定义代码长什么样;在提交代码前只需跑一遍 Gofmt 就能终结所有无休止的排版争论。对此,Rob Pike 精辟地指出:“If you wanted debate, we got to debate by changing Gofmt. You can’t debate by changing a document, and I think it’s actually, it does an amazingly good job.”,并将主要的功劳归于 Robert Griesemer。 关于程序调试工具,虽然尚不能立刻发布,但已经非常接近可用状态。由于 GDB 无法识别 Ken Thompson 编译器输出的符号表,直接调试 6g/8g 编译出的二进制程序体验很差;而 Gccgo 虽然可以配合 GDB 使用,但 GDB 会误将其当成普通的 C 语言程序来调试,符号表存在怪异现象,且对 Go 的运行时环境一无所知。为了实现一套真正理解 Go 语言运行时的调试器,暑期实习生 Austin Clement 做了大量扎实的开发工作。 ```mermaid flowchart TD accTitle: Go 文档生成与源码格式化工作流 accDescr: 展示 Go 源码如何分别通过 Godoc 生成富文档服务以及通过 Gofmt 统一排版入库 subgraph S1["源码与工具链"] SRC["Go 源码文件"] GFMT["Gofmt 排版工具"] GDOC["Godoc 服务引擎"] end subgraph S2["输出形态"] REPO["统一风格的代码仓库"] WEB["golang.org 在线文档与源码浏览"] end SRC --> GFMT GFMT -->|"消除风格争议"| REPO SRC --> GDOC REPO --> GDOC GDOC --> WEB ``` --- ### [54:15](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3255s) 为什么 Go 语言暂时没有泛型? 面对社区几乎所有人都会追问的核心问题——“Go 为什么没有泛型(Generics)?”,Rob Pike 给出了坦诚而严谨的技术解释。Go 团队目前之所以没有引入泛型,是因为他们还没有彻底想透泛型在 Go 的体系中究竟应该扮演什么角色。简单的示例固然很容易写出,但要得出一个完整且毫无瑕疵的定义则极其复杂,而团队绝不愿意在没有十拿九稳前草率行事。从某种程度讲,Go 没有类型继承,这剪除了泛型的一大复杂分支;但从另一角度看,Go 允许为任意大小的值类型定义方法,这种灵活性反过来又给泛型机制增加了全新的复杂度。 与此同时,Go 语言中泛型的紧迫性被其他特性在很大程度上缓解了。内置的 map、slice 以及 interface 已经涵盖了许多原本需要泛型才能解决的常见场景。开发者目前完全可以使用“空接口”(`interface{}`)来构建通用容器,因为任何类型都隐式满足没有任何方法的空接口(这在概念上稍微类似于 Java 中的 `Object`,但底层语义截然不同)。虽然当前基于空接口构建集合存在必须手动解包拆箱、牺牲编译期类型安全的缺陷,仅是摸索期的权宜之计,但团队宁可审慎观察。Rob Pike 援引了 Joshua Bloch 在《Effective Java》中的长篇论述,指出即便大家都认为 Java 的泛型做得很成功,Bloch 依然强调将泛型做对的难度远超常人想象,必须经过深思熟虑:“So generics, not yet but they're very subtle.”。 - **权衡的复杂性**:没有类继承降低了部分设计门槛,但支持为任意大小的值定义方法使得统一实现极其微妙。 - **内建替代方案**:通过内置的哈希表(map)、动态切片(slice)以及方法接口,许多泛型的常规应用已被原生化解。 - **空接口过渡**:目前使用 `interface{}` 构建集合库,虽牺牲了静态类型安全且需要显式断言,但在设计确定前足以支撑日常开发。 - **谨慎借鉴前人**:参考 Joshua Bloch 对泛型复杂度的反思,Go 团队选择宁缺毋滥,而非匆忙引入半成品。 --- ### [56:11](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3371s) 语言设计哲学:“我的特性去哪儿了?” 每个程序员在上手新语言时都会带着属于自己的功能清单,常常会疑惑“为什么没有我最想要的特性?”。Rob Pike 指出,这些缺失往往是深思熟虑后的刻意取舍。有些特性存在替代形式,例如 Go 没有传统的枚举(enum),而是提供了独特的 `iota` 机制;有些特性则因为破坏核心设计原则而被坚决抛弃,最典型的便是指针算术(Pointer Arithmetic)。指针算术固然灵活高效,但它与内存安全水火不容。为了捍卫语言的绝对安全性,Go 宁可彻底剔除指针算术,转而依赖编译器来优化数组下标边界检查带来的性能损耗:“Like there’s no point of arithmetic, point of arithmetic is nice but we can’t do a point of arithmetic and make a language safe, there just not there.”。 至于其他暂未实现的特性,并不代表它们永远不会出现,只是优先级排序问题。例如联合类型(Union Types),团队已经有了相当不错的初步构想,但尚未付诸实现。Rob Pike 鼓励开发者跳出固有的思维定式:Go 是一门特立独行的语言,哪怕它暂时缺乏某些你习惯的语法糖,但它引入的新概念(例如独特的隐式接口设计)绝大多数人此前从未接触过。开发者不应因为缺少某些预期功能就放弃尝试,官方网站上的“语言设计 FAQ”也专门详尽阐述了各项特性“为什么不在 Go 里面”的背后考量。 | 备受关注的特性 | 在 Go 中的设计现状与替代机制 | 团队核心决策考量 | |:--- |:--- |:--- | | **枚举 (enum)** | 由 `iota` 常量生成器替代 | 机制不同但能解决常量序列需求,保持概念精简 | | **指针算术** | **坚决不支持**(仅保留安全指针引用) | 指针算术破坏内存安全与垃圾回收,由编译器优化下标访问来弥补性能损失 | | **泛型 (generics)** | 暂缓实现(以 `interface{}`、map、slice 暂代) | 语义极其微妙且设计影响深远,未达完全自洽前不盲目引入 | | **联合类型 (union types)** | 方案构思中,尚未正式落地 | 已有良好提案设想,属于未来演进的潜在候选特性 | --- ### [57:53](https://www.youtube.com/watch?v=rKnDgT73v8s&t=3473s) 结语与开源邀请:一门高效而舒适的新语言 在演讲的尾声,Rob Pike 流露出了真挚的热忱,表示自己在这门语言中找回了久违的编程乐趣,因为 Go 让他真真切切地感受到了工作效率的爆发:“I’m having more fun programming than I’ve had in a long time because I feel like I’m getting work done, because I’m getting work done.”。尽管 Go 语言尚处早期、未来还会继续演进,但其基石已然稳如磐石。这是一门开发舒适、极具生产力的高效系统级语言,能够让开发者在闪电般写出代码的同时保障运行时的坚固与安全。 伴随着本次发布,全套完整的技术资料与代码均已准备就绪。官方提供了详尽的语言规范(Specification)、新手入门教程、全面阐释 Go 语言惯用思维的《Effective Go》指南以及丰富的设计 FAQ。更重要的是,整个生态——无论是 Ken Thompson 的 6g/8g/5g 编译器套件与核心工具链,还是 Ian Lance Taylor 领衔的 Gccgo 项目——都以开源形式向全球公开发布。Rob Pike 热烈欢迎社区开发者参与进来,共同打磨底层工具与标准库,把这门语言推向全新的高度。 - **完备的文档体系**:上线了涵盖《Language Specification》、《Effective Go》、新手教程及各类深度 FAQ 的全套资料。 - **全线开源发布**:涵盖 6g/8g/5g 原生编译器家族与 Gccgo 编译器,所有代码及工具链向公众开放。 - **社区共建号召**:诚邀社区在标准库扩充、新工具开发以及语言演进讨论上展开深度协作。 ## Notable quotes > [05:21](https://www.youtube.com/watch?v=rKnDgT73v8s&t=321s) 以当今类型系统的运作方式来看,你可以在高生产力与类型安全之间二选一,但你根本无法两者兼得;这实在是一种遗憾,我们理应解决这个问题。 — Rob Pike > [01:56](https://www.youtube.com/watch?v=rKnDgT73v8s&t=116s) 我们想要快速编程,我们想要享受编程的乐趣,而要让编程变得有趣,你就必须找到加速开发、让事物更顺畅运转的方法——不仅是代码本身的运行速度,编写代码的过程本身也必须极为高效。 — Rob Pike > [25:09](https://www.youtube.com/watch?v=rKnDgT73v8s&t=1509s) 在 Go 中,决定一个标识符在包外部是否可见的唯一依据,就是它是否以大写 Unicode 字母开头。仅此而已。 — Rob Pike ## Mentioned Go, Rob Pike, Robert Griesemer, Ken Thompson, Ian Lance Taylor, Russ Cox, Austin Clement, Andy Chu, Joshua Bloch, Tony Hoare, Google, Sun Microsystems, Plan 9, GCC, Gccgo, SWIG, GDB, Godoc, Gofmt, Effective Java, Effective Go, C, C++, Java, Python, Ruby, JavaScript, Smalltalk ## Video description Google Tech Talks October 30, 2009 ABSTRACT Presented by Rob Pike What is Go? Go is a new experimental systems programming language intended to make software development fast. Our goal is that a major Google binary should be buildable in a few seconds on a single machine. The language is concurrent, garbage-collected, and requires explicit declaration of dependencies. Simple syntax and a clean type system support a number of programming styles. For more on Go including FAQs, source code, libraries, and tutorials, please see: http://golang.org [Source](https://www.youtube.com/watch?v=rKnDgT73v8s)

Source quotes

[5:21] Rob Pike: 以当今类型系统的运作方式来看,你可以在高生产力与类型安全之间二选一,但你根本无法两者兼得;这实在是一种遗憾,我们理应解决这个问题。 [1:56] Rob Pike: 我们想要快速编程,我们想要享受编程的乐趣,而要让编程变得有趣,你就必须找到加速开发、让事物更顺畅运转的方法——不仅是代码本身的运行速度,编写代码的过程本身也必须极为高效。 [25:09] Rob Pike: 在 Go 中,决定一个标识符在包外部是否可见的唯一依据,就是它是否以大写 Unicode 字母开头。仅此而已。

Source quotes

[10:17] Rob Pike: We just think that programming by constructing type hierarchies is not the way to go. [46:56] Rob Pike: We believe, with multi-core machines, you can actually do concurrent garbage collection with essentially zero latency and very little cost and overhead.

Original transcript

[0:23] >> PIKE: I’m going to talk about the Go Programming Language, which is going to be--we [0:28] hope going open source on November 10th, which is in just a couple of weeks. This talk is [0:34] a little bit before then, so some--there are some future tense for things like links and [0:39] so on, but everything should be as recorded by the time we actually go public with this [0:43] stuff. You can see we have a website we’re setting up, which will be the public domain, [0:49] public viewable domain for that. So, what is it? Well, I’m can talk about all these [0:54] pieces but it is new, an experimental, concurrent, garbage-collected, systems, language. Experimental [1:00] because we’re not--we don’t believe this is the answer to everything, but we’re playing [1:04] and we think it’s gotten to the point where it’s time we should probably tell the world [1:07] about it. It’s concurrent because it’s really important in the modern world. It’s [1:12] garbage-collected because we have an opinion on that stuff, which I’ll talk about it [1:14] quite a bit later. And it’s a systems language in the sense that we intended to be written, [1:18] to be used to write things like Web servers and other, you know, system aspects like that. [1:24] You have more control over some things and you have another languages like, say, Java [1:28] or Python, but it’s--it works like a systems language when you use it. On the other hand, [1:33] it's very good at things we find for things like front-ends or just general programming. [1:38] So, although, it’s designed as a systems language, it has much broader use than just [1:42] that. I can't give a talk without showing you, "Hello, world." There’s "Hello, world" [1:49] with the "world" being Japanese, which I want to attempt to pronounce. But you can see that [1:53] it looks vaguely C like although there’s some "weirdnesses" about it. I won’t talk [1:56] about what those "weirdnesses" are. Who’s involve? Well, I’m giving the talk but it’s [2:01] by no means my project. There’s a bunch of people involve and I want to make sure [2:05] everyone’s cleared. Some of the names are really important here. Robert Griesemer, Ken [2:08] Thompson, and I started talking about doing something to solve the problems we were seeing [2:13] with other environments. Some time around two years ago in late 2007, we played mostly [2:18] at the Wade Park for a few months to figure some things out. But by middle of 2008, we [2:23] had a toy compiler and things were starting to look interesting and we started working [2:27] on the stuff for real. And around that time, Ian Taylor joined and Russ Cox joined, and [2:33] the five of us did the most of the work, but we’ve had a lot of help from a lot of people [2:37] that this slide is too small to name. So I just make--I want to make it clear this is [2:40] a collaborative effort and the collaboration is growing. Why? Well, the reason is things [2:48] were just taking too long: too long to compile, too long to build, too long to think about. [2:52] We want a program fast. We want a program fun. And to make programming fun, you have [2:57] to find ways to speed things up and make things work better, not just the code itself but [3:01] the process of writing the code needs to be efficient. So what’s happened? Well, you [3:08] know, program used to be fun. What went wrong? Well, it’s interesting there's been really [3:12] been no new major systems language in at least 10 years or could be longer. But in that decade, [3:18] a lot has changed. The libraries have gotten bigger. Professional programming has, to a [3:23] larger extent, become a layering process, and so we layer more and more stuff on and [3:28] we also broaden out the base. The libraries got really big. There’s a lots of them. [3:32] They communicate with dependencies to one another and so the force has become a thicket, [3:37] and at some times it’s kind of hard to cut your way through it. Networking has pretty [3:41] much taken over the world, that's the way to think about computing. The old son thing [3:45] about the network is the computer is pretty much true today, but the languages don’t [3:49] really reflect that. There’s a lot of client/server kind of stuff especially in the systems base. [3:54] Languages don’t really help you there too directly. Of course, we’re talking about [3:59] massive compute clusters now and again, the languages are designed for single processors. [4:04] They don’t really work very well in that model. And on a related note, multi-core CPUs [4:08] are taking over, too. It’s getting harder to buy a high-end computer with only one processor. [4:13] It tends to be multi-core, sometimes multi-chip/multi-core. And again, the languages that we’re using [4:19] weren't really designed with that kind of stuff in mind. And it’s also gotten--I think [4:25] this is the thing that finally pushed us to try to do something. It’s just gotten to [4:29] slow to build software. The tools are slow. They have a hard problem to do, but they still--they’re [4:35] tend to be slow and they’re getting slower. The computers have speed up enormously but [4:39] the software development process, if anything, has gotten slower over the last ten years. [4:44] The dependencies are uncontrolled and the languages don’t help you control them, so [4:47] you tend to spend a lot of time maybe building things you don’t even need but you don’t [4:51] know it, can’t prove it. The machines have also stopped getting faster. They’ve gotten [4:56] more but they haven’t gotten faster. There are more processors, there are more of them, [5:01] but the actual clock speed has hardly changed in the last few years and Moore's law, to [5:06] some extent, is peering out and yet the software gets bigger and bigger and bigger. And so, [5:09] somehow, it feels like if we don’t do something, then software construction is just going to [5:15] become unbearably slower and we need to think about the process of making it fast again. [5:21] Robert Griesemer observes that a lot of the interesting work in languages in the last [5:25] few years is been because a lot of the people using these standards systems languages like [5:30] C, C++, even Java are finding the clamp--the type systems are very sort of clumsy or hard [5:35] to work with. And as they go to the dynamic languages: the Pythons, the Ruby’s, even [5:39] the JavaScript of the world, they have a lot more fun because the type systems don’t [5:43] get in the way so much at least as long as the program keeps running. And so, the challenge [5:48] here is to try to deal with this. You want typing. You want a good typing because it [5:53] makes programs robust, but it can be done badly. And we are given in some ways, there [5:58] is--if not done badly, it should at least be done the different way. Sometimes good [6:04] ideas, in principle, make bad practice. And a good example of that is the "const" keyword [6:09] in C and in C++, which was very well intentioned and seem to address the real need in making [6:14] program safer, but it tends to make programming awkward in a lot of ways, so this isn’t [6:19] worth the benefit you get back from using. Also, there is this notion of everything being [6:23] a type hierarchy in object-oriented programming. And yet, the types in large programs don’t [6:29] really naturally fall into hierarchies. We find the way to make them fit, but it’s [6:33] a bit of a struggle. In program, we spend so much time re-factoring a code, juggling [6:37] type trees around, and that has very little to do with the implementation and lot to do [6:41] with the way the language is forcing you to think. We’d like to be--we’d like to sort [6:45] of step back from that model. And in short, therefore, with the way type systems work [6:49] today, you can be productive or you can safe, but you can’t really be both. And that seems [6:54] like a shame; we should be able to fix that. So, why a new language? Why don't we just, [6:59] you know, fix what we've got? Well, some of the problems are just fundamental to the way [7:03] the languages are thinking, the way the type systems work, the way they’re compiled, [7:07] the way dependencies are managed. The languages themselves have these problems endemic in [7:11] them, and to fix them, you have to rethink the languages itself. For instance, the libraries [7:17] can’t help you, because adding anything to try to fix some of these problems is going [7:21] in the wrong direction. There’s too much already. We need to trim down, cut the fat, [7:25] and make things cleaner. Fixing it by adding software isn’t going to do it. You have [7:28] to sort of step aside and do something separate. And so, we decided: Robert, Ken, and I couple [7:34] of years ago that we need to start over. Just take a step away and think about the way programs [7:38] are written and constructed and try to make a language that makes that process a lot easier. [7:43] So, it’s a new language. What are the goals of this language? The sort of short version [7:49] of the story is we want the efficiency of a statically compiled language; that means [7:54] truly compiled language, but the ease of programming of a dynamic language. And I think we have [7:59] come pretty close to achieving that. You can decide for yourself as we go on. Safety is [8:02] critical. It’s critical to the language: the type-safe and then the memory-safe. It’s [8:07] important that a program not be able to derive a bad address and just use it, that a program [8:11] that compiles is type-safe and memory-safe. That’s a critical part of making robust [8:14] software and we just--that’s just fundamental. We want good support for concurrency. I’ll [8:19] talk a little bit about that. Communication, I’ll talk about that, too. Those are tools [8:24] that can help you build software that runs in the network or in multi-core CPUs. We want [8:30] to build--we’re going to garbage like to think, because a lot of the bookkeeping that [8:33] goes on inside modern programming, especially in C and C++, has to do with memory management. [8:38] And that can be totally automated. And we believe, "believe" is the word, that you can [8:44] do that efficiently and, essentially, latency-free that the garbage collection technology is [8:48] advance to the point where a garbage collected systems language actually makes a lot of sense, [8:52] and so we’d like to do that. And also, we’d like the compiler to just run fast. Everyone [8:57] knows this slide and there’s some truth to it. You know, you spent too much time waiting [9:02] for compilers. So, let me give you a compilation demo here. This is a directory of Go code. If you look [9:13] in here, there’s a 1,300 lines, so I make clean, and then I’ll just do a major. There, [9:20] I just built the last library. That’s compiled, efficient code, clean again. In case you wonder [9:27] if I can type, okay, time make. Okay. So that’s what two--well, pretty accurately, 200 milliseconds [9:33] real time to build the library. That feels about right. For the last years or so, those [9:36] who’s working with Go had been getting used to 100 millisecond builds on one machine. [9:41] That’s running on my laptop, by the way. That’s not some distributed thing. That’s [9:45] one processor, one build. And just to prove it a little better, let me make clean. This [9:50] is the complete Go source tree. All of the libraries written in Go, there’s something [9:55] of order. I think it’s about 120, 130,000 lines. So, included in here are regular expressions, [10:03] flags, concurrency primitives, the run-time, the garbage collector, pieces--oh it’s finished. [10:11] Okay. So that was a one processor. I could’ve paralyzed it a bit better. But that’s eight [10:17] seconds. That feels long to me now. That’s a long time because this is all the software [10:21] that’s there and, you know, it’s a long time to build--you can get a lot done with [10:26] that much time. Just to remind you, then we go back here, that’s what you want to see. [10:32] You want to have character return time for your compilations, and it can be done. So [10:38] let me get that set for later and we’ll go back to this. Okay. So, we think we’ve [10:46] got fast compilation and I can’t tell you how much--what a difference that makes to [10:50] your life. Sometimes hours and hours to go by when people finally showed up to my office [10:55] and say, "Hey, you didn’t answer my mail." I have to look in the mail and actually getting [10:58] work done. Okay. So to do this language, we had to have some principles on which is built. [11:05] And there are actually some fundamental principles in here that in some ways, I think, differ [11:09] from some of the other languages out there, so I'd like to go through them. One of them [11:12] is that we try as much as possible to keep the concepts orthogonal. For instance, in [11:17] the interface surface, I’ll talk about the concept of implementation versus interface [11:21] or completely orthogonal. They’re two separate things, they don’t interact and you don’t [11:25] define them together, you define them in separate objects and that makes a lot of stuff cleaner. [11:29] And in practice, this is because a few orthogonal features that cover the space work a lot better [11:34] in practice than a lot of overlapping lines. That’s a principle that’s a little vague [11:38] but I think, I think you know what I’m getting at. The grammar is very regular and very simple. [11:42] It’s actually conflict free and can be parsed without a symbol table, there’s no conflicts [11:46] in it. That makes it easier to write tools, like IDEs or editor plug-ins, and things like [11:51] that, debuggers because the parsing line is understanding and the program is actually [11:56] pretty straight forward. We try to reduce the typing. What I mean by that is that we [12:01] want the language to work things out. You tend to type too much when you’re programming [12:06] in modern object or in languages. You tend to write statements like that. That’s literal. [12:10] If you change "Foo" for a much longer word, that’s an example I found in some code I [12:15] want to end. And I just, you know, "Foo" should just go away. You shouldn’t have to type [12:20] all those "Foos," that’s crazy. So you want to get rid of the bookkeeping, you want the [12:24] type system to automatically works the "Foo," but you still want to be safe. You want it--you [12:27] want it to be type-safe, but well handled. And also, not only you don't want to reduce [12:33] typing, you want to reduce typing. You want to type to sort of melt away into the background. [12:41] You want it to be clear, you don’t want to type hierarchy, we don’t want to type [12:44] hierarchy, and then I'll show you some examples on why that actually helps. We just think [12:48] the program by constructing type hierarchies, is not the way to go. We have an alternate [12:53] world. However, Go is a profoundly object-oriented language. Arguably, more object-oriented language--more [12:57] object-oriented than, say, Java or C++, and I’ll show you why. So, the big picture, [13:05] there’s some fundamentals. We have a clean concise syntax. We have very lightweight type [13:08] system. We got rid of implicit conversions, because they’re all inconvenient. They tend [13:13] to cause a lot of confusion particularly in the old C rules for things like the, you know, [13:17] the usual conversions are actually not all that usual, and now these got right. So everything [13:22] is very clear and very clean on what’s going on in the page. Of course, when you know the [13:26] conversions, you tend to do a lot of casting or conversions by hand and that’s messy, [13:31] so we address that by changing constants pretty profoundly. I’ll talk about that in a little [13:35] bit. You'll never--you won’t see "0x80ULL" in a Go program. The ULL is completely pointless, [13:40] you can’t even say it, numbers are just numbers. And there’s a strict separation [13:44] between the interface and implementation. That’s part of keeping the concept orthogonal [13:48] and the type system clean. So there’s a runtime, there’s a garbage collection, there’s [13:52] a good support for strings, maps, associate raise, that kind of thing, and also the communication [13:57] channels, which we’ll talk about, and then there’s really strong support for concurrency. [14:01] It’s actually some of the best concurrency stuff, I think, that’s out there. It's really [14:05] good. And then, there’s a package model, which I won’t talk very much about because [14:09] it’s kind of takes a lot of explaining. But the notion here is that inside the packages, [14:14] everything is explicit. What you depend on is explicit. Compiler rejects your program [14:18] if you put in the dependency you don’t need. It’s all very clean and so, it makes tools [14:22] easy to understand dependencies and then it makes the builds guaranteed minimal. So, that [14:28] helps enormously. Regarding the dependencies, the linking going faster, the compilation [14:34] can go faster but--and there’s actually a notion in here that I think is important. [14:39] The compiler actually pulls the transitive dependency information up the tree as it’s [14:44] compiling, simply to reduce the number of files it has to look at, which in turn speeds [14:48] up the compilation process. So, if you have a program, A.go, that depends on B.go that [14:53] depends on C.go, which is pretty standard kind of thing. You--in the usual way, you [14:57] have to compile them in reverse order, because you got to build the dependent stuff before [15:00] the depending stuff. But the point is when you go to compile, A.go, it doesn’t need [15:05] to compile C. It doesn’t need to look at C.go at all. It doesn’t even need to look [15:10] at the object code for C, because everything that B need from C has already been pulled [15:15] up into B.go. And that doesn’t sound like a big deal, but when you’re at scale, it’s, [15:21] literally, exponentially faster at building software when you get in to this huge dependents [15:27] situation. For concurrency, we sort of have a model. I’ll touch on it today. There’s [15:32] a lot more stuff online about it. But we have a suggestion for how to write systems and [15:37] servers as concurrent garbage-collected processes, which we called, goroutines. We’ll have [15:42] good support from the language and the run-time. We change the name because they're slightly [15:46] different from what you're used to. If you use the words like thread or process or goroutine, [15:49] that’s not quite the same, so we have our own word, goroutine. The language takes care [15:53] of managing the goroutines and managing the memory that they use and the communication [15:57] between them. Stacks grow automatically. That’s taken care of by the language. You don't have [16:02] to declare how big the stack is going to be. If it needs a bigger stack, it gets the bigger [16:06] stack. If it needs a smaller stack, it uses less memory. And those stacks are multiple--I’m [16:11] sorry, the goroutines are then multiplexed on the threads. That’s all done automatically [16:14] for you and [INDISTINCT] transparently. And, one of the feature--one of the reasons for [16:19] needing garbage collection is that concurrent programming is very hard without garbage collection, [16:23] because as you hand items around between this concurrent things, who owns the memory? Whose [16:27] job is it to free it? And there’s a lot of bookkeeping involved that the--if the language [16:32] or the system doesn’t take care of that for you. So you need garbage collection to [16:36] make concurrency work well. On the other hand, you need--to get the garbage collection to [16:40] work well, you need a language that makes garbage collection a feasible thing to do, [16:42] which is another argument for doing a new language. So, I don’t have time to explain [16:48] all of Go to you. It’s a significant language, it’s--I’d say probably bigger than C but [16:53] not as big as C++. Enough time to go through all of it, but I want to give you the flavors. [16:57] I’m going to go through about a dozen slides that are--I hope sort of representative of [17:02] the kinds of things that go on, but I’m going to leave out quite a bit. Lexically, [17:07] it looks a little bit like C, actually a little more than like C than this slide, we let you [17:14] believe. There’s usually a keywords involved, introducing things, you see const and var. [17:19] Const, that means it’s a constant, not--it’s not the const keyword from C, it’s completely [17:22] different meaning. It just means, in the first line there, N is a constant 1024. And it’s [17:27] just a number, it’s not an int, it’s not a float, it’s not an unsigned int, it’s [17:31] just a number. And there’s a const string and you can see we support Unicode just fine. [17:35] In fact, the language requires that the input be UTF-8 encoded. And then, you can declare [17:40] variables with the "var" keyword in the usual way. You’ll notice--I’m quite use to it [17:45] now, but for C programmers this looks backwards, because in C, you put the float before the [17:49] X and the Y, but they're reversed and there’s a long explanation on the web about why we [17:53] did this. But the fundamental reason is it actually has been cleaner because, for instance, [17:59] in that declaration, X and Y both have type float. Whereas, if flip it around put the [18:03] keyword first, things get a little funnier. And you can see what the C version of that [18:08] will look like. You'd have to reproduce the star. So this is simplification you get. There’s [18:13] characters, Unicode supports, and stuff like that. Then we in this example, was not really [18:17] an example, just some lines of code, there’s a type struct, T, that's defined. It's got [18:22] a couple of fields within if they're both integers. Then we declare a variable, t0, [18:28] which is a new T. That should be pretty familiar what that means. But there’s too much typing [18:32] going on there. So the next line there, T1 := new(T), that’s an old notation from some [18:38] languages that I work with before. What that call and equals means is declaring the initialized. [18:44] So that--those two lines are equivalent. Var t0 *T = new(T), it has too many Ts in it. [18:50] We can get rid one of them by just having the type of t1 we derive from the expression [18:54] that’s used to declare and initialize it. And you’ll see that used a lot in Go code. [18:59] The var doesn’t show up nearly as much as the colon equals notation. Their control structure [19:04] is they're actually quite interesting control structures--just the first blast through, [19:09] pretty the same as C. They’re actually quite richer, but the basics are the same. The main [19:13] difference is syntactic. The braces are required on the blocks and there’s no parenthesis [19:18] on, for instance, the conditional expression inside an "if" statement. That's just to clean [19:23] up the grammar. So here’s a real program. It’s not "hello, world," it's echo this [19:28] time, but it’s got all the parts. The first time the code says, "package main," that has [19:32] some meaning in the language about me get muted. We're thinking about simplifying a [19:36] few things but at least for the moment it's "package main." then you can see there's a [19:39] function called "main" is declared down below. And the program begins execution by starting [19:44] "main.main" initialization inside. There’s a declaration of the flag from the flag’s [19:50] package, so that makes minus N, turn off new lines from echo. It’s not strictly a UNIX [19:55] echo because it’s got real flag parsing, but it’s essentially the same. Those import [19:59] statements say, "We’re going to use package OS," which does some stuff for us and we’re [20:02] going to use the flag package, obviously. Now, the way the initialization stuff works [20:06] is that in-flag variable will be initialized before "main" starts executing. All the global [20:11] variables get initialized by--and they can be initialized by arbitrary complex stuff [20:16] before "main" starts. So then in "main," we do the flag parts that get the command line [20:21] pulled apart. And then we just loop over the arguments which are now the [INDISTINCT] the [20:25] flag package, and it’s the obvious loop accumulating. You can see += on the string [20:30] there to append to the string, and stuff like that. And then at the bottom, the flags are [20:36] actually pointers in Go, the flag package makes them pointers. So the *nFlag, which [20:41] is a Boolean, is false. Then, you add a new line to the end of it. And then finally, you [20:46] do a method called "os.Stdout" to get the open-up. So, although, pretty much every line [20:52] in here is different from what you’ve seen before, it should be perfect and clear what’s [20:57] going on. I don’t think you’ll find that a mysterious program. So let’s talk about [21:01] constants. I said they are a little different. Constants in Go are what we like to think [21:07] of as sort of type-less. They have no type. They have no size. They’re just numbers [21:11] or strings. You can have constants and strings in Booleans, but numbers are the interesting [21:14] case. So in this little snippet here, is just some constants being used. We start by defining [21:19] a type TZ for timeZone, which is an integer. And then, we declare some constants, UTC and [21:27] EST. We declare them to have type TZ and assigned to those initial values. You can clear what’s [21:33] going on there. The parenthesis around the const is distributing the const declaration [21:38] introducing keyword across the elements inside that declaration. So that’s the same as [21:44] saying, "Two const declarations with the two variables in them." You’ll see later on [21:50] the next page here that actually matters that you can group them like this, because there’s [21:54] a thing called iota, which is an enumerating constant. It literally--for each const declaration, [21:59] counts which element you’re on. At every semicolon, it increments. Starts at zero on [22:05] the next line, it's one, the next line is two. And so, here you can see, we’re declaring [22:09] bit0 and mask0 to be uint32s, and the first one is set to one shifted up iota. Iota starts [22:15] at zero in every const. So that’s one shifted up zero and one shift zero minus one. So that’s [22:20] a bit and a mask. And then the next line does it again, but now iota has gone up to one. [22:24] So we get the next guy in the sequence. You can see how to use that to build enumerated [22:27] constants but this is a slightly richer example. And again, to reduce typing, if you want, [22:34] you can just say, declaration, like the third line there with the semicolon and what that [22:39] says is, "Just keep doing what I did." Take the declaration initializer from the previous [22:43] line then pretend that you just type that here. And so, if you want to declare a new [22:47] ready constant, you can say, const open parenthesis inum0 equals iota semicolon and then you just [22:53] go inum1 semicolon, inum2 semicolon and all the way down the page. And just have them [22:56] declare sequential numbers for their constants. One very important properties of these numeric [23:04] constants in Go, is it there are arbitrary precision. So, here’s a const, the logarithm [23:10] of 2, you can see that’s got like 40 or 50 decimal places in it, which is more than [23:16] even a float 64 can represent. But that’s a valid declaration, because they’re just [23:23] numbers. And then the next line, we can actually compute log2, log base 2 of E as one over [23:29] log of 2, and that’s a precise division because it’s happening not in floating point [23:35] space but in arbitrary number precision space. So that’s a very accurate reciprocal. And [23:40] that kind of stuff is nice too, because you don’t see cache and conversions and worries [23:43] about overflows and underflows. Constants just take the job for you. And then we have [23:49] values, obviously. There’s an array like type called the slice. Those are arrays but [23:54] they’re kind of peculiar. You don’t use them much. Instead, use a reference type called [23:58] the slice. So we can there, is declared to be a slice of strings with Saturday and Sunday [24:02] as strings declared inside it. And then we take the timeZones and make declare timeZones [24:09] map to be a map from a string to TZ or timeZone. And you can see how that works. It’s pretty [24:14] straightforward. There’s a function declaration to do an "add." You can see the function syntax [24:20] there. There’s actually a lot of interesting things in how functions deal with return values, [24:23] which I’m not going to talk about today, but you should check that out. And then, there’s [24:28] a declaration of function type which takes two integers return int, so "add" is of type [24:32] Op. And then, here we declare an RPC, which is a structure. I showed that before but it's [24:38] a [INDISTINCT] example. It’s got A and B integers. It’s got an operator, and it’s [24:42] got a result which is a pointer to an integer. And all that should be fairly straightforward [24:46] even if the declarations look backwards to a C programmer. And then the last line there, [24:51] we declare using a structure literal, an instance of an RPC and again, it’s very simple. You [24:56] just say type name, open brace, list the elements of the structure, closing brace and then you [25:00] got a value, you can assign that to or in this case, initialize a new variable to that [25:09] value. So, I said [INDISTINCT] language. We haven’t seen much yet, there was actually [25:11] one and the other is a method in the echo example where we wrote the os.Stdout. But [25:17] let’s talk about methods, because they’re kind of different. So here’s a type called [25:21] point that has X and Y. Now, those are upper case and the reason is, in Go, the complete [25:28] decision about whether an identifier is visible outside the package or not, is whether or [25:32] not it starts with an uppercase Unicode letter. That’s it. So if a variable or a type or [25:38] whatever declared at the top level is uppercase, is visible to the clients of the package. [25:41] If it’s lower case, it’s not. Inside the global struct, if a method or field is uppercase, [25:47] it’s visible to the clients of the package, otherwise, it’s not. That’s it. It’s [25:51] really simple. So, there’s that type. Now, we’re going to define a couple of methods [25:56] on it. The declaration syntax for a method, you’ll notice it’s not declared inside [26:01] the struct, and the reason, we'll see in a minute, is a method and structs are independent [26:07] ideas. You can have methods on things other than structs, so it wouldn’t make sense [26:12] if you put them inside the struct declaration. What you do is you say, "Here’s a func." [26:16] And then before the name of the method, you write a parenthesis, kind of like its own [26:20] little one element parameter list, receive a declaration and receiver is a short hand [26:25] for the thing that’s receiving the method when it’s called. So this says, that scale [26:29] is a method of type star point and it takes a float argument and then the implementation [26:34] is inside and it’s just multiplies up the point by the usual, by the scale factor. You’ll [26:39] notice that the receiver is explicit. There’s no this, automatic or anything like that. [26:43] You have to actually say, declare receiver and use it if you need to access the fields [26:47] of the struct inside the method. And then there’s Abs, you can imagine what that is. [26:51] There’s a square root, which is a function inside the math package computing the hypotenuse [26:56] of the point of the [INDISTINCT] of the vector. And then here’s--we declare an X. Now, this [27:04] time you notice there’s an ampersand before the point, word there. Point of three, four [27:08] is a value. Ampersand of point of three, four allocates. It’s like a constructor like [27:14] thing to create a pointer to a new instance of that. And that--every time you execute [27:18] that, you get a new instance of point that initializes the three, four. Then, you can [27:23] call x.Scale, which is assumed you'd expect to be able to do. Okay. So that’s pretty [27:26] simple. The syntax is slightly different, but the ideas are pretty simple. Now, it gets [27:32] more interesting because methods on any type you define. And here’s a full example, this [27:37] is another complete program that actually does something a little different. So again, [27:41] we start with package main and now we import fmt, which is the formative printing package. [27:46] It has printf and stuff like that in it. And then, here’s our type TZ int coming back. [27:51] And now, we declare few more of these constants, we have an Hour which is of type TZ, which [27:55] is 60 minutes times 60 seconds. And then we declared, just to make it short, two timeZones: [28:01] Zero hours and minus five hours for UTZ and EST, okay? Now, we declare a map from the [28:07] names to the values of those timeZones. That should be familiar in the timeZones. We actually [28:12] have that exact the same example before. And now, we put a method on a timeZone. Now, timeZone [28:17] is just an int, but I’m going to put a method on it. So, I declare a function called capital [28:22] string that returns a string to receive tz, lower case, plus a type TZ, upper case. Notice [28:28] that it’s not a pointer, that’s really important. It doesn’t have to be a pointer. [28:31] And then, using a variant of the for-loop that's arranged over the elements of a collection, [28:37] I say name comma zone is range of timeZones. What I’m looking for is the element of the [28:43] timeZone map whose value is equal to the value of TZ. That’s the name of the guy I want. [28:48] So it’s like reversing the map. So I just loop over all the elements looking for the [28:52] one whose timeZone is equal to the zone of that element of the map and if that’s true [28:57] I return the name. And you can see there’s actually quite a bit going on in there. There’s [29:00] the range which is giving you the iterator, there’s a cone [PH] equals to declare loop [29:03] variables that work over the elements and thing inside in and all not. But of course [29:07] you might get a value of the--of TZ integer that’s not an exact timeZone number. And [29:13] so, if that happens, we finish the loop and we return fmt.Sprintf of blah-blah. An Sprintf [29:21] is analogous to the standard C as printf, but actually returns a new string. It doesn’t [29:25] put into a buffer. So, that’s an implementation of a method that let’s you print timeZones [29:29] in interesting ways. So we can use it in our main function by saying, if we just say, fmt.Println [29:37] of EST, Println is a variant of print that doesn’t require a format. It’ll work. [29:42] It’ll actually printout EST by catching it in the inner loop of the string method. [29:47] And notice that Println(EST) knows under the covers by mechanisms that are described the [29:53] length and the website, how that the timeZone type has a string method and that’s how [30:00] you should pretty print it if you get a value of that type. So, that’s all you need to [30:03] do to make it print itself. And then, there’s another example, we can print Println five [30:08] times hour over two and it comes out as plus two colon thirty, because the printf at the [30:12] bottom works. So this is a fairly contrive example, but you can see to there’s a [INDISTINCT] [30:16] going on here. Having methods on general types, as any small talk programmer will know, a [30:20] very convenient thing to do. But by separating them methods away from structural classes, [30:25] we actually introduce some new ideas that you can play with and that’s pretty fun. [30:29] Okay. So now, we come to something that I will not be able to describe in full in the [30:35] time available, but they are pretty interesting and they are arguably the most noble about [30:39] Go, and that is the concept of an Interface. An Interface is an, what’s the word, a formalization [30:48] of the concept of a set of methods. So, we can declare a method, sorry, an interface [30:54] type Magnitude and say that anything that implements the Magnitude interface is something [30:59] that implements the Abs method that returns a float. And of course there could be lots [31:03] of other things in there, but we’ll assume for the moment there’s just one. So, remember [31:07] we declared an Abs method for the type star point. So, we can--we had an x from the previous [31:13] slide, we go back there, there, that x address the point three, four, that’s the same x. [31:18] We can use it here. We declare a variable with the interface type magnitude and assign [31:23] it to assign to it the value x. And the reason we can do that is x is an implementation of [31:29] that interface, and so that’s a valid assignment, okay? And having done that, had I typed var [31:35] m magnitude instead of m magnitude. No, I got it right. Sorry, sorry, I confused myself. [31:42] Var m magnitude is a variable, we assign to x and then we declare a new variable mag that’s [31:46] m.Abs, so it’s just the, the vector length of the point x. But now we declare another [31:52] type point three which has three coordinates to find the Abs method for that, same idea. [31:57] And now, we can assign m equal to a point three and that value still is an interface [32:05] var, but inside it is an object that has the implementation of the Abs method. And so we [32:10] can call the Abs method on that and add that to the variable that we’ve got. And then [32:14] we can do it again, and this time just to be fun, we declare a polar object, which is [32:19] R and theta and, of course, that’s a capital theta because a public field. And then, we [32:25] declare p Polar. Notice there is not a pointer this time. Why make it a pointer, it doesn’t [32:30] matter. So we just make it a value polar and it returns the radius of the thing which is [32:35] the definition of the magnitude. And then we design a value that type to m, and now [32:40] we can add another thing at the magnitude. So the interface variable is letting you define, [32:45] "I can work with anything that implements these methods and anything at all will do." [32:50] Here, I use three different representations of a coordinate, but they’re quite different [32:54] in character. Under the covers, it’s all very simple. And the key point is that nowhere [32:59] does point three or polar have to tell you that it implements the magnitude interface. [33:06] It implemented the fact, though, because it satisfies the methods that are defined by [33:10] the interface. And there’s way more going on there than I have time to talk about, but [33:14] I have to talk about one and very important concept behind them, which is the generality [33:18] that they give you, because any method, it could be part of an interface, an interface [33:24] can be any method. It's all sort of, it’s not one to one, it’s many to many. It’s [33:28] all mixed up. And so a struct of a given type may implement multiple interfaces depending [33:34] on what interface you are using. And that gives you the opportunity to define very simple [33:40] interfaces that define very general properties. And a really good example of this is in the [33:45] IO package and it is called the Writer interface. And a Writer is anything that implements a [33:50] standard write call and that’s what we define a standard write call to be. The, you know, [33:54] there’s a slice of bytes that can--that is the data you’re going to write. It has [33:58] a length inclusively inside it, so we don’t need a byte count. And it returns, there’s [34:03] that function that [INDISTINCT] before returns a pair, a count and an error, but don’t [34:07] worry about that. Just think about the writer. The writer said anything that implements this [34:11] standard form of the write call can be used to write it to. It makes sense. This is just--it's [34:16] almost a tautology. But now, anything you implement write for can be used by anybody [34:22] that only needs something that implements write. It doesn’t matter where the properties [34:26] it has. So for instance, fmt.Printf which is just what it sounds, fmt.Fprintf, pardon [34:32] me, which is product much what it sounds like, doesn’t take a file as the first argument. [34:37] It takes an io.Writer as its first argument. And a result, you can call fmt.Fprintf on [34:43] a network connection, a pipe, a file descriptor, a buffer, all kinds of other things, anything [34:48] that implements write, a cryptography pipeline. And then, the way buffer io is done in Go [34:54] is that there is no such thing as buffer io, there’s just buffering. And what happens [34:58] is if you want to create a buffered version of something, you give it a writer which is [35:02] anything that implements write into the covers and it gives you back a buffered version of [35:06] that same thing that you can use where you used it before. So here’s an example that [35:10] puts that together, again, a complete program. Starting with package main, we import bufio, [35:15] fmt and os again. And now, we wrote the os.Stdout before, but now were going to use Fprintf [35:21] to write to it to make it explicit. So, we say, fmt.Fprnintf os.Stdout hello. Then, we [35:28] call bufio.NewWriter. We pass it os.Stdout but NewWriter only--all it cares about os.Stdout [35:34] is that it implements the right call. And the NewWriter function inside bufio returns [35:41] something else that implements the writer interface, and so I can write to it. So, I [35:45] can call fmt.Fprintf of buf and now I have a buffered write. So I better flush it when [35:49] I’m done. But you see the Fprintf use two different types in there for its output and [35:54] it worked, because underneath--if you look at the Fprintf declaration, all it cares about [35:59] is that the thing that is being passed in implements the write call, which of course [36:04] they do and it’s type checks, so you know they do. So, there's a lot more of that stuff [36:09] to talk about to, but we’re trying to skip through the highlights. Communication channels [36:15] are little as radical than Interfaces because they’re based on, worked on in some early [36:20] language, going back to a Hoare’s CSP. But unlike Hoare’s original CSP, the communication [36:26] is done through first-class values called Channels. His second round of it done about [36:30] 10 years later had these, but we’d played with them linguistically by then. And, the [36:35] ideas, you make a Channel, it has type string. That means you can send and receive strings [36:40] on that channel. Just like a pipe that you can send type values through. And so, that [36:44] notation with the left facing out is the communication operator when you used infix it means send. [36:49] So, that sends the string, hello, down the channel. And somewhere else, some other time, [36:54] some other Goroutine can do read from channel c by doing a prefix operator on c of that [37:00] communications and get out the greetings, so it will see greeting as assigned to hello. [37:05] And this is haven’t done giving Goroutine because the channel is synchronized by default, [37:09] hello. You can control that when you allocate them. And one of the cool things about having [37:14] them is you can make them--channels can actually be first class size. That means you can pass [37:19] a channel on a channel. And I’ll show you in a minute to actually give you some interesting [37:22] capability. So, if you have someone else wants to have this channel that you can send greetings [37:28] on, you can pass it to him over channel then he can send you greetings, all right? So, [37:34] to do that, though, we have to talk a little bit about Goroutines. So, I’ve mentioned [37:37] some calculations, it just takes far too long, right? Who wants to wait around for that? [37:40] You're going to block forever waiting for the calculation, but maybe you don’t want [37:45] to. What you can do instead is create a channel to get the answer back on. And then declare [37:50] a function, wrapper, that takes the value of the parameter for the calculation, and [37:56] a channel invokes the calculation and then when it is done, sends the result back. And [38:01] I broke it in two lines just to make it easier to read. You'd probably just write C gets [38:05] long calculation of A in a real code, okay? And then, to run the thing, there’s a Go [38:10] keyword. Go is a keyword. Go wrapper of 17c says, "Take the function call, 17 comma C, [38:16] and run in the background and let me keep going." Okay? And then, eventually, you want [38:20] to hear what happened, so once you’re finished doing your business, you come back and say, [38:23] "Okay, I’m ready for the answer now." And you say, "Now, I want to receive from C and [38:27] get the result." So I’ve taken this calculation and I’ve solved it off over here and then [38:31] it slows back to me by doing a receive on the channel down underneath. That should be [38:36] fairly easy to follow. This might not be so easy, but we’ll try. This is a multiplexed [38:43] server. At least, the server side of it, I’ll show you the client on the next slide. And [38:47] the idea here, remember I said you can pass a channel to a channel. What we have is a [38:52] request which is a request for the server to implement. That's trivial. It just takes [38:55] a pair of integers in the channel in which it replies. Very much like what we did before [38:58] on previous slides, generalized somewhat. We defined a binary operator type and we defined [39:03] a function run that takes a binary operator and a request and sends on the reply channel [39:10] inside the request the value of running the operator on the arguments inside the request. [39:18] So that’s just turning to send the operations in the sending of the thing from the previous [39:22] slide into a function, not very exciting yet. But then, we defined a server function that [39:27] takes an operator that is going to serve in a channel of request. And it’s just sits [39:32] in a loop reading request from the service channel and then saying run of Op request, [39:38] but it puts a Go keyword at the beginning. So, it runs all these requests in parallel [39:42] in the background. The server is never blocked. It’s always ready to receive another call [39:46] no matter how much is going on. Obviously, there's issues about re-limiting and stuff [39:49] like that, but let's not worry about those. The key point is that that’s, basically, [39:53] a server loop. And then, to make it easy to use for the client we defined a public function [39:59] or an exported function by starting with a capital letter, starts server. And it says, [40:05] "Take an operator and start a server for that operator and return the channel on which those [40:12] requests can be sent. So, what we do is we allocate the channel, a channel start a request, [40:18] we start the server process which in turn is going to start the little implementers [40:22] of the operator as they come in. And then, we return the channel as the return value [40:27] from start server. So, you call a function you get a channel back, now you got a thing [40:30] you can talk to and that is the fundamental idea on how you use this stuff to build services, [40:36] okay? So on the client side, it looks like this. You say, server, start server, here's [40:42] a function. Now, notice that I’m just writing a function in line there. Go has four closure, [40:45] it seems writing down its expressions. And so, now I’ve got a server running that’s [40:50] going to add numbers for me. And I’ve got a channel for that server in the variable [40:56] server called Lowercase Server. I allocated a couple of request in the obvious way and [40:59] inside the request, I allocate new channels because I want to do--respond to me on separate [41:05] channels for whatever reason, okay? And then, I can send the request to the server, it doesn’t [41:09] matter what order I send them. It’s totally not blocking. I'll just send them anywhere [41:12] I want. And now, I can print them out again, print the results as they come back. And I [41:16] don’t have to worry about the order because, again, it’s totally parallel. So, in fact, [41:18] I send one first but ask her two first. And if two takes forever, I’ll still hang around [41:23] there waiting for the response to come back and once it finally does then I’ll print [41:27] the value on one even if one is ready sooner. So, this one is very rushed through hear, [41:32] so I apologize if I left you a little be fuddled, but there’s examples like this in the tutorial [41:37] and some other documents. It should make a little clear what’s going on if that looses [41:42] you. Now, if we go back to the server here, remember I said, it has a rate limiting issue [41:50] and also the problem is it runs forever. You want to go and shut it down. One way to do [41:54] that is to use a control structure in Go called the Select, which is quite a rich control [42:00] structure. But the basic idea is given a set of communications on channels. Select let’s [42:06] you choose, let's you to actually wait for any one of them to be become ready and once [42:11] it does become ready, let that single thing communicate. So, it’s like a switch statement [42:15] for communication. So, if we replace the inner loop of server, which was just, remember, [42:20] get a request and then run Go on it. With an operator that has a select statement inside [42:26] it and the second channel called Quit, that’s passed into the server when we create it. [42:31] Then, we can just run this normally. It will just do what it was doing before, but as soon [42:35] as you want it to go away, we just send a signal by sending a Boolean of any value, [42:39] it doesn’t matter what, on the Quit channel and that branch of the select will fire and [42:44] this function will return, which is equivalent to exiting the Go routine. So, that’s again [42:49] pretty quick and I apologize for that. But I want to make sure we hit some of those points. [42:54] And in fact, there’s a lot more to talk about. I haven’t talk much about package [42:58] construction. Initialization is actually quite interesting in Go. It's much--it's sort of [43:02] hard to see, but it does a lot for you. It can help you set up things like RPC servers [43:06] with almost no code and stuff like that. There’s fall reflection, you can reflect from there, [43:11] you can reflect on functions, channels, maps, all kinds of stuff. It’s got interesting [43:15] some dynamic typing going on if you wanted to, but you don’t have to use it. There’s [43:18] a thing called Embedding that is a little bit like an inheritance, but somewhat more [43:24] simple and yet general. There’s iterators that come out of the way. The range thing [43:29] works on fours in our channel’s functions, things like that. And there’s some interesting [43:32] testing software, but I don’t have time to talk about any of these right here, but [43:35] I just want to make it clear there’s a lot more of the language than what I have time [43:39] to show you. So let me close this section with another interesting example. This is [43:44] a real communications example. Remember, we started this language because we want to build [43:50] servers. And one of the problems with building servers in the modern world is there’s this [43:54] model of a thread per request, which is quite labor intensive and resource intensive to [44:01] run and quite difficult to get right in more languages. And we want to make it easy to [44:06] think about having tens of thousands or even hundreds of thousands of threads—-whatever [44:10] they are-—working on your behalf. And to prove that we got some way towards that. This [44:17] is an example that will create a hundred thousand goroutines and then they can do something. [44:21] And what it does, you probably won’t be able to decode this in real time, but what [44:25] it does is it has a little function F there that all it does is read a value from a channel, [44:31] add one to it and send it out again. So, there left gets one plus, get from right. It’s [44:35] that thing inside F. It just passes its token along but adds one to it, okay? And inside [44:41] main, we declare the left most piece—-to go your direction—-and then a couple of [44:48] variables. And then for the number of goroutines you want to start, we sort of thread a new [44:52] piece on and walk away along, but we remember where we started. So, we stitch together a [44:57] bunch of goroutines like this, each of which is passing a channel value along to the next [45:02] one. And so, we end up constructing, in this case, a hundred thousand goroutines off turning [45:06] together like pearls on a string. And each end is waiting for the guy upstream to give [45:11] him a value. So, then right at the bottom there after the loop--once we built this thing—-we [45:15] say, drop a value into the right hand side and it goes pfft and pops out the left. And [45:21] the value that comes out is of course the number of goroutines in the chain because [45:25] we’ve added one for each step, okay? So, you can examine that later if it confuses [45:31] you, but let me show it to you actually running. Okay. So--oh dear, I think it was the wrong [45:40] file—-okay, sorry it took so long. And now, I’ll run it—-now remember this is a little, [45:49] a little mac air very fast. That's a hundred thousand threads of control--I won’t say [45:55] threads because that’s not how it is done—-but just incase you’re wondering, it takes about [46:01] 1.5 seconds to manage a hundred thousand goroutines. And it’s real--it’s honest to God management [46:07] too. It’s [INDISTINCT], it’s stitch together communication. Doing the communication and [46:11] it’s tearing them down. This hasn't created an instant kill, this is a legal honest build, [46:16] use them, tear them down. It’s kind of a minimum thing you can do that, but still, [46:20] hundred thousand is an interesting number and we could go more but it start to use up [46:25] memory. So, let’s just prove that there’s—-anyone knew that. Here we go. So, that’s concurrency [46:40] showing that you are going to at least, conceptually, have many, many things. Now, the goroutine [46:44] implementation has to worry about real threading and real stuff like that when you’re doing [46:48] I/O operations that could block but that is taken care off. So, you are left with a simple [46:52] mind example, it’s indicative of something that really works. So, let me talk a little [46:56] bit about the status. It’s actually quite a bit to say, we have two complete implementations [47:03] in two different compiler technologies. Ken Thompson wrote a suite loosely based on some [47:09] of the Plan 9 tools but it’s from scratch, and using the peculiar notation, 6g is the [47:16] AMD—-it’s a 64 bit X86 compiler. There’s also a 3D 61 and an ARM which is five for [47:23] reasons. Don’t even ask. But anyway, the point is there is a compiler. That’s the [47:28] one I’ve been using today. Some are more experimental than the other compiler but as [47:33] you’ve seen it, it generates code quickly. The code it generates is pretty good, it’s [47:37] not as good as what comes out of GCC, but it’s better than you might think for a compiler [47:41] that goes that fast. But, in order to do that, it got it’s own rules and so, the output [47:46] is not linkable directly with GCC. But there is a foreign function interface support that [47:51] is coming along pretty well. We can call C-code from Go with this compiler just fine and once [47:57] we get Swig work and we’re able to call C++ code. And then Ian Taylor, wrote a GCC [48:02] front end for Go which is a complete implementation--I should say, by the way, that Ken’s compiler [48:08] is written in C for bootstrapping reasons, although, honestly it would be a great language [48:12] to write a compiler but it’s written in C for now. And Ian Taylor thing is a C++ front [48:17] end for the GCC backend. It generates excellent code with GCC quality but it’s not as fast [48:24] it’s about something like four or five times slower. But the real advantage of compiling [48:28] GO isn’t the compiler speed, it’s the compilation speed. The whole picture is where [48:32] the speed really comes from. And both these compilers supports 32 and 64 bit x86 and with [48:38] ARM compiler that Kyle has been working on that’s almost ready—-I think it’s up [48:43] to 97% to test suite compliance now. So, we are very close. We have to have it ready in [48:48] time for the open source release. And for performance of the generated code, it varies [48:52] obviously because it’s different but if you avoid some of the libraries for which [48:56] have weak implementations. Typical inter loop kind of performance or standard programming [49:01] gets you typically to 10 to 20% range with the GCC tending to be more like—-by which [49:07] I mean 10 percent slower than C and Ken’s compiler is typically like 20% slower. So, [49:14] 1.1x to 1.2x, but this is for a type save, statistically compiled garbage collection [49:19] language. It’s pretty nice. And I’m happy to give up 10 percent even with words lost [49:28] flowing down, that’s still not very much clock time on the calendar. So, there’s [49:34] a Run-time that handles all the memory allocation, garbage collection, stuff like that, the stack [49:37] handling stuff which is pretty special but very important. The goroutines support channels, [49:43] slices, map, reflection; all of that stuff is built into a real run-time. So, even though [49:46] it’s a system language it has a really powerful run-time system including dynamic type reflection. [49:52] And it’s pretty solid. It’s improving, it got ways to go especially in memory allocation [49:56] and some of the scheduling stuff, but that’s yet to come, but we’re working on it. 6g [50:01] has a very good goroutines support and much of them on the threads well and implements [50:05] what we call segmented stacks, which is how we keep the footprints small but let them [50:08] grow as they need to. GCC goes a little behind on that because Ian has been working on a [50:13] few other things lately. But we hope by the end of the year to have all that stuff working [50:19] in GCC Go and they’ll basically be at the same stage at that point. But even so, GCC [50:24] Go can compile all the code and run it now. The garbage collector, 6g has a simple but [50:32] effective mark-and-sweep collector. And this sounds simple, but work is under the way to [50:37] do a much better job. We believe, with multi-card machines, you can actually do concurrent garbage [50:42] collection with essentially zero latency and very little cost and overhead. And IBM has [50:47] this garbage collector technology that we think is pretty exciting. We think building [50:51] on that stuff, we can actually make our real goals which are to avoid a lot of the pitfalls [50:57] that garbage collector tend to have. And we’ve done work in other languages before that indicate [51:02] this really can be done and now that we have multi-card machines we believe that we can [51:06] really solve it. GCC Go at the moment has no collector, but, as I said, we're working [51:10] on a common run-time, and I hope by the time this collector is done is design to run with [51:15] either compiler. It’s part of the general run-time so both compilers will have real [51:20] garbage collectors inside of them. There’s lot of libraries, there’s tons more to do, [51:26] but we have a pretty good start. We’ve got obviously, OS and I/O stuff. I showed you [51:32] some of that. It’s got a nice math, a simple math package. It got strings, good support [51:35] for Unicode, rudimentary but functional regular expression implementation. It’s got run-time [51:41] reflection, command-line flags and logging, which are very nice to use. It's got full [51:45] hashes and crypto and all that kind of stuff. It's got a really good testing tool and a [51:48] library to support it, standard networking libraries just like you’d expect, including [51:53] a native RPC implementations. It’s kind of pretty. There’s a really interesting [51:58] template library based on some work that Andy Chu did that let’s you write HTML or, in [52:03] fact, anything at all using this to generate really simple data driven page generation. [52:10] It's really quite nice. Andy gets most of the credit for that. There’s a lot more, [52:14] but you can see there’s actually quite a bit in place already. So, one of the most [52:18] interesting programs written in Go is a pair of things that are related--they share a lot [52:22] of code--called Godoc and Gofmt. Godoc is analogous to Javadoc. It serves documents [52:29] to request to look up, you know, what this package does or what that function does. And [52:35] there’s a set of links listed there. Golang.org is a top-level guide that’s running Godoc. [52:40] It serves the landing page. And underneath that, you can find all the other documents [52:45] specifications, editorials, and FAQs, and stuff like that. But if you dig down into [52:49] the package subdirectory, that’s really interesting, automatically generate the package [52:53] documentation is especially quite rich. And then under the source directory, the source [52:57] gets served, but, of course, you can get out of this code repository, too. The difference [53:02] is that the source is processed by the coding Gofmt. The Gofmt is a pretty printer and all [53:08] of the coding repository has been formatted by Gofmt. So, rather than set a bunch of style [53:14] rules, we have a program that says this is what a code looks like, and you just run through [53:18] Gofmt when you check it in and that’s, just settles all those arguments. If you want debate, [53:23] if you wanted debate, we got to debate by changing Gofmt. You can’t debate by changing [53:26] a document, and I think it’s actually, it does an amazingly good job. Actually, Robert [53:31] [INDISTINCT] a good job. We have a debugger. It’s not quite ready, but it’s pretty [53:36] close. I hope by the end of the year we'll something functional but we’ll be happy [53:39] to show. But it's coming along and it will work with either environment but at the moment [53:45] its working in the 6G world primarily. Gccgo users can of course use gdb. Gdb doesn’t [53:52] understand the symbols tables. They're coming out of Ken's compiler, so it doesn’t work [53:55] very well with that. But with Gccgo compilation, you can use gdb, but it thinks that the C [54:00] program is debugging and there’s some weirdness in the symbol table. And also it doesn’t [54:03] know anything about the run time, which is a critical thing when you’re debugging on [54:06] an environment like this. So, we really got to get our debug up to scratch. We had a summary [54:10] in turn. Austin Clement was here working on that. It's pretty close, but not quite ready [54:15] for real use. One question everybody wants to know is what about generics. And the answer [54:19] is Go doesn’t have them yet. We don’t understand exactly what they do in Go’s [54:27] world. They’re quite subtle. The easy examples are easy to write down. It's obvious what [54:32] the easy examples do, but the complete, correct definition is going to be quite complicated. [54:37] We want to make sure we get it right. In some ways, Go makes it simpler because there’s [54:41] no type inheritance, and that eliminates one branch of complexity in generics. But on the [54:45] other hand, you can put methods on things that are values of arbitrary size, so, that’s [54:50] a consequence, and the other directions are complicates and simplifies at the same time. [54:54] And we just don’t think we understand it well enough to do them yet. Also, in the current [55:00] world, although that would be definitely useful and could solve a lot of problems, the map [55:05] and slice implementations and the interfaces themselves actually cover a lot of the sort [55:09] of obvious examples for generics, which is not to say they wouldn’t cover a lot of [55:12] others if they came along, but the need isn’t quite as acute as it is in the language. It [55:16] doesn’t have those features. You can in fact build collections fairly nicely using [55:22] what’s called the MT interface, which is just interface open close. That’s an interface [55:26] that implements no methods because everything in the world satisfies the MT interface. It's [55:31] kind of a little bit like it but in a completely different meaning the object in Java. And [55:35] you can build collections that use. There's quite a few checked in, but they have the [55:40] disadvantage that they’re not type safe since you have to unbox manually and stuff [55:45] like that. They’re not really what we would propose to do instead. It's just what we’re [55:49] using for now, why we struggle at this question. So generics, not yet but they're very subtle. [55:54] It’s interesting in the codes workbook, Josh Black has a long discussion about them. [55:59] And I think most people think that Java generics works really well including Josh, but he points [56:03] out they're actually quite a bit harder to get right than you think, and then maybe it's [56:07] time to think really hard before you put them in. So, they may come but they're not there [56:11] now. And, of course, there’s a million of other thing. Where’s my feature? Everybody [56:17] who programs has a feature they want, and chances are that some of the things you really [56:21] think are important are missing, or they might be there but not in the way you’re used [56:25] to. A really good example is inum but there is no iota. And iota's an interesting thing, [56:31] but it's not an inum. It's different. So, it may not be there, but there may be some [56:35] way to do what you want. It’s just Go is different or almost as good or maybe even [56:38] better. But why is it a feature there? Well, maybe it doesn’t really work in the language, [56:43] you know. Like there’s no point of arithmetic, point of arithmetic is nice but we can’t [56:46] do a point of arithmetic and make a language safe, there just not there. And the compiler [56:50] makes up for trying to compile a way some of the inefficiencies you get by having your [56:55] index and everywhere but on the other hand you got a safe length. So, a lot of the things [56:59] the people expect the language like this were not there because they break the rules and [57:03] we don’t have them. Or at least they break principles about what we think for language [57:07] should do. And there’s also a possible--like generics is a good example. They're just not [57:11] on top of our list. It doesn’t mean it won’t happen. Union types is another one we’ve [57:15] been thinking about. Actually, I think it would be a very good proposal for them, but [57:18] we haven’t done that yet. So it’s possible some of the things you’re finding for will [57:21] actually occur fairly sooner or not too far out. But it’s a different off language that [57:27] even if it doesn’t have the features you want, it may have features you haven’t used [57:30] before, if I can guarantee that some of you haven’t used before, the interface stuff [57:33] is very interesting. And don’t let the lack of things you expect stop you from playing [57:38] with things that are there and seeing that it does actually do of some pretty interesting [57:41] stuff. And a lot more information on this topic available is actually explicit language-assigned [57:46] FAQ on the website and it talks about a lot of the "why things aren’t there" things. [57:53] So, to conclude, I think we really have a nice language. I’m having more fun programming [58:00] than I’ve had in a long time because I feel like I’m getting work done, because I’m [58:03] getting work done. It’s early yet. I think the language is going to evolve some more, [58:07] these things we know, but a lot of the basics are locked down pretty well. I feel pretty [58:11] comfortable with them. It’s a very comfortable language to work in. It’s very productive. [58:15] You can write code really fast and have it work and be safe. There’s tons of documentation. [58:21] There’s a specification, complete spec of the language. There’s simple to trial for [58:27] beginners. There’s a document called Effective Go, which is growing to sort of explain idioms [58:32] and how you think about things and language differently. There’s some FAQ, and there's [58:36] even other things than that. All the implementations are available as open-source, the 6GHG5G suite [58:43] and all its tools, the gzgo and all that stuff is also in the different place, but it’s [58:49] also going out and it will be linked from the landing page. And so, if you want to try [58:53] it, go get it. If you want to help, please do it. There’s tons of work to do to build [58:57] it up to another level. We really are welcoming people to come in and talk about stuff, especially [59:03] if they want to help us do some interesting library work or build new tools, there’s [59:06] plenty of room for things to happen. So that’s our site and that’s our language. Thanks [59:10] for listening.