Kodewick
开始学 →

Go 在线运行

免费,不用安装,不用登录。写完就跑,还能看每个 goroutine 各走各的、每个 channel 里装着什么。

试试:
Go 在线运行全屏 · 代码和输出都还在
Go
例子
pipeline.go
package main

import "fmt"

func main() {
	ch := make(chan int)
	go func() {
		for i := 1; i <= 3; i++ {
			ch <- i * i
		}
		close(ch)
	}()
	for v := range ch {
		fmt.Println(v)
	}
}
输出

按「运行」看结果

Go 1.24第 1 行,第 1 列缩进 4 空格学 Go →改一改就会存进「我的代码」
不只是运行

看它怎么跑:goroutine、channel、切片,一步一步画出来

每个 goroutine 一条泳道,谁在跑、谁在等什么一眼看清。channel 里有几个值看得见;几个切片共用同一个底层数组,也画得出来。

▶ 就在这里播放,不用登录
调试和分享

能跑,能停,能回放,能发出去

像在本地用 VS Code 一样顺手,还多了本地没有的:把每一步画出来。

单步

一行一行地跑,每个变量的值都摆在旁边

按「单步」,代码停在每一行:亮的是马上要跑的那行,旁边是此刻每个变量的值。也能往回退,← → 键就能走。

一行一行地跑,每个变量的值都摆在旁边
出错回放

出错了?直接看它是怎么错的

报错下面有「看看怎么错的」:可视化停在出错的那一步,红色标出来,还用人话说清楚原因。前面每一步都能倒回去看。

出错了?直接看它是怎么错的
分享

链接、动图、代码图片,一键发到 X

一个链接打开就是这段代码;也能导出运行过程的动图(浅色 / 深色、横版 / 竖版)和代码图片,发到 X、小红书、群聊,或嵌进博客。

链接、动图、代码图片,一键发到 X
例子

12 个 Go 例子,点一下就在上面运行

每个都能单步、能可视化,改一改再跑。

FizzBuzz

for 循环和不带值的 switch。

package main

import "fmt"

func main() {
	for i := 1; i <= 15; i++ {
		switch {
		case i%15 == 0:
			fmt.Println("FizzBuzz")
		case i%3 == 0:
			fmt.Println("Fizz")
		case i%5 == 0:
			fmt.Println("Buzz")
		default:
			fmt.Println(i)
		}
	}
}

切片和 append

越长越大时 len 和 cap 怎么变;可视化里看得见背后的数组。

package main

import "fmt"

func main() {
	var s []int
	for i := 1; i <= 5; i++ {
		s = append(s, i*i)
		fmt.Println(s, len(s), cap(s))
	}
}

map 计数

数每个单词出现了几次。

package main

import (
	"fmt"
	"sort"
	"strings"
)

func main() {
	text := "go is fun and go is fast"
	count := map[string]int{}
	for _, w := range strings.Fields(text) {
		count[w]++
	}
	keys := make([]string, 0, len(count))
	for k := range count {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		fmt.Println(k, count[k])
	}
}

结构体和方法

指针接收者改的是结构体本身。

package main

import "fmt"

type Account struct {
	Owner   string
	Balance int
}

func (a *Account) Deposit(n int) {
	a.Balance += n
}

func main() {
	acc := Account{"Maya", 100}
	acc.Deposit(50)
	fmt.Println(acc.Owner, acc.Balance)
}

接口

不同的形状,同一个 Area 方法。

package main

import (
	"fmt"
	"math"
)

type Shape interface {
	Area() float64
}

type Rect struct{ W, H float64 }
type Circle struct{ R float64 }

func (r Rect) Area() float64   { return r.W * r.H }
func (c Circle) Area() float64 { return math.Pi * c.R * c.R }

func main() {
	shapes := []Shape{Rect{3, 4}, Circle{1}}
	for _, s := range shapes {
		fmt.Printf("%.2f\n", s.Area())
	}
}

错误处理

函数返回一个值和一个 error。

package main

import (
	"errors"
	"fmt"
)

func divide(a, b int) (int, error) {
	if b == 0 {
		return 0, errors.New("division by zero")
	}
	return a / b, nil
}

func main() {
	for _, b := range []int{2, 0} {
		q, err := divide(10, b)
		if err != nil {
			fmt.Println("error:", err)
			continue
		}
		fmt.Println(q)
	}
}

闭包

一个记得自己计数的函数。

package main

import "fmt"

func counter() func() int {
	n := 0
	return func() int {
		n++
		return n
	}
}

func main() {
	next := counter()
	fmt.Println(next(), next(), next())
}

goroutine 和 channel

一个 goroutine 发平方数,main 收。

package main

import "fmt"

func main() {
	ch := make(chan int)
	go func() {
		for i := 1; i <= 3; i++ {
			ch <- i * i
		}
		close(ch)
	}()
	for v := range ch {
		fmt.Println(v)
	}
}

等大家都做完

sync.WaitGroup 和 Mutex。

package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	var mu sync.Mutex
	total := 0
	for i := 1; i <= 3; i++ {
		wg.Add(1)
		go func(n int) {
			defer wg.Done()
			mu.Lock()
			total += n
			mu.Unlock()
		}(i)
	}
	wg.Wait()
	fmt.Println("total", total)
}

select 和超时

等哪个先来就用哪个。

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan string)
	go func() {
		time.Sleep(50 * time.Millisecond)
		ch <- "done"
	}()
	select {
	case r := <-ch:
		fmt.Println(r)
	case <-time.After(time.Second):
		fmt.Println("too slow")
	}
}

sort.Slice 排序

用比较函数按年龄排序。

package main

import (
	"fmt"
	"sort"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	people := []Person{{"Ava", 31}, {"Ben", 25}, {"Cy", 40}}
	sort.Slice(people, func(i, j int) bool {
		return people[i].Age < people[j].Age
	})
	fmt.Println(people)
}

下标越界

Go 原话的 panic,以及出事的那一步。

package main

import "fmt"

func main() {
	nums := []int{1, 2, 3}
	for i := 0; i <= len(nums); i++ {
		fmt.Println(nums[i])
	}
}
三步

怎么在线运行 Go

  1. 1
    写代码,或选一个例子编辑器和 VS Code 一样:自动补全、括号配对、Ctrl+/ 注释。
  2. 2
    点「运行」,或按 Ctrl + Enter结果在旁边的输出里;程序要 fmt.Scan 时直接在输出里回答。
  3. 3
    想看过程:点「单步」或「可视化」代码自动存在这个浏览器里,「我的代码」里能找回来。
支持什么

Go,在你的浏览器里跑

✓ 支持

  • Go 1.24:结构体、方法、接口、泛型、闭包、defer / panic / recover
  • goroutine、channel、select、sync(WaitGroup、Mutex)、time
  • fmt、strings、strconv、sort、math、errors 等常用包
  • fmt.Scan 在输出面板里读输入
  • 编译错误和 panic 用 Go 的原话;死锁能找出来

✕ 暂不支持

  • 标准库以外的包(go get)
  • 读写电脑上的文件、联网(net/http)
  • reflect、unsafe、cgo
  • 停不下来的程序(跑太久会自动停下)
常见问题

关于 Go 在线运行

要钱吗?要注册吗?

都不用。打开就能写 Go、能跑;代码存在你自己的浏览器里。

和官方的 Go Playground 有什么不同?

都能在浏览器里跑 Go。这里还能单步,看每个 goroutine 各自一条泳道、每个 channel 里装着什么,能用 fmt.Scan 读输入,代码也会存下来。

这是真的 Go 编译器吗?

这是我们自己写、在浏览器里运行的 Go 引擎,在它支持的标准库范围内,输出和 Go 1.24 一样。

能找出死锁吗?

能:所有 goroutine 都在等时会停下,用 Go 的原话说出来;可视化里能看到谁在等什么。

程序 panic 了,能看出为什么吗?

报错下面点「看看怎么错的」:可视化停在出错那一步,可以一步步往回看。

手机上能用吗?

能。点一下代码就全屏写,有 Tab、花括号、:= 这些专门的按键。

代码存在哪?

存在这个浏览器里,「我的代码」能找回来;重要的可以分享成链接存一份。

怎么发给别人?

「分享」→ 复制链接,或者导出运行过程的动图、代码图片,一键发到 X。

还有

另外四种语言,同样能跑、能看