codeberg.org/elliethepuppy/result
1.1.0 / published Jun 23, 2026 / repository
Making error and value handling one and the same. Inspired by Rust's Result<T,E> type comes the same thing: Crystal-flavored!
Result
The Result type, but for Crystal!
Installation
- Add the dependency to your
shard.yml:
dependencies:
result:
codeberg: elliethepuppy/result
version: 1.1.0
- Run
shards install
Usage
require "result"
- NOTE: this will also include the
Result(T, E)type, and theok(value)anderr(value)macros directly into your top-level scope. if you wish to avoid this, require like so:require "result/result"
A Result type signature looks like Result(T, E), where T is the type of your good value and
E is the type of your error value. It is recommended to always use different types if you are
not creating your own Ok and Err objects to give to the Result because the constructors can
become confused.
Using a given Ok or Err value, created with the corresponding macro:
good_result = ExampleResult.new(ok(2 * 43)) # => Ok(86)
bad_result = ExampleResult.new(err("huh")) # => Err("huh")
Using a Block to determine a value or error value to initialize (this is the generally best way to do it).
good_or_bad_result = Result(Float64, String).new do
random = rand(-10..10)
if random == 0
"nooooo!"
else
4 / random
end # => Ok(<Some Float64>) or Err("nooooo!")
end
Similar to the above, a Proc can be used (and is in fact how the block variant is implemented) instead of a Block.
four_six_seven = -> { if rand(0..1) % 2 == 0; 467; else; "467" }
good_or_bad_result = ExampleResult.new four_six_seven # => Ok(467) or Err("467")
Check if a value is good or bad with #ok? and #err?.
good_result = ExampleResult.new(ok(87)) # => Ok(87)
good_result.ok? # => true
good_result.err? # => false
Trying to unwrap a non-Ok value raises a ResultType::ResultError.
good_result = ExampleResult.new(ok(9300)).unwrap # => Ok(9300), Looks great!
bad_result = ExampleResult.new(err("l")).unwrap # => raises ResultType::ResultError!! Ick!!!
Of course, if you must unwrap the contained value, whether it is or isn't good, use #unwrap!.
suspect_result = ExampleResult.new(err("dangerous!")).unwrap! # => Err("dangerous!"), scary!!
You can also deliberately unwrap an error using #unwrap_err. This has the same rules as regular #unwrap, except for error values.
bad_result = ExampleResult.new(err("expected!")).unwrap_err # => Err("expected!"), not scary!!
In addition to #unwrap and #unwrap_err are #expect(message) and #expect_err(message). These latter two methods function identically to the former two, except the exception message
given on failure is the one you provide to the method. You must provide the message.
good_result = ExampleResult.new(err("string"))
.expect("Should be Int32") # => raises `ResultType::ResultError` with "Should be Int32".
bad_result = ExampleResult.new(ok(75))
.expect_err("Should be String") # => raises `ResultType::ResultError` with "Should be String".
If you want to give a default value instead of checking for Ok-ness, use #unwrap_or(value) (which can return any type, really), or #unwrap_or_else, which takes a Block or Proc that takes
the Err value's type and returns a value of the Ok value's type (not an Ok(T), but a value that is of type T).
bad_result = ExampleResult.new(err("237"))
.unwrap_or "any value can go here." # => "any value can go here"
bad_result = ExampleResult.new(err("jlft"))
.unwrap_or_else { |e|
if e.to_i?
e.to_i
else
0
end
} # => 0
# using a Proc:
to_i_proc = ->(e : String) {
if e.to_i?
e.to_i
else
0
end
}
bad_result = ExampleResult.new(err("245"))
.unwrap_or_else to_i_proc # => 245
# using a method as the Proc:
def str_to_i(str : String) : Int32
if str.to_i?
str.to_i
else
0
end
end
bad_result = ExampleResult.new(err("two"))
.unwrap_or_else ->str_to_i(String) # => 0
Development
TODO:
- Determine if having
Ok(T),Err(E), andResult(T, E)being completely separate types is ideal. - Write tests for: Comparables,
Ok(T), andErr(E).- Do
Ok(T)andErr(E)really need tests? they're quite simple, and are tested by being so tied to the Result type itself.
- Do
Contributing
- Clone it (
git clone https://codeberg.org/elliethepuppy/result.git) - Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Create a patch against the main branch (
git diff main > feature-name.patch) - Send me an email whose subject begins with
[GIT/Result](Include your feature name as well!). If you don't, I might not get it. - Wait for me to get back to you with questions and updates!
Contributors
- ellie nil - creator and maintainer
API
- ResultType
ResultTypeis a module that seeks to bring the robustness of Rust's Result<T,E> type to Crystal.