Rust

License

Use the MIT license.

Development

  • Install Rust via rustup rather than via Homebrew.

Preferences

Code style

Macros

Prefer functions, but use macros if you need:

  • Variadic arguments, like println! or vec!.

  • Code generation at the item level, like creating structs.

  • Code generation at the expression level, like accessing struct fields dynamically.

See also

Debugging:

Crates:

Guard clauses

Code with too much indentation is hard to read. One option is to use guard clauses. For example:

let Some(inner_value) = outer_value else {
    return
};
let inner_value = match outer_value {
    Ok(o) => o,
    Err(e) => return e,
};

Continuous integration

Create a .github/workflows/ci.yml file. As a base, use:

name: CI
on: [push, pull_request]
jobs:
  build:
    if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [macos-latest, windows-latest, ubuntu-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.os }}
      # https://github.com/Swatinem/rust-cache/issues/93#issuecomment-1321064841
      - run: cargo update
      # https://github.com/taiki-e/cargo-llvm-cov#on-github-actions
      - uses: taiki-e/install-action@cargo-llvm-cov
      - run: cargo llvm-cov --lcov --output-path lcov.info
      - if: matrix.os == 'ubuntu-latest'
        uses: coverallsapp/github-action@master
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
          path-to-lcov: lcov.info

Release process

  1. Ensure that you are on an up-to-date main branch:

    git checkout main
    git pull --rebase
    
  2. Ensure that the package is ready for release:

    • All tests pass on continuous integration

    • The version number is correct in Cargo.toml

    • The changelog is up-to-date and dated

  3. Tag the release, replacing x.y.z twice:

    git tag -a x.y.z -m 'x.y.z release.'
    
  4. Push the release:

    git push --follow-tags
    
  5. Edit the GitHub release that is created by GitHub Actions, to add the description value from Cargo.toml followed by the relevant section of the changelog.

  6. If the software has a formula in our Homebrew tap, update the url and sha256 values. For example, from the homebrew-tap directory, after updating the url values, prepare the sha256 values for the ocdscardinal formula with:

    grep --only-matching -E 'https://.+zip' Formula/ocdscardinal.rb | xargs -I{} sh -c 'curl -sSL {} | shasum -a 256'
    

    Then, push the changes.

  7. Publish the crate:

    cargo publish
    
  8. Announce on the discussion group if relevant

Development

Troubleshooting

If you’re getting confusing compile errors, especially any involving type annotations, check that:

  • You wrote enough code. If you produce results that you don’t use, the compiler still wants to determine their definite type. Adding more code to give the compiler a hint can spare adding optional type annotations.

  • Your annotations are correct. If you change your code but don’t change your annotations, the compiler might report errors that are distantly related to the misannotation.

  • You duck type using trait objects: for example, Box<dyn Read> to use std::io:stdin() and File::open(file).unwrap() interchangeably. The compiler can’t determine which traits are relevant across the two types.

If errors relate to ownership, try:

  • Using Arc<Mutex<T>>, as discussed in sections 16.3 and 20.2 of The Rust Programming Language.

  • Using Option with take(), as discussed in sections 17.3 and 20.3 of The Rust Programming Language.

To reduce the number of allocations, try:

Learning

Rust has no:

Introductions

Tip

Use Rust Playground to test code snippets.

Topics

Reference