概览
Go 程序员视角
为什么 Go 程序员要学习 Dart?
作为 Go 程序员,你已经习惯了简洁、高效、并发优先的编程范式。Dart 作为一门现代语言,在某些方面与 Go 相似,但在其他方面提供了不同的解决方案:
并发模型对比
Go: Goroutine + Channel
Dart: Event Loop + Isolate
错误处理
Go: error 返回值
Dart: Exception + try/catch
类型系统
Go: 结构体 + 组合
Dart: 类 + 继承 + Mixin
异步编程
Go: goroutine + select
Dart: async/await + Stream
核心差异速览
| 特性 | Dart | Go | 对 Go 程序员的影响 |
|---|---|---|---|
| 类型系统 | 面向对象 + 泛型 | 组合优先 + 泛型 | 需要适应继承和类概念 |
| 并发模型 | Event Loop + Isolate | Goroutine + Channel | 理解单线程 Event Loop 模型 |
| 错误处理 | Exception + try/catch | error 返回值 | 适应异常而非错误值 |
| 编译方式 | JIT + AOT 双模式 | AOT | 享受热重载开发体验 |
| 接口 | 显式 implements | 隐式鸭子类型 | 需要显式声明接口实现 |
Hello World 对比
Go vs Dart
// Go 版本
package main
import "fmt"
func main() {
// 类型推断
name := "Go"
// fmt.Printf 不支持字符串插值
fmt.Printf("Hello, %s!\n", name)
// 数组 vs 切片
numbers := []int{1, 2, 3, 4, 5}
// 没有级联操作符
numbers = append(numbers, 6)
sort.Ints(numbers)
// 匿名函数
doubled := mapFunc(numbers, func(n int) int {
return n * 2
})
fmt.Println(doubled)
}
func mapFunc(arr []int, fn func(int) int) []int {
result := make([]int, len(arr))
for i, v := range arr {
result[i] = fn(v)
}
return result
}
// ====================
// Dart 版本
void main() {
// 类型推断(与 Go 的 := 类似)
var name = 'Dart';
// 字符串插值(Go 需要 fmt.Printf)
print('Hello, $name!');
// List(Go 是 slice)
var numbers = [1, 2, 3, 4, 5];
// 级联操作符(Go 没有这个)
numbers
..add(6)
..sort();
// 箭头函数(Go 是匿名函数)
var doubled = numbers.map((n) => n * 2);
print(doubled); // (2, 4, 6, 8, 10, 12)
}
Go → Dart 学习路径
- 语法相似性:变量声明、函数定义、import 语句都很相似
- 最大的转变:从结构体+组合转向类+继承,从 error 值转向异常
- 并发思维:从多线程 Goroutine 转向单线程 Event Loop
- 类型系统:从隐式接口转向显式 implements
- 空安全:Dart 的空安全比 Go 的 nil 更严格
类型系统
Go 对比
类型推断对比
Go 的 := 和 Dart 的 var 很相似,但语义有些差异
Go vs Dart: 类型推断
// Go 版本
package main
func main() {
// := 只能在函数内使用
name := "Go" // 推断为 string
count := 42 // 推断为 int
items := []string{} // 推断为 []string
// const 需要编译时确定
const maxItems = 100
// Go 没有类似于 Dart 的 final
// 如果要不可变,使用大写命名约定(包外不可见)或不修改
}
// ====================
// Dart 版本
void main() {
// var 可在任意位置使用
var name = 'Dart'; // 推断为 String
final count = 42; // 推断为 int(运行时常量,不可变)
const maxItems = 100; // 编译时常量
late Config config; // 延迟初始化(Go 没有这个)
// List 字面量
var items = []; // 明确泛型
// ⚠️ 注意:Dart 的 var 是类型推断,不是重新赋值
// var message = calculate(); // 如果返回类型不明确,建议显式声明
// ✅ 推荐明确类型
String result = calculate(); // 清晰表达意图
// ✅ 返回类型和参数类型必须明确
List getUsersById(List ids) {
return ids.map((id) => User.findById(id)).toList();
}
}
泛型系统对比
Go 1.18+ 支持泛型,与 Dart 的泛型系统在约束语法上有所不同
Go vs Dart: 泛型约束
// Go 版本
package main
import "fmt"
// 约束:类型参数必须实现指定接口
type Numeric interface {
int | int64 | float64
}
func Sum[T Numeric](values []T) T {
var total T
for _, v := range values {
total += v
}
return total
}
// 多个类型参数
func Pair[K comparable, V any](k K, v V) (K, V) {
return k, v
}
// 嵌套约束
type Ordered interface {
comparable
Less(other any) bool
}
func Sort[T Ordered](values []T) {
// 排序逻辑
}
// ====================
// Dart 版本
// 泛型约束:使用 extends 关键字
class Repository {
final Map _cache = {};
Future findById(int id) async {
if (_cache.containsKey(id)) {
return _cache[id];
}
final data = await _fetchFromDb(id);
if (data != null) {
_cache[id] = data;
}
return data;
}
Future _fetchFromDb(int id);
}
// 泛型方法
T firstOrNull(List list) {
return list.isEmpty ? null : list.first;
}
// 多重约束(Dart 使用 & 符号,Go 需要定义组合接口)
class Serializer> {
String serialize(T obj) {
return obj.toJson();
}
}
// Go 的 any 对应 Dart 的 Object? 或 dynamic
// Go 的 comparable 对应 Dart 没有直接等价物,需要手动实现 == 和 hashCode
// 使用
final userRepo = Repository();
final result = firstOrNull(['a', 'b']); // 'a'
final sum = Sum([1, 2, 3]); // 6
扩展方法对比
Dart 的 extension 是 Go 没有的强大特性,无需继承即可扩展类型
Go vs Dart: 扩展方法
// Go 版本 - Go 没有扩展方法!
// 只能创建辅助函数
package main
import (
"fmt"
"regexp"
"strings"
)
func IsValidEmail(email string) bool {
matched, _ := regexp.MatchString(
`^[\w-\.]+@[\w-]+\.[\w-]{2,4}$`,
email,
)
return matched
}
func Capitalize(s string) string {
if len(s) == 0 {
return s
}
return strings.ToUpper(string(s[0])) + s[1:]
}
// 使用
email := "test@example.com"
if IsValidEmail(email) {
fmt.Println(Capitalize(email))
}
// ====================
// Dart 版本 - Extension 提供原生支持
// 扩展 String
extension StringValidation on String {
bool get isValidEmail =>
RegExp(r'^[\w-\.]+@[\w-]+\.[\w-]{2,4}$').hasMatch(this);
String capitalize() =>
this.isEmpty ? this : this[0].toUpperCase() + substring(1);
}
// 扩展 List - 泛型扩展
extension ListOperations on List {
List distinct() => [...{...this}];
T? firstWhereOrNull(bool Function(T) test) {
for (var item in this) {
if (test(item)) return item;
}
return null;
}
}
// 使用 - 语法更自然,像原生方法
print('test@example.com'.isValidEmail); // true
print('hello'.capitalize()); // 'Hello'
print([1, 2, 2, 3].distinct()); // [1, 2, 3]
print(['a', 'b'].firstWhereOrNull((s) => s == 'b')); // 'b'
类型系统关键差异
- 接口实现:Go 是隐式鸭子类型,Dart 需要显式 implements
- 空安全:Dart 在编译时保证,Go 运行时可能出现 nil panic
- 扩展方法:Dart 有原生 extension,Go 只能用辅助函数
- 类型推断:Go 的 := 只能在函数内,Dart 的 var 无此限制
- 泛型约束:Go 使用接口组合,Dart 使用 extends 和 &
异步编程
并发模型对比
并发模型根本差异
这是 Go 和 Dart 最大的区别!理解这一点对 Go 程序员至关重要:
| 特性 | Go | Dart |
|---|---|---|
| 执行模型 | 多线程(M:N 调度) | 单线程 Event Loop |
| 并发原语 | Goroutine + Channel | Future + Stream + Isolate |
| 通信方式 | 共享内存 + Channel | 消息传递(Isolate 间) |
| 数据共享 | 共享可变状态(需同步) | 默认不可共享(Isolate 隔离) |
| 阻塞操作 | 阻塞 Goroutine(调度器处理) | 阻塞整个 Event Loop(需避免) |
并发执行对比
Go vs Dart: 并发执行
// Go 版本 - Goroutine
package main
import (
"fmt"
"sync"
)
func fetchUserData(userId int, wg *sync.WaitGroup, ch chan<- string) {
defer wg.Done()
// 阻塞操作不会阻塞其他 goroutine
data := httpGet(fmt.Sprintf("/api/users/%d", userId))
ch <- data
}
func loadDashboard() {
var wg sync.WaitGroup
ch := make(chan string, 3)
wg.Add(3)
go fetchUserData(1, &wg, ch)
go fetchUserPosts(1, &wg, ch)
go fetchUserSettings(1, &wg, ch)
// 等待所有 goroutine 完成
go func() {
wg.Wait()
close(ch)
}()
// 收集结果
results := make([]string, 0)
for data := range ch {
results = append(results, data)
}
renderDashboard(results)
}
// 错误处理 - Go 使用 error 返回值
func safeFetch() error {
data, err := fetchData()
if err != nil {
return fmt.Errorf("fetch failed: %w", err)
}
fmt.Println(data)
return nil
}
// ====================
// Dart 版本 - async/await
// 注意:Dart 默认单线程,async/await 让出执行权给 Event Loop
Future fetchUserData(int userId) async {
final response = await http.get('/api/users/$userId');
if (response.statusCode == 200) {
return response.body;
}
throw Exception('Failed to load user');
}
// 并发执行 - 类似 Go 的 WaitGroup
Future loadDashboard() async {
// 并行请求(Go 使用 goroutine + channel)
final results = await Future.wait([
fetchUserData(1),
fetchUserPosts(1),
fetchUserSettings(1),
]);
final [user, posts, settings] = results;
renderDashboard(user, posts, settings);
}
// 错误处理 - Dart 使用异常(Go 用 error 值)
Future safeFetch() async {
try {
final data = await fetchData();
print(data);
} on TimeoutException catch (e) {
print('请求超时: $e');
} on HttpException catch (e) {
print('HTTP错误: $e');
} catch (e) {
print('未知错误: $e');
} finally {
print('请求完成');
}
}
// 关键差异:
// 1. Go: goroutine 可以真正并行执行(多核)
// 2. Dart: async/await 只是让出执行权,仍是单线程
// 3. Go: channel 用于通信,也可以同步
// 4. Dart: Future.wait 等待多个 Future 完成
Stream vs Channel
Dart 的 Stream 类似 Go 的 Channel,但语义不同
Go vs Dart: 数据流处理
// Go 版本 - Channel
package main
func counter(ch chan<- int) {
for i := 1; i <= 10; i++ {
time.Sleep(time.Second)
ch <- i
}
close(ch)
}
func listenToCounter() {
ch := make(chan int)
go counter(ch)
for value := range ch {
fmt.Printf("计数: %d\n", value)
}
}
// 使用 select 处理多个 channel
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
results <- j * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// select 监听多个 channel
select {
case msg := <-ch1:
fmt.Println(msg)
case msg := <-ch2:
fmt.Println(msg)
case <-time.After(time.Second):
fmt.Println("超时")
}
}
// ====================
// Dart 版本 - Stream
// 创建 Stream
Stream counter() async* {
for (int i = 1; i <= 10; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
// 监听 Stream
void listenToCounter() {
counter().listen(
(value) => print('计数: $value'),
onError: (error) => print('错误: $error'),
onDone: () => print('完成'),
);
}
// Stream 转换 - 函数式风格(Go 需要手动写循环)
Stream processEvents() {
return eventStream
.where((event) => event.type == 'important') // 过滤
.map((event) => event.message.toUpperCase()) // 映射
.take(10) // 限制数量
.distinct(); // 去重
}
// Dart 没有直接等价于 select 的语法
// 但可以使用 StreamGroup 合并多个 Stream
import 'package:async/async.dart';
void advancedStreamProcessing() {
final merged = StreamGroup.merge([
userEvents,
systemEvents,
]);
merged
.debounce(Duration(milliseconds: 300)) // 防抖
.buffer(Duration(seconds: 1)) // 批处理
.listen((events) => processBatch(events));
}
// 关键差异:
// 1. Go: channel 是缓冲的或有界的,可以用于同步
// 2. Dart: Stream 是推式的,类似 RxJS 的 Observable
// 3. Go: select 可以等待多个 channel(Dart 需要用 StreamGroup)
// 4. Dart: Stream 有丰富的操作符(map, filter, reduce 等)
// 5. Go: channel 通信会复制数据(除非用指针),Dart Stream 传递引用
Go 程序员迁移指南
- 不要阻塞 Event Loop:Dart 单线程,同步阻塞会卡死整个应用
- async/await 不是真正的并行:只是让出执行权,类似 Go 的 goroutine yield
- Isolate 用于 CPU 密集型:类似 Go 的 goroutine,但成本更高
- Stream 是推式的:Go channel 可以拉取,Dart Stream 只能监听
- 错误处理方式:Go 用 error 返回值,Dart 用异常
面向对象
结构体 vs 类
结构体 vs 类
Go 的结构体和 Dart 的类在概念上有根本差异
Go vs Dart: 数据结构定义
// Go 版本 - 结构体
package main
import "fmt"
// 结构体定义
type User struct {
ID string
Name string
Email string
// 嵌入结构体(组合)
Profile
CreatedAt time.Time
}
// 嵌入的结构体
type Profile struct {
Avatar string
Bio string
}
// 构造函数(不是语法特性,只是约定)
func NewUser(name, email string) *User {
return &User{
ID: generateID(),
Name: name,
Email: email,
CreatedAt: time.Now(),
}
}
// 方法(接收者可以是值或指针)
func (u *User) UpdateEmail(email string) {
u.Email = email
}
func (u User) FullName() string {
return u.Name
}
// JSON 序列化需要 struct tags
type User struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func (u User) ToJSON() ([]byte, error) {
return json.Marshal(u)
}
// ====================
// Dart 版本 - 类
class User {
final String id;
final String name;
final String email;
final DateTime createdAt;
// 父类(Go 没有继承,只有组合)
User({
required this.id,
required this.name,
required this.email,
DateTime? createdAt,
}) : createdAt = createdAt ?? DateTime.now();
// 命名构造函数(Go 没有这个)
User.fromJson(Map json)
: id = json['id'],
name = json['name'],
email = json['email'],
createdAt = DateTime.parse(json['createdAt']);
// 工厂构造函数(Go 用 NewUser 函数)
factory User.create({
required String name,
required String email,
}) {
return User(
id: generateID(),
name: name,
email: email,
);
}
// 方法(Dart 不需要显式的接收者声明)
void updateEmail(String newEmail) {
// Dart 的 final 字段不能修改
// 需要返回新实例
// User updatedUser = this.copyWith(email: newEmail);
}
String get fullName => name;
// copyWith 模式(Go 没有这个语法糖)
User copyWith({
String? id,
String? name,
String? email,
DateTime? createdAt,
}) {
return User(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
createdAt: createdAt ?? this.createdAt,
);
}
// 序列化
Map toJson() => {
'id': id,
'name': name,
'email': email,
'createdAt': createdAt.toIso8601String(),
};
}
// 关键差异:
// 1. Go: 结构体是值类型,可以复制;Dart: 类是引用类型
// 2. Go: 字段默认导出(大写);Dart: 字段默认私有(下划线前缀)
// 3. Go: 方法接收者可以是值或指针;Dart: 方法隐式使用 this
// 4. Go: 没有继承,只有组合;Dart: 支持继承和组合
// 5. Go: JSON 需要 struct tags;Dart: 使用 toJson/fromJson 方法
接口实现对比
Go 的隐式接口 vs Dart 的显式接口
Go vs Dart: 接口实现
// Go 版本 - 隐式接口
package main
// 定义接口(鸭子类型)
type Shape interface {
Area() float64
Perimeter() float64
}
// 结构体自动实现接口(无需显式声明)
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
// 使用
func PrintShapeInfo(s Shape) {
fmt.Printf("Area: %.2f\n", s.Area())
fmt.Printf("Perimeter: %.2f\n", s.Perimeter())
}
// ====================
// Dart 版本 - 显式接口
abstract class Shape {
double get area;
double get perimeter;
}
class Rectangle implements Shape {
final double width;
final double height;
Rectangle({required this.width, required this.height});
@override
double get area => width * height;
@override
double get perimeter => 2 * (width + height);
}
// 使用
void printShapeInfo(Shape s) {
print('Area: ${s.area}');
print('Perimeter: ${s.perimeter}');
}
// Dart 也支持 Mixin(类似 Go 的组合)
mixin Flyable {
void fly() {
print('Flying!');
}
}
class Bird with Flyable {
void chirp() {
print('Chirp chirp!');
}
}
// Go 没有Mixin,只能通过组合实现类似功能
type Bird struct {
flyer Flyer
}
type Flyer interface {
Fly()
}
func (b *Bird) Chirp() {
fmt.Println("Chirp chirp!")
}
func (b *Bird) Fly() {
b.flyer.Fly()
}
面向对象关键差异
- 接口实现:Go 隐式(鸭子类型),Dart 显式(implements)
- 继承机制:Go 没有继承(只有组合),Dart 支持继承和 Mixin
- 构造函数:Go 用 NewXxx 函数,Dart 有多种构造函数形式
- 方法接收者:Go 需要显式声明(值/指针),Dart 隐式使用 this
- 序列化:Go 用 struct tags,Dart 用 toJson/fromJson 方法
空安全
nil vs null
空安全对比
Dart 2.12+ 引入了严格的空安全,与 Go 的 nil 处理有本质区别
Go vs Dart: 空安全
// Go 版本 - nil 可能导致 panic
package main
func main() {
var name *string // 指针默认为 nil
fmt.Println(*name) // panic: nil pointer dereference
// 需要显式检查
if name != nil {
fmt.Println(*name)
}
// 切片的 nil 检查
var items []string
if items != nil {
fmt.Println(len(items))
}
// map 的 nil 检查
var m map[string]int
if m != nil {
m["key"] = 1
}
}
// ====================
// Dart 版本 - 编译时空安全检查
void main() {
// String? 表示可能为 null
String? name; // 默认为 null
// 编译错误:不能直接访问可能为 null 的值
// print(name.length); // 错误!
// 使用 ?. 空感知操作符
print(name?.length); // null
// 使用 ?? 空合并操作符
print(name ?? 'default'); // 'default'
// 使用 ! 断言非空(运行时检查)
if (name != null) {
print(name.length); // 安全
}
// late 延迟初始化
late String config = loadConfig();
// final 不可变
final String immutable = 'value';
// immutable = 'new'; // 错误!
// List 和 Map 的空安全
List? items; // 可能为 null
items = []; // 赋值非空
Map? m; // 可能为 null
m = {}; // 赋值非空
}
String loadConfig() {
return 'config';
}
// 关键差异:
// 1. Go: nil 可能导致运行时 panic,需要手动检查
// 2. Dart: 空安全在编译时保证,不会出现空指针异常
// 3. Go: 所有引用类型都可以为 nil
// 4. Dart: 需要显式声明类型是否可空(Type?)
// 5. Dart: 提供丰富的空安全操作符(?., ??, !等)
空安全操作符
Dart 空安全操作符
// Dart 空安全操作符示例
void main() {
String? name;
// ?. 空感知访问
print(name?.length); // null
// ?? 空合并
print(name ?? 'Anonymous'); // 'Anonymous'
// ??= 空赋值
name ??= 'John';
print(name); // 'John'
// ! 非空断言(运行时检查)
String definitelyName = name!; // 如果 name 为 null,抛出异常
// 级联空感知
User? user = User(name: 'John');
print(user?.name?.toUpperCase()); // 'JOHN'
// 列表空安全
List? items;
print(items?.length); // null
print(items?.first); // null
// 空安全链式调用
print(user?.profile?.bio ?? 'No bio');
}
class User {
final String? name;
final Profile? profile;
User({this.name, this.profile});
}
class Profile {
final String? bio;
Profile({this.bio});
}
// vs Go 的 nil 检查
func printName(name *string) {
if name == nil {
fmt.Println("Anonymous")
return
}
fmt.Println(*name)
}
空安全最佳实践
- 优先使用非空类型:只有确实可能为 null 时才使用 Type?
- 避免过度使用 !:非空断言应该只在确定不为 null 时使用
- 使用 late 谨慎:确保延迟初始化的变量在使用前会被赋值
- 合理使用 ?? 操作符:提供有意义的默认值
- 类型设计:让 API 明确表达是否可能返回 null
函数式编程
高阶函数
函数式编程对比
Dart 提供了丰富的函数式编程特性,与 Go 的函数处理方式有所不同
Go vs Dart: 函数式编程
// Go 版本
package main
import "fmt"
// 高阶函数
func mapInts(arr []int, fn func(int) int) []int {
result := make([]int, len(arr))
for i, v := range arr {
result[i] = fn(v)
}
return result
}
func filterInts(arr []int, fn func(int) bool) []int {
result := []int{}
for _, v := range arr {
if fn(v) {
result = append(result, v)
}
}
return result
}
func reduceInts(arr []int, initial int, fn func(int, int) int) int {
result := initial
for _, v := range arr {
result = fn(result, v)
}
return result
}
func main() {
numbers := []int{1, 2, 3, 4, 5}
// Map
doubled := mapInts(numbers, func(n int) int {
return n * 2
})
fmt.Println(doubled) // [2 4 6 8 10]
// Filter
evens := filterInts(numbers, func(n int) bool {
return n%2 == 0
})
fmt.Println(evens) // [2 4]
// Reduce
sum := reduceInts(numbers, 0, func(acc, n int) int {
return acc + n
})
fmt.Println(sum) // 15
}
// ====================
// Dart 版本
void main() {
final numbers = [1, 2, 3, 4, 5];
// Map - 箭头函数
final doubled = numbers.map((n) => n * 2);
print(doubled); // (2, 4, 6, 8, 10)
// Filter
final evens = numbers.where((n) => n % 2 == 0);
print(evens); // (2, 4)
// Reduce
final sum = numbers.fold(0, (acc, n) => acc + n);
print(sum); // 15
// 链式操作
final result = numbers
.where((n) => n % 2 == 0)
.map((n) => n * 2)
.reduce((acc, n) => acc + n);
print(result); // 12 (2*2 + 4*2)
}
// 高阶函数示例
List mapList(List list, R Function(T) fn) {
return list.map(fn).toList();
}
// 函数作为一等公民
void applyOperation(int value, int Function(int) operation) {
print(operation(value));
}
void main() {
final numbers = [1, 2, 3, 4, 5];
final squared = mapList(numbers, (n) => n * n);
print(squared); // [1, 4, 9, 16, 25]
applyOperation(5, (n) => n * 2); // 10
// 闭包
final multiplier = (int factor) {
return (int n) => n * factor;
};
final double = multiplier(2);
print(double(5)); // 10
}
// 关键差异:
// 1. Dart 有内置的 map/where/fold 等方法,Go 需要手动实现
// 2. Dart 支持箭头函数语法,更简洁
// 3. Dart 支持链式调用,Go 需要多行代码
// 4. Dart 的集合操作返回惰性迭代器,Go 立即计算
// 5. Dart 支持更丰富的函数式操作符
函数式编程关键差异
- 内置方法:Dart 有 map/where/fold,Go 需要手动实现
- 箭头函数:Dart 支持简洁的箭头语法,Go 只能用完整函数
- 链式调用:Dart 支持流畅的链式操作,Go 需要多行代码
- 惰性求值:Dart 的 map/where 返回惰性迭代器,Go 立即计算
- 闭包支持:两者都支持,但 Dart 语法更简洁
模式匹配
switch vs when
模式匹配对比
Dart 3.0 引入了强大的模式匹配功能,与 Go 的 switch 语句相比更强大
Go vs Dart: 模式匹配
// Go 版本 - switch 语句
package main
import "fmt"
func describe(value interface{}) {
switch v := value.(type) {
case int:
fmt.Printf("Integer: %d\n", v)
case string:
fmt.Printf("String: %s\n", v)
case bool:
fmt.Printf("Boolean: %t\n", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}
func main() {
describe(42)
describe("hello")
describe(true)
describe(3.14)
}
// Go 结构体解构(Go 1.18+)
type Point struct {
X, Y int
}
func distance(p Point) int {
switch {
case p.X == 0 && p.Y == 0:
return 0
default:
return p.X*p.X + p.Y*p.Y
}
}
// ====================
// Dart 版本 - 模式匹配
String describe(Object? value) {
return switch (value) {
int i => 'Integer: $i',
String s => 'String: $s',
bool b => 'Boolean: $b',
null => 'null value',
_ => 'Unknown type: ${value.runtimeType}',
};
}
void main() {
print(describe(42)); // Integer: 42
print(describe('hello')); // String: hello
print(describe(true)); // Boolean: true
print(describe(3.14)); // Unknown type: double
print(describe(null)); // null value
}
// 记录类型解构
void main() {
final point = (x: 3, y: 4);
final result = switch (point) {
(x: 0, y: 0) => 'Origin',
(x: var x, y: 0) => 'On X axis at $x',
(x: 0, y: var y) => 'On Y axis at $y',
(x: var x, y: var y) => 'At ($x, $y)',
};
print(result); // At (3, 4)
}
// 类解构
class Point {
final int x;
final int y;
Point(this.x, this.y);
}
void main() {
final point = Point(3, 4);
switch (point) {
case Point(x: 0, y: 0):
print('Origin');
case Point(x: var x, y: var y) when x == y:
print('On diagonal at $x');
case Point(x: var x, y: var y):
print('At ($x, $y)');
}
}
// 列表解构
void main() {
final numbers = [1, 2, 3, 4, 5];
final result = switch (numbers) {
[] => 'Empty',
[var first] => 'Single: $first',
[var first, ...var rest] => 'First: $first, Rest: $rest',
};
print(result); // First: 1, Rest: [2, 3, 4, 5]
}
// 关键差异:
// 1. Dart 3.0+ 的 switch 是表达式,可以返回值
// 2. Dart 支持更丰富的模式匹配(记录、类、列表等)
// 3. Dart 支持 when 守卫条件
// 4. Dart 支持解构赋值
// 5. Go 的 switch 只能用于值匹配和类型断言
模式匹配进阶
Dart 模式匹配进阶
// Dart 模式匹配进阶示例
// if-case 语句
void main() {
final pair = (1, 2);
if (pair case (var x, var y) when x > y) {
print('First is greater: $x > $y');
} else {
print('Not greater');
}
}
// 模式赋值
void main() {
final json = {'name': 'John', 'age': 30};
if (json case {'name': String name, 'age': int age}) {
print('Name: $name, Age: $age');
}
}
// for 循环中的模式
void main() {
final points = [(1, 2), (3, 4), (5, 6)];
for (final (x, y) in points) {
print('Point: ($x, $y)');
}
}
// 可选模式匹配
void main() {
final result = getUser();
switch (result) {
case Success(value: var user):
print('Success: $user');
case Error(message: var error):
print('Error: $error');
case null:
print('No result');
}
}
// 模式匹配在函数参数中
void main() {
handleData([1, 2, 3]); // List
handleData({'key': 'value'}); // Map
handleData('string'); // String
}
void handleData(dynamic data) {
switch (data) {
case List(length: var len) when len > 0:
print('List with $len items');
case Map(length: var len) when len > 0:
print('Map with $len entries');
case String(length: var len) when len > 0:
print('String with $len characters');
default:
print('Other type');
}
}
// vs Go 的类型断言
func handleData(data interface{}) {
switch v := data.(type) {
case []interface{}:
fmt.Printf("List with %d items\n", len(v))
case map[string]interface{}:
fmt.Printf("Map with %d entries\n", len(v))
case string:
fmt.Printf("String with %d characters\n", len(v))
default:
fmt.Println("Other type")
}
}
模式匹配最佳实践
- 使用 switch 表达式:比传统的 if-else 更清晰
- 合理使用守卫条件:when 子句让模式匹配更精确
- 利用解构:简化数据提取逻辑
- 模式优先原则:将最具体的模式放在前面
- 避免过度匹配:保持代码可读性
语言对比
完整对比
完整语言对比
Go 和 Dart 在设计哲学和应用场景上有显著差异
| 维度 | Go | Dart |
|---|---|---|
| 设计哲学 | 简单、实用、性能优先 | 现代、类型安全、开发者体验 |
| 主要用途 | 后端服务、系统编程、云原生 | 前端(Flutter)、跨平台应用 |
| 执行模型 | 编译型、多线程 | JIT/AOT 双模式、单线程 Event Loop |
| 并发模型 | Goroutine + Channel | Future + Stream + Isolate |
| 类型系统 | 结构体 + 组合 + 泛型 | 类 + 继承 + 泛型 |
| 错误处理 | error 返回值 | Exception + try/catch |
| 接口机制 | 隐式鸭子类型 | 显式 implements |
| 空安全 | 运行时 nil 检查 | 编译时空安全 |
| 函数式编程 | 有限支持 | 丰富支持(map/where/fold) |
| 学习曲线 | 平缓,语法简单 | 中等,概念较多 |
| 生态系统 | 成熟,专注后端 | 强大,专注移动/Web |
| 性能 | 极高,接近 C | 良好,适合移动应用 |
何时选择 Dart?
- 跨平台应用开发:使用 Flutter 构建 iOS/Android/Web 桌面应用
- 前端项目:需要类型安全的 JavaScript 替代方案
- 快速原型开发:热重载功能加速开发迭代
- 团队熟悉面向对象:从 Java/C# 背景迁移
- 现代开发体验:需要优秀的 IDE 支持和工具链
何时选择 Go?
- 后端服务:API 服务、微服务、分布式系统
- 高并发场景:需要真正并行处理和强大并发控制
- 系统编程:工具、CLI 应用、操作系统组件
- 云原生应用:Docker、Kubernetes 生态
- 简单可维护:需要代码简洁、易于理解的团队项目
总结
Go 和 Dart 都是优秀的现代语言,但设计目标和适用场景不同:
- Go:适合构建高性能、可扩展的后端服务和系统工具
- Dart:适合构建跨平台的移动应用和 Web 前端
- 两者可以互补:后端用 Go,前端用 Dart/Flutter
- 学习价值:掌握两种语言可以拓宽技术视野