Result - Krystian-L-Lis/Stage GitHub Wiki
#Guide #Results
Rust programming language offers very neat error handling patterns. If a function can fail it returns a Result type, which then needs to be unwrapped using either if-let
or match
statement. This encourages explicit error handling.
fn main() {
let input_1: &str = "1";
let input_2: &str = "1000";
let input_3: &str = "123";
if let Ok(val) = input_1.parse::<i8>() {
println!("Parsed: {}", val);
}
if let Err(err) = input_2.parse::<i8>() {
println!("Error: {:?}", err);
}
match input_3.parse::<i8>() {
Ok(val) => println!("Parsed: {}", val),
Err(err) => println!("Error: {:?}", err),
}
}
The point of Result
, although limited compared to what Rust provides, is to deliver a lightweight system for communicating what caused a function or method to fail. It introduces some indirection, but this is limited to a few functions and DINT
values. It also helps visually, or conceptually, distinguish when a method or function fails or succeeds. For example:
// Assuming the function returns Result
IF IsOk(FailableFunction()) THEN
// Success
END_IF
IF IsErr(FailableFunction()) THEN
// Failure
END_IF
CASE FailableFunction() THEN
Ok:
// Handle success
None:
// Handle absence of value
Error:
// Handle general error
Err.Ovf:
// Handle overflow error
Err.Itf0:
// Handle some specific error
ELSE
// Handle unexpected case
END_IF
This is easier to understand and maintain compared to the following:
// Assuming the function returns BOOL
IF FailableFunction() THEN
// Success ? Fail ? Neither ?
END_IF
The difference should now be clear. Hopefully, this can be built into Structured Text by default in a more advanced Rust-like format.
Why not use an Enum
? The answer is straightforward: Enum
s cannot be extended. The idea here is that the framework provides a few basic error variants, but users can define their own error variants depending on their needs.
An alias over INT
value. The idea is that any value larger than 0
means success or partial success, any value smaller than 0
means failure, and 0
itself means neither. This framework provides basic error values: Ok := 1
, Error := -1
, and None := 0
. Slightly more advanced values are provided through the Err
namespace:
Itf0 : Result := -2; // Null Interface
Str0 : Result := -3; // Empty String
ItfEq : Result := -4; // Interfaces are equal
NoMatch : Result := -5; // No match
Ptr0 : Result := -6; // Null Pointer
InvRef : Result := -7; // Invalid reference
Ovf : Result := -8; // Overflow/Out of bounds
Udf : Result := -9; // Undefined
Idx : Result := -10; // Index error
TcLib : Result := -11; // Error caused by TwinCAT libraries
InfLoop : Result := -12; // Potential infinity loop
IncArg : Result := -13; // Incorrect input argument
Init : Result := -14; // Initialization error
NotFound : Result := -15; // Item not found
InvItf : Result := -16; // Invalid interface
End : Result := -17; // End of collection
Provides a way to convert Result
values provided by the framework to String
.
Multiple result functions are available. They take a Result
value and return BOOL
if the result matches what they check for.
These functions are:
-
IsOk
- Check for success values. -
IsErr
- Check for failure values. -
IsFail
- Check for not Ok values. -
IsNone
- Check forNone
value. -
IsSome
- Check for either success or failure. -
IsEnd
- Checks for end of collection errors
The result functions take Result
as input and optionally output the same result for further processing.
IF IsErr(Failable(), nOut => nResult) THEN
DebugStr(ResultToStr(nResult));
END_IF
Note:
None
should be used for outcomes that are not covered by the programmer.
To carry the result value into the CASE
statement, use the following syntax:
CASE (nResult := Failable()) OF
// Variants
END_CASE
< Previous | Home | Next >