Result[T]
The type Result[T]
mimicks Result<T,E>
Rust idiom. It is a union type that is either a value of the type T
or an error of the type error
.
The main use of Result[T]
is to avoid typing error checks.
func ReadTextFile(fname string) (string, error) {
f, err := os.Open(fname)
if err != nil {
return "", err
}
buf := bytes.Buffer{}
_, err = io.Copy(&buf, f)
if err != nil {
return "", err
}
return buf.String(), nil
}
Here is how this function could be implemented using Result style.
import "github.com/pakuula/go-rusty/result"
func ReadTextFileR(fname string) (res result.Result[string]) {
defer result.Catch(&res)
f := result.Wrap(os.Open(fname)).Must()
defer f.Close()
buf := bytes.Buffer{}
result.Must(io.Copy(&buf, f))
return result.Val(buf.String())
}
The calls to result.Wrap[T]
convert the pair of velues (T, error)
into a Result[T]
object. Method
Must()
panics with an error if the object is not a value, while defer result.Catch(&res)
catches the error and writes is to the returned result res
.
The function result.Must(T, error)
is a shorthand for result.Wrap(T, error).Must()
.
The constructor result.Val[T](value T)
builds a Result[T]
with the given value
.
More about Result[T]
Option[T]
The type Option[T]
mimicks Option<T>
Rust idiom. It is a union type that is either a value of the type T
or None
.
The main use of Option[T]
is to provide flag if the operation failed, without details.
func AddKeyValues(m map[string]int, key1, key2 string) (int, bool) {
v1, ok1 := m[key1]
v2, ok2 := m[key2]
if ok1 && ok2 {
return (v1 + v2), true
} else {
return 0, false
}
}
Here is how this function could be implemented using Option style.
import "github.com/pakuula/go-rusty/option"
func AddKeyValuesOpt(m map[string]int, key1, key2 string) (res option.Option[int]) {
defer option.Catch(&res)
v1 := option.MapGet(m, key1).Must()
v2 := option.MapGet(m, key2).Must()
return option.Some(v1 + v2)
}
The function option.MapGet
return an Option[T]
object that is either the value from the map or None
.
Must()
panics with an error if the object is None
, while defer option.Catch(&res)
catches missing
values and writes None
to the returned option res
.
The constructor Option.Some[T](value T)
builds a Option[T]
with the given value.
More about Option[T]
If expression
Rust has if
expression:
let y = if 12 * 15 > 150 {
"Bigger"
} else {
"Smaller"
};
The function expr.If
mimics this semantics:
y := expr.If(12 * 15 > 150 , "Bigger", "Smaller")
It is a convenient shorthand for the traditional go syntax
var y string
if 12 * 15 > 150 {
y = "Bigger"
} else {
y = "Smaller"
}
Lazy expression
The function expr.If
exaluates both branches of the if-then-else
expression. It is undesirable when the
branches have side effects:
var body []byte
var err error
if FileExists(configFile) {
body, err = os.ReadFile(configFile)
} else {
body, err = json.Marshal(DefaultConfig())
}
The plain expr.If
can't be used to replace the if
statement. Use expr.IfLazy
or expr.IfLazyE
instead:
body, err := expr.LazyE(
FileExists(configFile),
func () ([]byte, error) { os.ReadFile(configFile) }
func () ([]byte, error) { json.Marshal(DefaultConfig()) }
)
The function IfLazyE
delays branch evaluation and executes it only when needed.