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.
This commit is contained in:
google-labs-jules[bot]
2025-12-04 15:08:58 +00:00
parent 71e1718864
commit ba2fc89371
4 changed files with 580 additions and 355 deletions

View File

@@ -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` - 统一的表单状态管理

View File

@@ -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, 性能优化, 响应式, 兼容性 |

405
reference/java.md Normal file
View File

@@ -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<Dto> result = list.stream()
.filter(...)
.map(...)
.peek(...)
.sorted(...)
.collect(...); // 难以调试
// ✅ 拆分为有意义的步骤
var filtered = list.stream().filter(...).toList();
// ...
```
### Optional 正确用法
```java
// ❌ 将 Optional 用作参数或字段(序列化问题,增加调用复杂度)
public void process(Optional<String> name) { ... }
public class User {
private Optional<String> email; // 不推荐
}
// ✅ Optional 仅用于返回值
public Optional<User> findUser(String id) { ... }
// ❌ 既然用了 Optional 还在用 isPresent() + get()
Optional<User> 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<Order> orders;
}
// 业务代码
List<User> 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<User> findAllWithOrders();
```
### 事务管理
```java
// ❌ 在 Controller 层开启事务(数据库连接占用时间过长)
// ❌ 在 private 方法上加 @TransactionalAOP 不生效)
@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<String, String> 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
- [ ] 魔法值提取为常量或枚举

View File

@@ -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<i32>) {
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<i32>) {
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<Data>) {
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<Entity>; // 隐式 Box
}
// ✅ Rust 1.75+:原生 async trait 方法
trait Repository {
async fn find(&self, id: i64) -> Option<Entity>;
// 返回具体 Future 类型以避免 allocation
fn find_many(&self, ids: &[i64]) -> impl Future<Output = Vec<Entity>> + Send;
}
// ✅ 对于需要 dyn 的场景
trait DynRepository: Send + Sync {
fn find(&self, id: i64) -> Pin<Box<dyn Future<Output = Option<Entity>> + 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<Message> {
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<Message> {
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<Data>) {
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<JoinHandle<()>>,
}
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<sqlx::Error> 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::<Vec<_>>() // 不必要!
.iter()
.sum()
// ❌ 运行时检查状态
struct Order {
state: String,
items: Vec<Item>,
}
// ✅ 惰性迭代
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<Output> {
// 直接实现
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<State> {
items: Vec<Item>,
state: std::marker::PhantomData<State>,
}
// ✅ 使用泛型(静态分发,可内联)
fn good_process<H: Handler>(handler: &H) {
handler.handle(); // 可能被内联
struct Created;
struct Paid;
struct Shipped;
impl Order<Created> {
fn pay(self) -> Order<Paid> {
// 执行支付逻辑
Order {
items: self.items,
state: std::marker::PhantomData,
}
}
}
// ✅ trait 对象适用场景:异构集合
fn store_handlers(handlers: Vec<Box<dyn Handler>>) {
// 需要存储不同类型的 handlers
impl Order<Paid> {
fn ship(self) -> Order<Shipped> {
// 执行发货逻辑
Order {
items: self.items,
state: std::marker::PhantomData,
}
}
}
// ✅ 使用 impl Trait 返回类型
fn create_handler() -> impl Handler {
ConcreteHandler::new()
}
// 无法对 Created 状态的订单调用 ship()
// let order = Order::<Created>::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<Mutex<T>> 真的需要共享状态吗?
- [ ] 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<dyn Trait> 选择合理
- [ ] 热路径避免分配
- [ ] 考虑使用 Cow 减少克隆
### 代码质量
- [ ] cargo clippy 零警告
- [ ] cargo fmt 格式化
- [ ] 文档注释完整
- [ ] 测试覆盖边界条件
- [ ] 公共 API 有文档示例
### API 设计
- [ ] 使用 Typestate 模式将运行时错误转换为编译时错误
- [ ] 公共 API 难以误用
- [ ] 类型签名清晰表达意图