From ba2fc89371b1e21b79327c4264163ebb083bf5d4 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 4 Dec 2025 15:08:58 +0000 Subject: [PATCH] feat: Add Java guide and optimize Rust guide - Add `reference/java.md` covering Java 17/21, Spring Boot 3, and Virtual Threads. - Update `reference/rust.md` to include modern idioms (let-else), tracing for observability, and the Typestate pattern. - Update `SKILL.md` and `README.md` to reflect new capabilities. --- README.md | 28 ++- SKILL.md | 3 +- reference/java.md | 405 +++++++++++++++++++++++++++++++++++++ reference/rust.md | 499 ++++++++++++++-------------------------------- 4 files changed, 580 insertions(+), 355 deletions(-) create mode 100644 reference/java.md diff --git a/README.md b/README.md index e3d8382..498386e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## English -> A modular code review skill for Claude Code, covering React 19, Vue 3, Rust, TypeScript, Python, CSS/Less/Sass, architecture design, and performance optimization. +> A modular code review skill for Claude Code, covering React 19, Vue 3, Rust, TypeScript, Java, Python, CSS/Less/Sass, architecture design, and performance optimization. [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) @@ -14,7 +14,7 @@ This is a Claude Code skill designed to help developers conduct effective code reviews. It provides: -- **Language-specific patterns** for React 19, Vue 3, Rust, TypeScript/JavaScript, Python +- **Language-specific patterns** for React 19, Vue 3, Rust, TypeScript/JavaScript, Java, Python - **Modern framework support** including React Server Components, TanStack Query v5, Suspense & Streaming - **Comprehensive checklists** for security, performance, and code quality - **Best practices** for giving constructive feedback @@ -30,6 +30,7 @@ This is a Claude Code skill designed to help developers conduct effective code r | **Vue 3** | Composition API, reactivity system, defineProps/defineEmits, watch cleanup | | **Rust** | Ownership & borrowing, unsafe code review, async/await, error handling (thiserror vs anyhow) | | **TypeScript** | Type safety, async/await patterns, common pitfalls | +| **Java** | Java 17/21 features (Records, Switch), Spring Boot 3, Virtual Threads, Stream API best practices | | **Go** | Error handling, goroutines/channels, context propagation, interface design, testing patterns | | **CSS/Less/Sass** | CSS variables, !important usage, performance optimization, responsive design, browser compatibility | | **TanStack Query** | v5 best practices, queryOptions, useSuspenseQuery, optimistic updates | @@ -45,6 +46,7 @@ This is a Claude Code skill designed to help developers conduct effective code r | **reference/vue.md** | ~920 | Vue 3.5 patterns + Composition API (on-demand) | | **reference/rust.md** | ~840 | Rust async/ownership/cancellation safety (on-demand) | | **reference/typescript.md** | ~540 | TypeScript generics/strict mode/ESLint (on-demand) | +| **reference/java.md** | ~800 | Java 17/21 & Spring Boot 3 patterns (on-demand) | | **reference/python.md** | ~1070 | Python async/typing/pytest (on-demand) | | **reference/go.md** | ~990 | Go goroutines/channels/context/interfaces (on-demand) | | **reference/css-less-sass.md** | ~660 | CSS/Less/Sass variables/performance/responsive (on-demand) | @@ -97,6 +99,7 @@ code-review-skill/ │ ├── vue.md # Vue 3 patterns (on-demand) │ ├── rust.md # Rust patterns (on-demand) │ ├── typescript.md # TypeScript/JS patterns (on-demand) +│ ├── java.md # Java patterns (on-demand) │ ├── python.md # Python patterns (on-demand) │ ├── go.md # Go patterns (on-demand) │ ├── css-less-sass.md # CSS/Less/Sass patterns (on-demand) @@ -124,6 +127,13 @@ This means reviewing a React PR only loads SKILL.md + react.md, not Vue/Rust/Pyt ### Key Topics Covered +#### Java & Spring Boot + +- **Java 17/21 Features**: Records, Pattern Matching for Switch, Text Blocks +- **Virtual Threads**: High-throughput I/O with Project Loom +- **Spring Boot 3**: Constructor Injection, `@ConfigurationProperties`, ProblemDetail +- **JPA Performance**: Solving N+1 problems, correct Entity design (equals/hashCode) + #### React 19 - `useActionState` - Unified form state management @@ -178,7 +188,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file ## 中文 -> 一个模块化的 Claude Code 代码审查技能,覆盖 React 19、Vue 3、Rust、TypeScript、Python、CSS/Less/Sass、架构设计和性能优化。 +> 一个模块化的 Claude Code 代码审查技能,覆盖 React 19、Vue 3、Rust、TypeScript、Java、Python、CSS/Less/Sass、架构设计和性能优化。 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) @@ -186,7 +196,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file 这是一个为 Claude Code 设计的代码审查技能,旨在帮助开发者进行高效的代码审查。它提供: -- **语言特定模式**:覆盖 React 19、Vue 3、Rust、TypeScript/JavaScript、Python +- **语言特定模式**:覆盖 React 19、Vue 3、Rust、TypeScript/JavaScript、Java、Python - **现代框架支持**:包括 React Server Components、TanStack Query v5、Suspense & Streaming - **全面的检查清单**:安全、性能和代码质量检查 - **最佳实践**:如何提供建设性的反馈 @@ -202,6 +212,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file | **Vue 3** | Composition API、响应性系统、defineProps/defineEmits、watch 清理 | | **Rust** | 所有权与借用、unsafe 代码审查、async/await、错误处理(thiserror vs anyhow) | | **TypeScript** | 类型安全、async/await 模式、常见陷阱 | +| **Java** | Java 17/21 特性(Records, Switch)、Spring Boot 3、虚拟线程、Stream API 最佳实践 | | **Go** | 错误处理、goroutine/channel、context 传播、接口设计、测试模式 | | **CSS/Less/Sass** | CSS 变量规范、!important 使用、性能优化、响应式设计、浏览器兼容性 | | **TanStack Query** | v5 最佳实践、queryOptions、useSuspenseQuery、乐观更新 | @@ -217,6 +228,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file | **reference/vue.md** | ~920 | Vue 3.5 + Composition API(按需加载)| | **reference/rust.md** | ~840 | Rust async/所有权/取消安全性(按需加载)| | **reference/typescript.md** | ~540 | TypeScript 泛型/strict 模式/ESLint(按需加载)| +| **reference/java.md** | ~800 | Java 17/21 & Spring Boot 3 模式(按需加载)| | **reference/python.md** | ~1070 | Python async/类型注解/pytest(按需加载)| | **reference/go.md** | ~990 | Go goroutine/channel/context/接口(按需加载)| | **reference/css-less-sass.md** | ~660 | CSS/Less/Sass 变量/性能/响应式(按需加载)| @@ -269,6 +281,7 @@ code-review-skill/ │ ├── vue.md # Vue 3 模式(按需加载) │ ├── rust.md # Rust 模式(按需加载) │ ├── typescript.md # TypeScript/JS 模式(按需加载) +│ ├── java.md # Java 模式(按需加载) │ ├── python.md # Python 模式(按需加载) │ ├── go.md # Go 模式(按需加载) │ ├── css-less-sass.md # CSS/Less/Sass 模式(按需加载) @@ -296,6 +309,13 @@ code-review-skill/ ### 核心内容 +#### Java & Spring Boot + +- **Java 17/21 特性**:Records、Switch 模式匹配、文本块 +- **虚拟线程**:Project Loom 带来的高吞吐量 I/O +- **Spring Boot 3**:构造器注入、`@ConfigurationProperties`、ProblemDetail +- **JPA 性能**:解决 N+1 问题、正确的 Entity 设计(equals/hashCode) + #### React 19 - `useActionState` - 统一的表单状态管理 diff --git a/SKILL.md b/SKILL.md index 43c5c6d..2f9cf2a 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,7 +1,7 @@ --- name: code-review-excellence description: | - Provides comprehensive code review guidance for React 19, Vue 3, Rust, TypeScript, and Python. + Provides comprehensive code review guidance for React 19, Vue 3, Rust, TypeScript, Java, and Python. Helps catch bugs, improve code quality, and give constructive feedback. Use when: reviewing pull requests, conducting PR reviews, code review, reviewing code changes, establishing review standards, mentoring developers, architecture reviews, security audits, @@ -180,6 +180,7 @@ Use labels to indicate priority: | **Rust** | [Rust Guide](reference/rust.md) | 所有权/借用, Unsafe 审查, 异步代码, 错误处理 | | **TypeScript** | [TypeScript Guide](reference/typescript.md) | 类型安全, async/await, 不可变性 | | **Python** | [Python Guide](reference/python.md) | 可变默认参数, 异常处理, 类属性 | +| **Java** | [Java Guide](reference/java.md) | Java 17/21 新特性, Spring Boot 3, 虚拟线程, Stream/Optional | | **Go** | [Go Guide](reference/go.md) | 错误处理, goroutine/channel, context, 接口设计 | | **CSS/Less/Sass** | [CSS Guide](reference/css-less-sass.md) | 变量规范, !important, 性能优化, 响应式, 兼容性 | diff --git a/reference/java.md b/reference/java.md new file mode 100644 index 0000000..2e4e177 --- /dev/null +++ b/reference/java.md @@ -0,0 +1,405 @@ +# Java Code Review Guide + +Java 审查重点:Java 17/21 新特性、Spring Boot 3 最佳实践、并发编程(虚拟线程)、JPA 性能优化以及代码可维护性。 + +## 目录 + +- [现代 Java 特性 (17/21+)](#现代-java-特性-1721) +- [Stream API & Optional](#stream-api--optional) +- [Spring Boot 最佳实践](#spring-boot-最佳实践) +- [JPA 与 数据库性能](#jpa-与-数据库性能) +- [并发与虚拟线程](#并发与虚拟线程) +- [Lombok 使用规范](#lombok-使用规范) +- [异常处理](#异常处理) +- [测试规范](#测试规范) +- [Review Checklist](#review-checklist) + +--- + +## 现代 Java 特性 (17/21+) + +### Record (记录类) + +```java +// ❌ 传统的 POJO/DTO:样板代码多 +public class UserDto { + private final String name; + private final int age; + + public UserDto(String name, int age) { + this.name = name; + this.age = age; + } + // getters, equals, hashCode, toString... +} + +// ✅ 使用 Record:简洁、不可变、语义清晰 +public record UserDto(String name, int age) { + // 紧凑构造函数进行验证 + public UserDto { + if (age < 0) throw new IllegalArgumentException("Age cannot be negative"); + } +} +``` + +### Switch 表达式与模式匹配 + +```java +// ❌ 传统的 Switch:容易漏掉 break,不仅冗长且易错 +String type = ""; +switch (obj) { + case Integer i: // Java 16+ + type = String.format("int %d", i); + break; + case String s: + type = String.format("string %s", s); + break; + default: + type = "unknown"; +} + +// ✅ Switch 表达式:无穿透风险,强制返回值 +String type = switch (obj) { + case Integer i -> "int %d".formatted(i); + case String s -> "string %s".formatted(s); + case null -> "null value"; // Java 21 处理 null + default -> "unknown"; +}; +``` + +### 文本块 (Text Blocks) + +```java +// ❌ 拼接 SQL/JSON 字符串 +String json = "{\n" + + " \"name\": \"Alice\",\n" + + " \"age\": 20\n" + + "}"; + +// ✅ 使用文本块:所见即所得 +String json = """ + { + "name": "Alice", + "age": 20 + } + """; +``` + +--- + +## Stream API & Optional + +### 避免滥用 Stream + +```java +// ❌ 简单的循环不需要 Stream(性能开销 + 可读性差) +items.stream().forEach(item -> { + process(item); +}); + +// ✅ 简单场景直接用 for-each +for (var item : items) { + process(item); +} + +// ❌ 极其复杂的 Stream 链 +List result = list.stream() + .filter(...) + .map(...) + .peek(...) + .sorted(...) + .collect(...); // 难以调试 + +// ✅ 拆分为有意义的步骤 +var filtered = list.stream().filter(...).toList(); +// ... +``` + +### Optional 正确用法 + +```java +// ❌ 将 Optional 用作参数或字段(序列化问题,增加调用复杂度) +public void process(Optional name) { ... } +public class User { + private Optional email; // 不推荐 +} + +// ✅ Optional 仅用于返回值 +public Optional findUser(String id) { ... } + +// ❌ 既然用了 Optional 还在用 isPresent() + get() +Optional userOpt = findUser(id); +if (userOpt.isPresent()) { + return userOpt.get().getName(); +} else { + return "Unknown"; +} + +// ✅ 使用函数式 API +return findUser(id) + .map(User::getName) + .orElse("Unknown"); +``` + +--- + +## Spring Boot 最佳实践 + +### 依赖注入 (DI) + +```java +// ❌ 字段注入 (@Autowired) +// 缺点:难以测试(需要反射注入),掩盖了依赖过多的问题,且不可变性差 +@Service +public class UserService { + @Autowired + private UserRepository userRepo; +} + +// ✅ 构造器注入 (Constructor Injection) +// 优点:依赖明确,易于单元测试 (Mock),字段可为 final +@Service +public class UserService { + private final UserRepository userRepo; + + public UserService(UserRepository userRepo) { + this.userRepo = userRepo; + } +} +// 💡 提示:结合 Lombok @RequiredArgsConstructor 可简化代码,但要小心循环依赖 +``` + +### 配置管理 + +```java +// ❌ 硬编码配置值 +@Service +public class PaymentService { + private String apiKey = "sk_live_12345"; +} + +// ❌ 直接使用 @Value 散落在代码中 +@Value("${app.payment.api-key}") +private String apiKey; + +// ✅ 使用 @ConfigurationProperties 类型安全配置 +@ConfigurationProperties(prefix = "app.payment") +public record PaymentProperties(String apiKey, int timeout, String url) {} +``` + +--- + +## JPA 与 数据库性能 + +### N+1 查询问题 + +```java +// ❌ FetchType.EAGER 或 循环中触发懒加载 +// Entity 定义 +@Entity +public class User { + @OneToMany(fetch = FetchType.EAGER) // 危险! + private List orders; +} + +// 业务代码 +List users = userRepo.findAll(); // 1 条 SQL +for (User user : users) { + // 如果是 Lazy,这里会触发 N 条 SQL + System.out.println(user.getOrders().size()); +} + +// ✅ 使用 @EntityGraph 或 JOIN FETCH +@Query("SELECT u FROM User u JOIN FETCH u.orders") +List findAllWithOrders(); +``` + +### 事务管理 + +```java +// ❌ 在 Controller 层开启事务(数据库连接占用时间过长) +// ❌ 在 private 方法上加 @Transactional(AOP 不生效) +@Transactional +private void saveInternal() { ... } + +// ✅ 在 Service 层公共方法加 @Transactional +// ✅ 读操作显式标记 readOnly = true (性能优化) +@Service +public class UserService { + @Transactional(readOnly = true) + public User getUser(Long id) { ... } + + @Transactional + public void createUser(UserDto dto) { ... } +} +``` + +### Entity 设计 + +```java +// ❌ 在 Entity 中使用 Lombok @Data +// @Data 生成的 equals/hashCode 包含所有字段,可能触发懒加载导致性能问题或异常 +@Entity +@Data +public class User { ... } + +// ✅ 仅使用 @Getter, @Setter +// ✅ 自定义 equals/hashCode (通常基于 ID) +@Entity +@Getter +@Setter +public class User { + @Id + private Long id; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof User)) return false; + return id != null && id.equals(((User) o).id); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } +} +``` + +--- + +## 并发与虚拟线程 + +### 虚拟线程 (Java 21+) + +```java +// ❌ 传统线程池处理大量 I/O 阻塞任务(资源耗尽) +ExecutorService executor = Executors.newFixedThreadPool(100); + +// ✅ 使用虚拟线程处理 I/O 密集型任务(高吞吐量) +// Spring Boot 3.2+ 开启:spring.threads.virtual.enabled=true +ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + +// 在虚拟线程中,阻塞操作(如 DB 查询、HTTP 请求)几乎不消耗 OS 线程资源 +``` + +### 线程安全 + +```java +// ❌ SimpleDateFormat 是线程不安全的 +private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + +// ✅ 使用 DateTimeFormatter (Java 8+) +private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + +// ❌ HashMap 在多线程环境可能死循环或数据丢失 +// ✅ 使用 ConcurrentHashMap +Map cache = new ConcurrentHashMap<>(); +``` + +--- + +## Lombok 使用规范 + +```java +// ❌ 滥用 @Builder 导致无法强制校验必填字段 +@Builder +public class Order { + private String id; // 必填 + private String note; // 选填 +} +// 调用者可能漏掉 id: Order.builder().note("hi").build(); + +// ✅ 关键业务对象建议手动编写 Builder 或构造函数以确保不变量 +// 或者在 build() 方法中添加校验逻辑 (Lombok @Builder.Default 等) +``` + +--- + +## 异常处理 + +### 全局异常处理 + +```java +// ❌ 到处 try-catch 吞掉异常或只打印日志 +try { + userService.create(user); +} catch (Exception e) { + e.printStackTrace(); // 不应该在生产环境使用 + // return null; // 吞掉异常,上层不知道发生了什么 +} + +// ✅ 自定义异常 + @ControllerAdvice (Spring Boot 3 ProblemDetail) +public class UserNotFoundException extends RuntimeException { ... } + +@RestControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(UserNotFoundException.class) + public ProblemDetail handleNotFound(UserNotFoundException e) { + return ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, e.getMessage()); + } +} +``` + +--- + +## 测试规范 + +### 单元测试 vs 集成测试 + +```java +// ❌ 单元测试依赖真实数据库或外部服务 +@SpringBootTest // 启动整个 Context,慢 +public class UserServiceTest { ... } + +// ✅ 单元测试使用 Mockito +@ExtendWith(MockitoExtension.class) +class UserServiceTest { + @Mock UserRepository repo; + @InjectMocks UserService service; + + @Test + void shouldCreateUser() { ... } +} + +// ✅ 集成测试使用 Testcontainers +@Testcontainers +@SpringBootTest +class UserRepositoryTest { + @Container + static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:15"); + // ... +} +``` + +--- + +## Review Checklist + +### 基础与规范 +- [ ] 遵循 Java 17/21 新特性(Switch 表达式, Records, 文本块) +- [ ] 避免使用已过时的类(Date, Calendar, SimpleDateFormat) +- [ ] 集合操作是否优先使用了 Stream API 或 Collections 方法? +- [ ] Optional 仅用于返回值,未用于字段或参数 + +### Spring Boot +- [ ] 使用构造器注入而非 @Autowired 字段注入 +- [ ] 配置属性使用了 @ConfigurationProperties +- [ ] Controller 职责单一,业务逻辑下沉到 Service +- [ ] 全局异常处理使用了 @ControllerAdvice / ProblemDetail + +### 数据库 & 事务 +- [ ] 读操作事务标记了 `@Transactional(readOnly = true)` +- [ ] 检查是否存在 N+1 查询(EAGER fetch 或循环调用) +- [ ] Entity 类未使用 @Data,正确实现了 equals/hashCode +- [ ] 数据库索引是否覆盖了查询条件 + +### 并发与性能 +- [ ] I/O 密集型任务是否考虑了虚拟线程? +- [ ] 线程安全类是否使用正确(ConcurrentHashMap vs HashMap) +- [ ] 锁的粒度是否合理?避免在锁内进行 I/O 操作 + +### 可维护性 +- [ ] 关键业务逻辑有充分的单元测试 +- [ ] 日志记录恰当(使用 Slf4j,避免 System.out) +- [ ] 魔法值提取为常量或枚举 diff --git a/reference/rust.md b/reference/rust.md index 1fa062c..5d32f94 100644 --- a/reference/rust.md +++ b/reference/rust.md @@ -4,18 +4,86 @@ ## 目录 +- [现代 Rust 习语 (Modern Idioms)](#现代-rust-习语-modern-idioms) - [所有权与借用](#所有权与借用) - [Unsafe 代码审查](#unsafe-代码审查最关键) -- [异步代码](#异步代码) +- [异步代码与可观测性](#异步代码与可观测性) - [取消安全性](#取消安全性) - [spawn vs await](#spawn-vs-await) - [错误处理](#错误处理) - [性能](#性能) -- [Trait 设计](#trait-设计) +- [API 设计与 Typestate](#api-设计与-typestate) - [Review Checklist](#rust-review-checklist) --- +## 现代 Rust 习语 (Modern Idioms) + +### let-else (Rust 1.65+) + +```rust +// ❌ 传统的 match/if let 嵌套,导致右移(Rightward Drift) +fn process_user(id: Option) { + if let Some(user_id) = id { + if let Some(user) = find_user(user_id) { + // 业务逻辑深层嵌套 + process(user); + } else { + return; + } + } else { + return; + } +} + +// ✅ 使用 let-else 提前返回,减少嵌套 +fn process_user(id: Option) { + let Some(user_id) = id else { return }; + let Some(user) = find_user(user_id) else { return }; + + // 业务逻辑在顶层 + process(user); +} +``` + +### 派生 Default 与 Smart Constructors + +```rust +// ❌ 手动实现 Default 或使用 new() 但参数过多 +struct Config { + timeout: u64, + retries: u32, + verbose: bool, +} + +impl Config { + fn new(timeout: u64, retries: u32, verbose: bool) -> Self { + Self { timeout, retries, verbose } + } +} + +// ✅ 派生 Default 并提供构建器风格的方法 +#[derive(Default)] +struct Config { + timeout: u64, + retries: u32, + verbose: bool, +} + +impl Config { + // 覆盖默认值的方法 + fn with_timeout(mut self, timeout: u64) -> Self { + self.timeout = timeout; + self + } +} + +// 使用 +let config = Config::default().with_timeout(5000); +``` + +--- + ## 所有权与借用 ### 避免不必要的 clone() @@ -188,7 +256,29 @@ pub fn fast_copy(src: &[u8], dst: &mut [u8]) { --- -## 异步代码 +## 异步代码与可观测性 + +### Tracing vs Println + +```rust +// ❌ 在 async 代码中使用 println! 或 log crate +// 无法关联同一请求的不同日志,缺乏结构化信息 +async fn bad_log(user_id: &str) { + println!("Processing user {}", user_id); + db.query().await; + println!("Done"); +} + +// ✅ 使用 tracing crate +// 自动携带上下文(span),支持结构化日志,兼容 OpenTelemetry +#[tracing::instrument(skip(data))] +async fn good_log(user_id: &str, data: Data) { + tracing::info!("Processing user"); // 自动包含 user_id + if let Err(e) = db.query().await { + tracing::error!(error = ?e, "Database query failed"); + } +} +``` ### 避免阻塞操作 @@ -242,33 +332,6 @@ async fn good_lock_tokio(mutex: &tokio::sync::Mutex) { async_operation().await; // OK: tokio Mutex 设计为可跨 await process(&guard); } - -// 💡 选择指南: -// - std::sync::Mutex:低竞争、短临界区、不跨 await -// - tokio::sync::Mutex:需要跨 await、高竞争场景 -``` - -### 异步 trait 方法 - -```rust -// ❌ async trait 方法的陷阱(旧版本) -#[async_trait] -trait BadRepository { - async fn find(&self, id: i64) -> Option; // 隐式 Box -} - -// ✅ Rust 1.75+:原生 async trait 方法 -trait Repository { - async fn find(&self, id: i64) -> Option; - - // 返回具体 Future 类型以避免 allocation - fn find_many(&self, ids: &[i64]) -> impl Future> + Send; -} - -// ✅ 对于需要 dyn 的场景 -trait DynRepository: Send + Sync { - fn find(&self, id: i64) -> Pin> + Send + '_>>; -} ``` --- @@ -362,34 +425,6 @@ async fn pinned_select() { } ``` -### 文档化取消安全性 - -```rust -/// Reads a complete message from the stream. -/// -/// # Cancel Safety -/// -/// This method is **not** cancel safe. If cancelled while reading, -/// partial data may be lost and the stream state becomes undefined. -/// Use `read_message_cancel_safe` if cancellation is expected. -async fn read_message(stream: &mut TcpStream) -> Result { - let len = stream.read_u32().await?; - let mut buffer = vec![0u8; len as usize]; - stream.read_exact(&mut buffer).await?; - Ok(Message::from_bytes(&buffer)) -} - -/// Reads a message with cancel safety. -/// -/// # Cancel Safety -/// -/// This method is cancel safe. If cancelled, any partial data -/// is preserved in the internal buffer for the next call. -async fn read_message_cancel_safe(reader: &mut BufferedReader) -> Result { - reader.read_message_buffered().await -} -``` - --- ## spawn vs await @@ -424,7 +459,10 @@ async fn good_background_spawn() { // 启动后台任务,不等待完成 tokio::spawn(async { cleanup_old_sessions().await; - log_metrics().await; + // 使用 tracing 记录错误 + if let Err(e) = log_metrics().await { + tracing::error!(error = ?e, "Failed to log metrics"); + } }); // 继续执行其他工作 @@ -457,92 +495,6 @@ async fn good_spawn_arc(data: Arc) { process(&data).await; }); } - -// ✅ 方案3:使用作用域任务(tokio-scoped 或 async-scoped) -async fn good_scoped_spawn(data: &Data) { - // 假设使用 async-scoped crate - async_scoped::scope(|s| async { - s.spawn(async { - process(data).await; // 可以借用 - }); - }).await; -} -``` - -### JoinHandle 错误处理 - -```rust -// ❌ 忽略 spawn 的错误 -async fn bad_ignore_spawn_error() { - let handle = tokio::spawn(async { - risky_operation().await - }); - let _ = handle.await; // 忽略了 panic 和错误 -} - -// ✅ 正确处理 JoinHandle 结果 -async fn good_handle_spawn_error() -> Result<()> { - let handle = tokio::spawn(async { - risky_operation().await - }); - - match handle.await { - Ok(Ok(result)) => { - // 任务成功完成 - process_result(result); - Ok(()) - } - Ok(Err(e)) => { - // 任务内部错误 - Err(e.into()) - } - Err(join_err) => { - // 任务 panic 或被取消 - if join_err.is_panic() { - error!("Task panicked: {:?}", join_err); - } - Err(anyhow!("Task failed: {}", join_err)) - } - } -} -``` - -### 结构化并发 vs spawn - -```rust -// ✅ 优先使用 join!(结构化并发) -async fn structured_concurrency() -> Result<(A, B, C)> { - // 所有任务在同一个作用域内 - // 如果任何一个失败,其他的会被取消 - tokio::try_join!( - fetch_a(), - fetch_b(), - fetch_c() - ) -} - -// ✅ 使用 spawn 时考虑任务生命周期 -struct TaskManager { - handles: Vec>, -} - -impl TaskManager { - async fn shutdown(self) { - // 优雅关闭:等待所有任务完成 - for handle in self.handles { - if let Err(e) = handle.await { - error!("Task failed during shutdown: {}", e); - } - } - } - - async fn abort_all(self) { - // 强制关闭:取消所有任务 - for handle in self.handles { - handle.abort(); - } - } -} ``` --- @@ -583,258 +535,105 @@ fn good_error() -> Result<()> { operation().context("failed to perform operation")?; Ok(()) } - -// ✅ 使用 with_context 进行懒计算 -fn good_error_lazy() -> Result<()> { - operation() - .with_context(|| format!("failed to process file: {}", filename))?; - Ok(()) -} -``` - -### 错误类型设计 - -```rust -// ✅ 使用 #[source] 保留错误链 -#[derive(Debug, thiserror::Error)] -pub enum ServiceError { - #[error("database error")] - Database(#[source] sqlx::Error), - - #[error("network error: {message}")] - Network { - message: String, - #[source] - source: reqwest::Error, - }, - - #[error("validation failed: {0}")] - Validation(String), -} - -// ✅ 为常见转换实现 From -impl From for ServiceError { - fn from(err: sqlx::Error) -> Self { - ServiceError::Database(err) - } -} ``` --- -## 性能 +## API 设计与 Typestate -### 避免不必要的 collect() +### Typestate 模式 (状态机类型安全) ```rust -// ❌ 不必要的 collect——中间分配 -fn bad_sum(items: &[i32]) -> i32 { - items.iter() - .filter(|x| **x > 0) - .collect::>() // 不必要! - .iter() - .sum() +// ❌ 运行时检查状态 +struct Order { + state: String, + items: Vec, } -// ✅ 惰性迭代 -fn good_sum(items: &[i32]) -> i32 { - items.iter().filter(|x| **x > 0).copied().sum() -} -``` - -### 字符串拼接 - -```rust -// ❌ 字符串拼接在循环中重复分配 -fn bad_concat(items: &[&str]) -> String { - let mut s = String::new(); - for item in items { - s = s + item; // 每次都重新分配! - } - s -} - -// ✅ 预分配或用 join -fn good_concat(items: &[&str]) -> String { - items.join("") -} - -// ✅ 使用 with_capacity 预分配 -fn good_concat_capacity(items: &[&str]) -> String { - let total_len: usize = items.iter().map(|s| s.len()).sum(); - let mut result = String::with_capacity(total_len); - for item in items { - result.push_str(item); - } - result -} - -// ✅ 使用 write! 宏 -use std::fmt::Write; - -fn good_concat_write(items: &[&str]) -> String { - let mut result = String::new(); - for item in items { - write!(result, "{}", item).unwrap(); - } - result -} -``` - -### 避免不必要的分配 - -```rust -// ❌ 不必要的 Vec 分配 -fn bad_check_any(items: &[Item]) -> bool { - let filtered: Vec<_> = items.iter() - .filter(|i| i.is_valid()) - .collect(); - !filtered.is_empty() -} - -// ✅ 使用迭代器方法 -fn good_check_any(items: &[Item]) -> bool { - items.iter().any(|i| i.is_valid()) -} - -// ❌ String::from 用于静态字符串 -fn bad_static() -> String { - String::from("error message") // 运行时分配 -} - -// ✅ 返回 &'static str -fn good_static() -> &'static str { - "error message" // 无分配 -} -``` - ---- - -## Trait 设计 - -### 避免过度抽象 - -```rust -// ❌ 过度抽象——不是 Java,不需要 Interface 一切 -trait Processor { fn process(&self); } -trait Handler { fn handle(&self); } -trait Manager { fn manage(&self); } // Trait 过多 - -// ✅ 只在需要多态时创建 trait -// 具体类型通常更简单、更快 -struct DataProcessor { - config: Config, -} - -impl DataProcessor { - fn process(&self, data: &Data) -> Result { - // 直接实现 +impl Order { + fn pay(&mut self) { + if self.state != "created" { + panic!("Cannot pay order in state {}", self.state); + } + self.state = "paid".to_string(); } } -``` -### Trait 对象 vs 泛型 - -```rust -// ❌ 不必要的 trait 对象(动态分发) -fn bad_process(handler: &dyn Handler) { - handler.handle(); // 虚表调用 +// ✅ 编译时检查状态 (Typestate) +struct Order { + items: Vec, + state: std::marker::PhantomData, } -// ✅ 使用泛型(静态分发,可内联) -fn good_process(handler: &H) { - handler.handle(); // 可能被内联 +struct Created; +struct Paid; +struct Shipped; + +impl Order { + fn pay(self) -> Order { + // 执行支付逻辑 + Order { + items: self.items, + state: std::marker::PhantomData, + } + } } -// ✅ trait 对象适用场景:异构集合 -fn store_handlers(handlers: Vec>) { - // 需要存储不同类型的 handlers +impl Order { + fn ship(self) -> Order { + // 执行发货逻辑 + Order { + items: self.items, + state: std::marker::PhantomData, + } + } } -// ✅ 使用 impl Trait 返回类型 -fn create_handler() -> impl Handler { - ConcreteHandler::new() -} +// 无法对 Created 状态的订单调用 ship(): +// let order = Order::::new(); +// order.ship(); // 编译错误! ``` --- ## Rust Review Checklist -### 编译器不能捕获的问题 +### 现代 Rust 习语 +- [ ] 使用 `let-else` 减少嵌套 +- [ ] 使用 `#[derive(Default)]` 而非手动实现 +- [ ] 优先使用标准库/生态系统中的既定 trait (From/TryFrom) -**业务逻辑正确性** -- [ ] 边界条件处理正确 -- [ ] 状态机转换完整 -- [ ] 并发场景下的竞态条件 - -**API 设计** -- [ ] 公共 API 难以误用 -- [ ] 类型签名清晰表达意图 -- [ ] 错误类型粒度合适 +### 可观测性 (Observability) +- [ ] Async 代码使用 `tracing` 而非 `println!` 或 `log` +- [ ] `tracing::instrument` 用于关键业务方法 +- [ ] 错误日志包含上下文和错误链 (`error = ?e`) ### 所有权与借用 - - [ ] clone() 是有意为之,文档说明了原因 - [ ] Arc> 真的需要共享状态吗? -- [ ] RefCell 的使用有正当理由 - [ ] 生命周期不过度复杂 - [ ] 考虑使用 Cow 避免不必要的分配 ### Unsafe 代码(最重要) - - [ ] 每个 unsafe 块有 SAFETY 注释 - [ ] unsafe fn 有 # Safety 文档节 - [ ] 解释了为什么是安全的,不只是做什么 -- [ ] 列出了必须维护的不变量 - [ ] unsafe 边界尽可能小 -- [ ] 考虑过是否有 safe 替代方案 ### 异步/并发 - - [ ] 没有在 async 中阻塞(std::fs、thread::sleep) - [ ] 没有跨 .await 持有 std::sync 锁 - [ ] spawn 的任务满足 'static -- [ ] 锁的获取顺序一致 -- [ ] Channel 缓冲区大小合理 - -### 取消安全性 - -- [ ] select! 中的 Future 是取消安全的 - [ ] 文档化了 async 函数的取消安全性 -- [ ] 取消不会导致数据丢失或不一致状态 -- [ ] 使用 tokio::pin! 正确处理需要重用的 Future - -### spawn vs await - -- [ ] spawn 只用于真正需要并行的场景 -- [ ] 简单操作直接 await,不要 spawn -- [ ] spawn 的 JoinHandle 结果被正确处理 -- [ ] 考虑任务的生命周期和关闭策略 -- [ ] 优先使用 join!/try_join! 进行结构化并发 +- [ ] select! 中的 Future 是取消安全的 ### 错误处理 - - [ ] 库:thiserror 定义结构化错误 - [ ] 应用:anyhow + context - [ ] 没有生产代码 unwrap/expect -- [ ] 错误消息对调试有帮助 -- [ ] must_use 返回值被处理 - [ ] 使用 #[source] 保留错误链 -### 性能 - -- [ ] 避免不必要的 collect() -- [ ] 大数据传引用 -- [ ] 字符串用 with_capacity 或 write! -- [ ] impl Trait vs Box 选择合理 -- [ ] 热路径避免分配 -- [ ] 考虑使用 Cow 减少克隆 - -### 代码质量 - -- [ ] cargo clippy 零警告 -- [ ] cargo fmt 格式化 -- [ ] 文档注释完整 -- [ ] 测试覆盖边界条件 -- [ ] 公共 API 有文档示例 +### API 设计 +- [ ] 使用 Typestate 模式将运行时错误转换为编译时错误 +- [ ] 公共 API 难以误用 +- [ ] 类型签名清晰表达意图