Rust-inspired implementations of Result<T, E>, Option<T> and Iter<T> data types for Python.
Project description
rusty
Rust-inspired Result<T, E>, Option<T>, and Iter<T> types for Python.
rusty is a single, dependency-free module that brings Rust's most useful
data-modeling patterns to Python: explicit error contracts alongside Python's
exception mechanism, optional values instead of ambiguous None checks, and
lazy, chainable iterators instead of nested comprehensions.
Why rusty?
- Explicit error contracts. A
Result-returning API makes modeled failure paths visible in its signature. Callers explicitly choose whether to propagate, transform, recover from, or unwrap the result. - Composable by design.
map,and_then,or_else,filter, and friends let you build pipelines without intermediateif/elsechains. - Familiar to Rust developers. Method names and core workflows closely
follow
std::result::Result,std::option::Option, andIterator, with Python-friendly adaptations. - Zero dependencies, one file. Drop
rusty.pyinto any project running Python 3.12+. - Fully tested. The test suite covers all library features.
Installation
rusty has no runtime dependencies. Its PyPI distribution is named
rustypie and can be installed with pip:
pip install rustypie
The installed module is imported as rusty.
Alternatively, since the entire library is a single file, you can simply copy
rusty.py into your project.
Table of Contents
- Result: Ok / Err
- AsyncResult
- Option
- Iter
- Collecting Result Values
- Decorators
- Quick Reference
- Testing
- License
Result: Ok / Err
Result[T, E] is a Union[Ok[T], Err[E]] representing either a success value
or an error value as a visible return-value contract.
from rusty import Err, Ok, Result
def divide(a: int, b: int) -> Result[float, str]:
if b == 0:
return Err("division by zero")
return Ok(a / b)
result = divide(10, 2)
assert result.is_ok()
assert result.unwrap() == 5.0
doubled = divide(10, 2).map(lambda value: value * 2).unwrap_or(0.0)
assert doubled == 10.0
# Errors short-circuit the chain instead of raising:
failure = divide(10, 0).map(lambda value: value * 2).unwrap_or(-1.0)
assert failure == -1.0
Ok and Err support Python's structural pattern matching too:
match divide(10, 0):
case Ok(value):
print(f"got {value}")
case Err(message):
print(f"failed: {message}")
Key methods available on both Ok and Err:
| Method | Description |
|---|---|
is_ok() / is_err() |
Check the variant. |
unwrap() |
Extract the success value, or raise on Err. |
expect(msg) |
Extract the success value, or raise RuntimeError(msg) on Err. |
unwrap_or(default) / unwrap_or_else(fn) |
Extract the value or fall back. |
map(fn) / map_err(fn) |
Transform the success or error value. |
map_or(default, fn) / map_or_else(default_fn, fn) |
Transform with a fallback. |
and_then(fn) / or_else(fn) |
Chain fallible operations. |
op_and(other) / op_or(other) |
Rust's and/or combinators. |
ok() / err() |
Convert to Option[T] / Option[E]. |
as_ok() / as_err() |
Get self or None without unwrapping. |
inspect(fn) / inspect_err(fn) |
Peek at the value for side effects. |
iter() |
Get an Iter yielding zero or one item. |
unwrap() returns the value from Ok. On Err, it re-raises the wrapped value
when that value is an Exception; otherwise it raises a RuntimeError
describing the error. expect(msg) also returns the value from Ok, but always
raises RuntimeError(msg) on Err. These methods are intentional escape
hatches for callers that explicitly choose exception-based handling at a
boundary. Exceptions raised independently by user callbacks still propagate
normally.
AsyncResult
AsyncResult[T, E] wraps an Awaitable[Result[T, E]], letting you chain
map, and_then, map_err, and or_else — synchronously or with async
callbacks — before finally await-ing the resolved Result. Once the
awaitable resolves to a Result, that value is cached and returned by later
awaits.
from rusty import Ok, Result, async_result
async def fetch_value() -> Result[int, str]:
return Ok(21)
async def transformed_value() -> Result[int, str]:
return await (
async_result(fetch_value())
.map(lambda value: value * 2)
.and_then(lambda value: Ok(value + 1))
)
map, map_err, and_then, and or_else accept plain callables or ones
returning awaitables, so sync and async steps can be mixed freely in the same
chain.
AsyncResult also exposes async ok(), err(), as_ok(), as_err(),
unwrap_or_else(), and iter() for resolving into Option/Iter values.
Option
Option[T] represents an optional value (Some(value) or None) without
Python's ambiguity around whether a bare None means "absent" or "an actual
None value".
from rusty import Option
name = Option.some("Ferris").map(str.upper).unwrap_or("UNKNOWN")
assert name == "FERRIS"
missing = Option.none().map(str.upper).unwrap_or("UNKNOWN")
assert missing == "UNKNOWN"
Notable methods:
| Method | Description |
|---|---|
is_some() / is_none() |
Check the variant. |
unwrap() |
Extract the value, or raise ValueError on None. |
expect(msg) |
Extract the value, or raise ValueError(msg) on None. |
unwrap_or(default) / unwrap_or_else(fn) |
Extract with a fallback. |
map(fn), map_or(default, fn), map_or_else(default_fn, fn) |
Transform the value. |
and_then(fn) / or_else(fn) |
Chain optional-returning operations. |
op_and(other) / op_or(other) / op_xor(other) |
Combine two Option values. |
filter(predicate) |
Keep the value only if it matches a predicate. |
zip(other) / zip_with(other, fn) |
Combine two Option values. |
ok_or(err) |
Convert to Result[T, E]. |
take() |
Move the value out, leaving None behind. |
iter() |
Get an Iter yielding zero or one item. |
Option.some(value) raises ValueError if value is None — use
Option.none() (or the Option(value) constructor) to represent absence
explicitly. For an absent value, unwrap() raises a standard descriptive
ValueError, while expect(msg) raises ValueError with the supplied message.
Iter
Iter[T] wraps any Iterable[T] with lazy, chainable combinators inspired by
Rust's Iterator trait. Nothing is evaluated until a terminal method (like
collect(), fold(), or for_each()) consumes the iterator.
from rusty import into_iter
values = (
into_iter([1, 2, 3, 4, 5, 6])
.filter(lambda value: value % 2 == 0)
.map(lambda value: value * 10)
.collect()
)
assert values == [20, 40, 60]
Transformations return a new lazy Iter:
| Method | Description |
|---|---|
map(fn) / filter(predicate) / filter_map(fn) |
Transform or prune elements. |
flat_map(fn) / flatten() |
Flatten one level of Iterable, Result, or Option. |
take(n) / take_while(predicate) |
Limit the front of the iterator. |
skip(n) / skip_while(predicate) |
Drop from the front of the iterator. |
chain(other) / zip(other) |
Combine with another iterable. |
enumerate() |
Pair each item with its index. |
windows(size) |
Yield overlapping tuples of size (like slice::windows). |
inspect(fn) |
Run a side effect as each item is consumed, yielding it unchanged. |
Terminal methods consume the iterator and return a concrete result:
| Method | Description |
|---|---|
collect() / collect_set() / collect_tuple() |
Materialize into a list, set, or tuple. |
fold(init, fn) / reduce(fn) |
Accumulate into a single value. |
all(predicate) / any(predicate) |
Boolean tests over all elements. |
find(predicate) / position(predicate) / rposition(predicate) |
Locate a matching element. |
count() / last() / nth(n) / next() |
Positional access. |
partition(predicate) |
Split into (matched, unmatched) lists. |
for_each(fn) |
Run a side effect per item, returning None. |
flatten() and flat_map() understand Ok, Err, and Option in addition
to plain iterables, so you can flatten a stream of Result/Option values
directly:
from rusty import Err, Iter, Ok
assert Iter([Ok(1), Err("skip me"), Ok(3)]).flatten().collect() == [1, 3]
Collecting Result Values
collect_ok, collect_ok_set, and collect_ok_tuple turn an
Iter[Result[T, E]] (or any iterable of Result-like objects) into a single
Result. They short-circuit and return the first Err encountered, or an
Ok containing all successfully unwrapped values:
from rusty import Err, Iter, Ok, collect_ok, collect_ok_set, collect_ok_tuple
values = [Ok(1), Ok(2), Ok(3)]
assert collect_ok(Iter(values)) == Ok([1, 2, 3])
assert collect_ok_set(Iter(values)) == Ok({1, 2, 3})
assert collect_ok_tuple(Iter(values)) == Ok((1, 2, 3))
assert collect_ok(Iter([Ok(1), Err("invalid"), Ok(3)])) == Err("invalid")
Iteration stops as soon as an Err is found, so any remaining items are
never evaluated.
Decorators
Wrap existing exception-raising or None-returning functions (or methods)
without rewriting their bodies:
| Decorator | Use for | Wraps into |
|---|---|---|
@resultify |
Functions that may raise an exception. | Result[T, Exception] |
@resultify_method |
Instance methods that may raise an exception. | Result[T, Exception] |
@optionable |
Functions that may return None. |
Option[T] |
@optionable_method |
Instance methods that may return None. |
Option[T] |
from typing import Optional
from rusty import optionable, resultify
@resultify
def parse_int(value: str) -> int:
return int(value)
assert parse_int("42").map(lambda n: n * 2).unwrap() == 84
assert parse_int("nope").is_err()
@optionable
def find_user(user_id: int) -> Optional[str]:
return {1: "Ferris"}.get(user_id)
assert find_user(1).unwrap_or("unknown") == "Ferris"
assert find_user(2).unwrap_or("unknown") == "unknown"
Quick Reference
from rusty import (
AsyncResult,
Err,
Iter,
Ok,
Option,
Result,
async_result,
collect_ok,
collect_ok_set,
collect_ok_tuple,
into_iter,
optionable,
optionable_method,
resultify,
resultify_method,
)
Testing
The project uses pytest. Install the development dependencies and run the
test suite from the repository root:
python -m pip install -e ".[dev]"
python -m pytest
License
MIT License. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rustypie-0.1.0.tar.gz.
File metadata
- Download URL: rustypie-0.1.0.tar.gz
- Upload date:
- Size: 21.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff11001e397b26446f3d855daf93629abf22dc2ce075cd317a2652f6c65ca9c8
|
|
| MD5 |
f8b3a34789ed7c044ec68f5e3b74663c
|
|
| BLAKE2b-256 |
d9f25f748ac6e7062728dc81563a29a59b716cdba59bcee69a6c79ca83fa4790
|
Provenance
The following attestation bundles were made for rustypie-0.1.0.tar.gz:
Publisher:
release.yml on dshein-alt/rusty
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rustypie-0.1.0.tar.gz -
Subject digest:
ff11001e397b26446f3d855daf93629abf22dc2ce075cd317a2652f6c65ca9c8 - Sigstore transparency entry: 2136438212
- Sigstore integration time:
-
Permalink:
dshein-alt/rusty@42c5185737870338f48639d14c4cb3c1b50973cb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/dshein-alt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@42c5185737870338f48639d14c4cb3c1b50973cb -
Trigger Event:
push
-
Statement type:
File details
Details for the file rustypie-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rustypie-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83765bf5c63d0eac5b5d9cc2592656d8c774c97e630889b596b38dd2a34fb2d4
|
|
| MD5 |
7bfa7a0ecc7ff7eb3a22255d38af53bc
|
|
| BLAKE2b-256 |
fe2bd92f8ac760fc6ac3f45a37664337ff612b4ec26e3df675fa766ff85fce25
|
Provenance
The following attestation bundles were made for rustypie-0.1.0-py3-none-any.whl:
Publisher:
release.yml on dshein-alt/rusty
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rustypie-0.1.0-py3-none-any.whl -
Subject digest:
83765bf5c63d0eac5b5d9cc2592656d8c774c97e630889b596b38dd2a34fb2d4 - Sigstore transparency entry: 2136438323
- Sigstore integration time:
-
Permalink:
dshein-alt/rusty@42c5185737870338f48639d14c4cb3c1b50973cb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/dshein-alt
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@42c5185737870338f48639d14c4cb3c1b50973cb -
Trigger Event:
push
-
Statement type: