← → or space · progress saves for Continue on the roadmap
Goal
Return Result values from validators and parsers so callers handle errors uniformly.
Step 1 - Result type
class Result<T> {
final T? value;
final String? error;
const Result._({this.value, this.error});
const Result.ok(T value) : this._(value: value, error: null);
const Result.err(String error) : this._(value: null, error: error);
bool get isOk => error == null;
}Step 2 - Email as Result<String>
Result<String> validateEmail(String? raw) {
if (raw == null || raw.trim().isEmpty) {
return const Result.err('email required');
}
final email = raw.trim();
if (!email.contains('@')) {
return const Result.err('email invalid');
}
return Result.ok(email);
}Step 3 - Parse age
Result<int> parseAge(String? raw) {
if (raw == null || raw.trim().isEmpty) {
return const Result.err('age required');
}
final n = int.tryParse(raw.trim());
if (n == null) return const Result.err('age not a number');
if (n < 0 || n > 130) return const Result.err('age out of range');
return Result.ok(n);
}
void main() {
final e = validateEmail('a@b.com');
final a = parseAge('20');
if (e.isOk) print(e.value);
if (a.isOk) print(a.value);
}Practice tasks
- Add
Result<void>orResult<Unit>-style success (useResult<bool>withtrueor a tinysealedsuccess marker if you prefer). - Combine
Result<String>email +Result<int>age intoResult<SignUp>with a functionResult<SignUp> validateSignUp(...)that stops on first error or returns both values. - Add
extension<T> on Result<T> { Result<U> map<U>(U Function(T) f); }that propagates errors.