rust-port
Skill5 starsChanged 11 days agoBSD-3-ClauseView on GitHub
Use when porting or rewriting a C, C++, Python, Go, JavaScript, or TypeScript module in Rust that must keep the original's output. Covers the behavior inventory, tests ported first, the traps that change output (floor division, rounding, string length, ordered maps, strict parsing), a recorded comparison with the original, and PORTING.md. Not for calling a C library from Rust (use rust-native-linking).
7 other projects use a version of this.
Adopted by
Most recently taken up by
- XGENT-ai/skillstheir own version, first seen Nov 8, 2026
- promptctl/cc-candybartheir own version, first seen Nov 8, 2026
- weedmo/skillstheir own version, first seen Nov 8, 2026
- kabudu/engineering-agent-skillstheir own version, first seen Nov 1, 2026
- fedemelo/agent-configtheir own version, first seen Nov 1, 2026
- tuan3w/herdr-mobiletheir own version, first seen Nov 1, 2026
Used with
Projects with it also use rust-best-practices.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
MicrosoftSkillrust-best-practices
Guide for writing idiomatic Rust code based on Apollo GraphQL's best practices handbook. Use this skill when: (1) writing new Rust code or functions, (2) reviewing or refactoring existing Rust code, (3) deciding between borrowing vs cloning or ownership patterns, (4) implementing error handling with Result types, (5) optimizing Rust code for performance, (6) writing tests or documentation for Rust projects.
Also in 4 projects that use this
Other versions7 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 7
- morganlinton/BeautifulRust★ 1.8ksame name
- tuan3w/herdr-mobile★ 29same name
- fedemelo/agent-config★ 4same name
- kabudu/engineering-agent-skills★ 3same name
- weedmo/skills★ 1same name
- XGENT-ai/skills★ 1same name
- promptctl/cc-candybar★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository46 other files it gives its agents.
- AGENTS.mdAGENTS.md
- cargo-workflowsSkill
- ffi-error-progress-cancelSkill
- memory-modelSkill
- rust-android-buildSkill
- rust-async-internalsSkill
- rust-borrow-semanticsSkill
- rust-callback-boundsSkill
- rust-cliSkill
- rust-code-styleSkill
- rust-compiler-errorsSkill
- rust-copy-on-writeSkill
- rust-crate-architectureSkill
- rust-crate-releaseSkill
- rust-databaseSkill
- rust-debuggingSkill
- rust-disciplineSkill
- rust-embedded-no-stdSkill
- rust-event-loop-stateSkill
- rust-hot-pathSkill
- rust-ios-buildSkill
- rust-iterator-implSkill
- rust-jniSkill
- rust-lintsSkill
- rust-macrosSkill
- rust-native-linkingSkill
- rust-networkingSkill
- rust-observabilitySkill
- rust-panic-safetySkill
- rust-pattern-semanticsSkill
- rust-performanceSkill
- rust-pin-projectionSkill
- rust-reviewSkill
- rust-sanitizers-miriSkill
- rust-securitySkill
- rust-send-syncSkill
- rust-serdeSkill
- rust-swift-ffiSkill
- rust-tddSkill
- rust-test-toolsSkill
- rust-type-erasureSkill
- rust-unsafeSkill
- rust-varianceSkill
- rust-wasmSkill
- uniffi-boundarySkill
- uniffi-packaging-versioningSkill
The file
We split this skill into the rules it gives an agent
12 rules. 0 are common, 6 are distinctive, and 6 are very specific to this project.
0
Common
Most of the 7 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 16
Inventory the behavior: Read the original, its tests, and its callers.
- Distinctive: line 20
Port the tests first: Write one Rust test for each original test, and give it a name that maps back to the original.
- Distinctive: line 28
Implement against the ported tests: Keep the original algorithm unless it is wrong for the input it gets.
- Distinctive: line 33
If the original has a bug or an ambiguity, keep its behavior, record it, and let the owner decide.
- Distinctive: line 38
Run these when a Rust toolchain is present, and fix every finding before you report:
- Distinctive: line 54
Run the original once on a corpus, store each input with its output, and make a Rust test replay them.
- Very specific: line 60
Run the original on each input, and record the exact command and the original's version in PORTING.md.
- Very specific: line 144
Read references/source-languages.md when you map the constructs of one source language: memory and error handling of C and C++, Python …
- Very specific: line 154
Do not grow the API. Port the original's public surface, plus the standard traits Rust callers expect: Debug, Clone, PartialEq, Display and …
- Very specific: line 157
Do not port the source runtime.
- Very specific: line 159
Do not write unsafe or raw pointers unless the port must also expose or call a C ABI.
- Very specific: line 165
Write PORTING.md next to the code.
“Common” is measured: kept by at least 6 in 10 of the 7 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Rust Port
A port succeeds when Rust callers get the same results as the original's callers, for the inputs that the original receives. A line-by-line transliteration compiles, keeps every accident of the source language, and adds the borrow checker on top. Port the behavior. Design the Rust shape from that behavior.
The procedure
- Inventory the behavior. Read the original, its tests, and its callers. List each public function, its inputs, its outputs, and each way it fails. Add the quiet behavior, because a port goes wrong there: output order, tie breaks, empty input, whitespace and case handling, integer limits, rounding, the length of non-ASCII text, and what counts as an error.
- Port the tests first. Write one Rust test for each original test, and give it a name that maps back to the original. Run them; they fail until the port exists. When the original has no tests, record its outputs on the inventory inputs first (A recorded comparison), and make those the Rust tests: a test written from a guess about the original proves nothing.
- Design the Rust API from the inventory, not from the original's shape. Drop what exists
only because of the source language: out-parameters,
NULLreturns, error codes, global state, fixed buffers, classes that only group functions, and type checks at run time. - Implement against the ported tests. Keep the original algorithm unless it is wrong for the input it gets; record a change of algorithm in the notes.
- Compare with the original on recorded inputs (Verification).
- Write
PORTING.md(The porting notes).
If the original has a bug or an ambiguity, keep its behavior, record it, and let the owner decide. A port is not the place for a silent fix.
Verification
Run these when a Rust toolchain is present, and fix every finding before you report:
cargo test --locked
cargo clippy --all-targets --locked -- -D warnings| Claim | Check | A pass does not prove |
| The port matches the original's tests | Every ported test passes; the count equals the original's | Behavior that no original test covers |
| The port matches the original on real input | A recorded comparison (below) | Inputs outside the recorded corpus |
| The port matches a reference that a test can call in-process (a C library, an earlier Rust version) | The differential fuzz target of the rust-test-tools skill | Inputs that the fuzzer did not reach |
| Output order and formatting match | A byte comparison of whole outputs, not a parsed comparison | Locale or terminal effects of the original |
A recorded comparison
Run the original once on a corpus, store each input with its output, and make a Rust test replay them. This works for any source language, including one that the Rust test cannot call:
- Collect 30 to 100 inputs: real ones from logs or fixtures, plus the edge cases of the inventory. Add non-ASCII text, empty input, the largest and smallest numbers, and negative numbers for every division or modulo.
- Run the original on each input, and record the exact command and the original's version in
PORTING.md. - Store each input and output as a golden file pair, and add one test that runs the Rust entry
point on each input and compares bytes. The
rust-test-toolsskill owns the golden layout, the-textattribute that keeps line endings exact, and the scrubbing of volatile fields. - Regenerate an output file only by running the original again, never from the port: a blessed Rust output is no longer an oracle.
Traps that change output
Each row is a difference that a test written from the Rust side does not catch.
| Symptom in the port | Cause | Fix |
| A negative result is off by one against Python | Rust / and % truncate toward zero; Python // and % floor | A floor helper (below). div_euclid matches Python only for a positive divisor |
A .5 value rounds the other way | f64::round rounds half away from zero; Python round() rounds half to even; JavaScript Math.round rounds half up | round_ties_even for Python (Rust 1.77+); for JavaScript, ceil at an exact negative half and round otherwise (the probe below). (x + 0.5).floor() is wrong near 0.5 and above 2^52 |
| A count or a column differs on non-ASCII text | len() counts UTF-8 bytes in Rust and Go, code points in Python, UTF-16 units in JavaScript | chars().count() for Python, encode_utf16().count() for JavaScript; never slice a &str at a Python or JavaScript index |
| Output lines come in a different order | Python dict and JavaScript Map keep insertion order; HashMap does not | A Vec of pairs or indexmap::IndexMap when order is observable; BTreeMap only when the original sorts |
| Input that the original accepts is rejected | str::parse rejects surrounding whitespace and _; Python int() accepts both, C strtol skips leading space and stops at the first bad character | Trim, or write the original's rule out in one function and test it |
| A line count differs | str::lines splits on \n and \r\n only; Python splitlines() also splits on a lone \r, \x0b, \x0c, \x1c, \x85, \u2028, and more | Split on the original's separator set when the input can hold them |
| Fields appear or vanish | Python split() with no argument is split_whitespace; split(' ') keeps empty fields | Match the call, not the name |
| A debug build panics where the original wrapped | Go integers and C unsigned integers wrap; Rust panics on overflow in debug builds | wrapping_* where the original wraps by contract; checked_* where input can be large; i128 or a big-integer crate for Python int |
| Equal items swap places | sort_unstable* is not stable; Python sorted and JavaScript sort (ES2019+) are | sort_by_key or sort_by |
JavaScript sorts [10, 9, 1] as [1, 10, 9] | The default JavaScript sort compares strings | Port the comparison that the original really used, then record it as a preserved quirk |
| A large JavaScript integer loses digits | JavaScript numbers are f64, exact only up to 2^53 | f64 when the original depends on it, i64 when it was always meant as an integer; record which |
| A sort of floats does not compile, or NaN lands in a new place | f64 is not Ord | sort_by(f64::total_cmp), and record the NaN position |
The Rust-side claims that a probe can show, executed (Rust 1.99.1):
/// Floor division as Python's `//` defines it. `i64::MIN / -1` overflows in Rust, where Python
/// returns 2^63: check that pair when the input can reach it.
fn floor_div(a: i64, b: i64) -> i64 {
let q = a / b;
if a % b != 0 && ((a < 0) != (b < 0)) { q - 1 } else { q }
}
/// The remainder that pairs with `floor_div`: it takes the sign of the divisor. It corrects
/// `a % b` instead of computing `a - b * q`, which overflows near the limits.
fn floor_mod(a: i64, b: i64) -> i64 {
let r = a % b;
if r != 0 && ((r < 0) != (b < 0)) { r + b } else { r }
}
fn main() {
assert_eq!((-7 / 2, -7 % 2), (-3, -1));
assert_eq!((floor_div(-7, 2), floor_mod(-7, 2)), (-4, 1));
assert_eq!(((-7i64).div_euclid(2), 7i64.div_euclid(-2)), (-4, -3));
assert_eq!((floor_div(7, -2), floor_mod(7, -2)), (-4, -1));
assert_eq!((floor_div(i64::MIN, 3), floor_mod(i64::MIN, 3)), (-3074457345618258603, 1));
assert!(i64::MIN.checked_div(-1).is_none());
assert_eq!((2.5f64.round(), (-2.5f64).round()), (3.0, -3.0));
assert_eq!((2.5f64.round_ties_even(), 3.5f64.round_ties_even()), (2.0, 4.0));
// JavaScript Math.round: half toward positive infinity, exact for every f64.
let js_round = |x: f64| if x.fract() == -0.5 { x.ceil() } else { x.round() };
assert_eq!((js_round(-2.5), js_round(2.5), js_round(0.49999999999999994)), (-2.0, 3.0, 0.0));
assert_eq!(js_round(4503599627370497.0), 4503599627370497.0);
assert_eq!((0.49999999999999994f64 + 0.5).floor(), 1.0); // the polyfill fails here
let s = "héllo😀";
assert_eq!((s.len(), s.chars().count(), s.encode_utf16().count()), (10, 6, 7));
assert!(" 42 ".parse::<i32>().is_err());
assert!("1_000".parse::<i32>().is_err());
assert_eq!("+5".parse::<i32>(), Ok(5));
assert_eq!("a\u{2028}b\x0cc".lines().count(), 1);
assert_eq!(" a b ".split_whitespace().collect::<Vec<_>>(), ["a", "b"]);
assert_eq!(" a b ".split(' ').collect::<Vec<_>>(), ["", "a", "", "b", ""]);
assert_eq!(i32::MAX.wrapping_add(1), i32::MIN);
assert_eq!(i32::MAX.checked_add(1), None);
let mut v = [(1, 'b'), (0, 'x'), (1, 'a')];
v.sort_by_key(|&(k, _)| k);
assert_eq!(v, [(0, 'x'), (1, 'b'), (1, 'a')]);
let mut f = [2.0, f64::NAN, 1.0];
f.sort_by(f64::total_cmp);
assert_eq!(&f[..2], &[1.0, 2.0]);
assert!(f[2].is_nan());
}Read references/source-languages.md when you map the constructs
of one source language: memory and error handling of C and C++, Python classes and exceptions, Go
interfaces and goroutines, and JavaScript null, undefined, and promises.
Keep the port in proportion
- Port the inputs that the original receives. Where Rust's standard library differs from the
source runtime only on exotic input (Unicode digits, non-ASCII whitespace, unbounded integers),
record the difference in
PORTING.md. Write a compatibility shim only when the owner says such input occurs. - Do not grow the API. Port the original's public surface, plus the standard traits Rust callers
expect:
Debug,Clone,PartialEq,Displayandstd::error::Erroron the error type,FromStrfor parsed text, andDefaultwhere the original has defaults. - Do not port the source runtime. Use
str::trim,str::parse, andcharmethods; do not rewrite Python'sstrip,int(), orrepr(), or C's locale-dependentisspace. - Do not write
unsafeor raw pointers unless the port must also expose or call a C ABI. When the original is a C library that stays in use, bind it instead of porting it: a-syscrate withbuild.rsandbindgen(therust-native-linkingskill owns this, when it is installed).
The porting notes
Write PORTING.md next to the code. A reviewer checks it in a few minutes:
# Porting notes: <original name and version or commit>
## API map
| Original | Rust |
## Test map
| Original test | Rust test |
## Deliberate differences
<each behavior that now differs, the input that shows it, and why>
## Preserved quirks
<odd behavior kept on purpose, with the test that pins it>
## Verification
<the commands run and their results; the recorded-comparison command and corpus size;
the checks that were not run, and why>A difference that is not in PORTING.md is a bug in the port.
Related skills
Each applies when it is installed.
| Skill | Boundary |
rust-tdd | Characterization tests for an original that has none, and the red-green cycle |
rust-test-tools | The differential fuzz target, golden-file mechanics, and property tests |
rust-panic-safety | A port that must keep a C ABI: no panic may cross it |
rust-native-linking | Binding a C library that stays, instead of porting it (build.rs, bindgen, -sys crates) |
rust-serde | Serialized output that must match the original byte for byte (serde_json formatting, key order) |
rust-cli | A ported command line tool: argument parsing, exit codes, and the stdout and stderr contract (clap) |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

