Skip to content

Commit 13e55ed

Browse files
authored
results: collections integration (#179)
This set of helpers allows treating Result and Opt as collections of 0 or 1 item, allowing iterating over them and checking "membership" - such integration is useful in generic code which can then be generalised to handle more complex cases - the integration is most useful with Opt. One design tradeoff here is the "explicitness" of `items` vs `values` for `Result` - technically error and value are "equal" and therefore we shouldn't give preference to the value, but there exists a convenience argument to treat the value as the "default" and therefore define `items` / `contains` for `Result` as well - this PR chooses the more conservative and explicit approach - a more liberal version can easily be added later should motivating examples emerge.
1 parent 000eeb1 commit 13e55ed

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

stew/results.nim

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,3 +1076,49 @@ template `?`*[T, E](self: Result[T, E]): auto =
10761076

10771077
when not(T is void):
10781078
v.vResultPrivate
1079+
1080+
# Collection integration
1081+
1082+
iterator values*[T, E](self: Result[T, E]): T =
1083+
## Iterate over a Result as a 0/1-item collection, returning its value if set
1084+
if self.oResultPrivate:
1085+
yield self.vResultPrivate
1086+
1087+
iterator errors*[T, E](self: Result[T, E]): E =
1088+
## Iterate over a Result as a 0/1-item collection, returning its error if set
1089+
if not self.oResultPrivate:
1090+
yield self.eResultPrivate
1091+
1092+
iterator items*[T](self: Opt[T]): T =
1093+
## Iterate over an Opt as a 0/1-item collection, returning its value if set
1094+
if self.oResultPrivate:
1095+
yield self.vResultPrivate
1096+
1097+
iterator mvalues*[T, E](self: var Result[T, E]): var T =
1098+
if self.oResultPrivate:
1099+
yield self.vResultPrivate
1100+
1101+
iterator merrors*[T, E](self: var Result[T, E]): var E =
1102+
if not self.oResultPrivate:
1103+
yield self.eResultPrivate
1104+
1105+
iterator mitems*[T](self: var Opt[T]): var T =
1106+
if self.oResultPrivate:
1107+
yield self.vResultPrivate
1108+
1109+
func containsValue*(self: Result, v: auto): bool =
1110+
## Return true iff the given result is set to a value that equals `v`
1111+
self.oResultPrivate and self.vResultPrivate == v
1112+
1113+
func containsError*(self: Result, e: auto): bool =
1114+
## Return true iff the given result is set to an error that equals `e`
1115+
not self.oResultPrivate and self.eResultPrivate == e
1116+
1117+
func contains*(self: Opt, v: auto): bool =
1118+
## Return true iff the given `Opt` is set to a value that equals `v` - can
1119+
## also be used in the "infix" `in` form:
1120+
##
1121+
## ```nim
1122+
## assert "value" in Opt.some("value")
1123+
## ```
1124+
self.oResultPrivate and self.vResultPrivate == v

tests/test_results.nim

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,37 @@ block:
199199
doAssert rOk.filter(proc(x: int): auto = Result[void, string].err("filter")).error == "filter"
200200
doAssert rErr.filter(proc(x: int): auto = Result[void, string].err("filter")) == rErr
201201

202+
# Collections
203+
block:
204+
var i = 0
205+
for v in rOk.values:
206+
doAssert v == rOk.value()
207+
i += 1
208+
doAssert i == 1
209+
210+
for v in rOk.errors:
211+
raiseAssert "not an error"
212+
213+
doAssert rOk.containsValue(rOk.value())
214+
doAssert not rOk.containsValue(rOk.value() + 1)
215+
216+
doAssert not rOk.containsError("test")
217+
218+
block:
219+
var i = 0
220+
for v in rErr.values:
221+
raiseAssert "not a value"
222+
223+
for v in rErr.errors:
224+
doAssert v == rErr.error()
225+
i += 1
226+
doAssert i == 1
227+
228+
doAssert rErr.containsError(rErr.error())
229+
doAssert not rErr.containsError(rErr.error() & "X")
230+
231+
doAssert not rErr.containsValue(42)
232+
202233
# Exception conversions - toException must not be inside a block
203234
type
204235
AnEnum = enum
@@ -381,6 +412,17 @@ block: # Result[T, void] aka `Opt`
381412
doAssert oOk.orErr("error").value() == oOk.get()
382413
doAssert oErr.orErr("error").error() == "error"
383414

415+
# Collections
416+
block:
417+
var i = 0
418+
for v in oOk:
419+
doAssert v == oOk.value()
420+
i += 1
421+
doAssert i == 1
422+
423+
doAssert oOk.value() in oOk
424+
doAssert oOk.value() + 1 notin oOk
425+
384426
block: # `cstring` dangling reference protection
385427
type CSRes = Result[void, cstring]
386428

0 commit comments

Comments
 (0)