본문으로 건너뛰기
cccskills
무료GitHub에 공개

rust

Canonical rules for writing and reviewing Rust code in Shift's crates. Use whenever you add or change `.rs` code, especially error handling (`ok_or`, `ok_or_else`, `map_err`, `?`, `CoreError`, `StoreError`, `SlugError`, `BridgeError`), entity lookups by id, checked arithmetic and integer conversions, or helpers that several call sites repeat. Load `rustdoc` as well when the change adds or edits doc comments.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(1)

  • SKILL.md7.7 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

/rust — How Rust is written in Shift

Shift's crates had the same failure built by hand at hundreds of sites: .ok_or_else(|| CoreError::GlyphNotFound(glyph_id.clone()))?, .map_err(|_| SlugError::LengthOverflow)?, StoreError::MissingEntity { kind, id } struct literals. Every copy drifted a little: different kind strings, eager ok_or cloning ids on the success path, lookups cloning an id just to pass it by value. The rules below keep a failure defined once and named at the call site.

The rule

A failure that recurs for the same reason is constructed in exactly one place. Call sites say what they need (require_glyph, or_overflow, or_missing), not how to build the error.

ok_or_else with a site-specific error is fine. A message that only makes sense at one site, such as InvalidLibValue("missing value") in a parser, belongs inline. The second time the same error appears for the same reason, extract it.

Error helpers that already exist

Use these before writing an error by hand:

FailureHelperCrate
Entity missing from a Fontfont.require_glyph(&id)?, require_layer, require_layer_mut, require_layer_owner, require_source, require_axisshift-font (ir/font.rs)
Entity missing from any other lookuplookup.require(&id)? (Require + EntityRef; the id's type picks the *NotFound variant)shift-font (error.rs)
Contour, point, or anchor missing from a layerlayer.require_contour_mut(id)?, require_point_mut, require_point_contour, require_anchor_mutshift-font (layer_edit.rs)
Length or offset overflowa.checked_add(b).or_overflow()?, u32::try_from(n).or_overflow()?, length::to_u32, length::ensure_total, length::withinshift-slug (length.rs)
Row missing from the storelookup.or_missing("glyph", &id)?, order_index(conn, table, kind, &id)?shift-store (error.rs, change_set.rs)
Retained-source index out of rangeidentity.glyph_id(index)?, glyph_index, axis_id, source_idshift-bridge (SourceIdentity)

? converts between crate errors through the existing From impls (SlugError into AuthoredSlugError, BridgeError and the backends error; CoreError into StoreError). Do not name another crate's error variant at a call site, as in shift_slug::SlugError::LengthOverflow; call that crate's helper and let ? convert.

Adding a helper

When an error pattern repeats and no helper fits, add one next to the data it describes:

  • Lookups on an owner (Font, GlyphLayer, SourceIdentity): add a require_* method beside the Option lookup. It takes the id by reference and returns Result<&T, _>.
  • One error for many value types (all ids, all checked operations): add an extension trait on Option<T> or Result<T, E> whose method names the failure (require, or_overflow, or_missing), plus one impl per input type. A macro like entity_ref! keeps one line per variant.
  • A repeated query or computation that ends in the error (the same SQL with .optional()?): extract the whole operation as one function, not just its error.
  • Only one crate needs it: keep it pub(crate). Export it only when another crate would otherwise build the same error.

Every new helper gets Rustdoc with an # Errors section (see rustdoc) and a unit test of the failure it produces.

Error types

A failure that callers may handle, test, or wrap is a type, not text.

  • A function that can fail for more than one reason returns a thiserror enum with one variant per reason. Never return Result<_, String> or add a { message: String } variant to carry another error.
  • Wrap another module's error with #[from] or #[source]; never .map_err(|error| error.to_string()). Include {0} or {source} in the wrapping message when callers surface errors through to_string(), so the detail survives.
  • Convert to text only at a boundary that requires it: a NAPI or MCP message, or a foreign API such as fontir's Error::InvalidEntry.
  • A public error type cannot hold a private one. Export a wrapped error type alongside the public error that contains it.
  • When new code calls an existing function that returns String errors, type that function's error first instead of wrapping the string again. Inherited stringly errors spread through every caller that adapts to them.

Lookups and ids

  • Lookups take ids by reference: fn glyph(&self, id: &GlyphId). Ids are String newtypes; a by-value parameter forces every caller that keeps its id to clone it. Take an id by value only when the function stores or returns it.
  • Never pass &id.clone(); borrow the id you have.
  • Never clone an id only to build an error that is probably not returned. Use ok_or_else or the helpers above, which format the id only on failure. ok_or(CoreError::X(id.clone())) clones on every call, including successful ones.
  • Option lookups (font.glyph(id)) are for "absent is a normal answer". Use require_* when absence is an error for this caller.

Arithmetic and conversions

  • Lengths, offsets, and counts that reach packed buffers use checked arithmetic with .or_overflow()?. Keep the checked_add/checked_mul visible at the call site.
  • Integer narrowing uses try_from with .or_overflow()? or length::to_u32. Never use as for a narrowing conversion that can lose data.
  • Bit-packed fields with a limit below their integer type go through length::within(value, MASK)?.

Rules the editor needs

When the editor needs a shift-font rule synchronously, write it as a plain-data function in shift-font that native callers use, and export that function from crates/shift-wasm. Don't write a second Rust version for WebAssembly. The export only converts numbers to and from shift-font types; "Choosing a route" in crates/shift-wasm/docs/DOCS.md says what belongs there and what may stay in TypeScript.

Review checklist

  • No CoreError::*NotFound, SlugError::LengthOverflow, or StoreError::MissingEntity built inline where a helper above applies.
  • No other crate's error variant named at a call site.
  • No id cloned on a lookup's success path just for its error.
  • No new Result<_, String>, { message: String } error variant, or .to_string() of an error before a text boundary.
  • Code that calls a function with String errors types that error rather than wrapping the string.
  • Any error built at two or more sites for the same reason has one helper, with Rustdoc and a test.
  • cargo fmt, cargo clippy for the affected crates, and their tests pass.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

commit

무료

Canonical rules for writing git commits in the Shift codebase. Use whenever the user asks to commit, stage and commit, create a pull request that requires commits, or draft a commit message. Enforces Conventional Commits, release-note quality, concise subjects, and logical commit boundaries.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

shift-editor/shift3592026년 10월 20일 업데이트

dead-code

무료

Find and remove dead code (unused files, exports, class members) using Knip as a candidate generator, then verify each candidate through AST-level analysis and interface tracing before removing anything. Use when the user asks to clean up unused code, find dead code, or reduce the codebase.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

shift-editor/shift3592026년 10월 20일 업데이트

docs

무료

Update or create DOCS.md files for Shift subsystems. Use this skill whenever the user asks to update docs, refresh documentation, create a DOCS.md, write module documentation, or says "update docs for X". Also trigger after completing a large feature when Claude.md says to update docs — check if any DOCS.md in the affected subsystem needs refreshing.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

shift-editor/shift3592026년 10월 20일 업데이트

Adversarially fact-check DOCS.md files against the actual source code, verifying every concrete claim rather than trusting structure checks. Use when the user asks to audit docs, verify documentation accuracy, check whether docs are still true, or on a scheduled documentation review. This is the semantic layer the mechanical checkers cannot cover.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

shift-editor/shift3592026년 10월 20일 업데이트

issue

무료

Canonical rules for finding, creating, and updating Shift GitHub issues. Use whenever the user asks to file, create, open, update, triage, or search for an issue, or when substantial work needs an issue before a pull request. Prevents duplicates and defines acceptance criteria and pull-request closure semantics.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

shift-editor/shift3592026년 10월 20일 업데이트

jsdoc

무료

Add or revise source-level JSDoc for Shift APIs. Use this skill before writing or editing documentation comments for exported classes, methods, constructors, domain data structures, render frames, reactive state, or any API where caller intent, side effects, lifetime, ownership, or nullability are easy to misunderstand.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

shift-editor/shift3592026년 10월 20일 업데이트

shift-editor의 스킬 모두 보기

이 스킬의 문제 신고