Add docs and tests for switch.
This commit is contained in:
parent
b0c66eb5e5
commit
83c7c101d1
@ -117,13 +117,14 @@ The Rhai Scripting Language
|
||||
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
|
||||
8. [Usage Patterns](patterns/index.md)
|
||||
1. [Object-Oriented Programming (OOP)](patterns/oop.md)
|
||||
2. [Loadable Configuration](patterns/config.md)
|
||||
3. [Control Layer](patterns/control.md)
|
||||
4. [Singleton Command](patterns/singleton.md)
|
||||
5. [Multi-Layer Functions](patterns/multi-layer.md)
|
||||
6. [One Engine Instance Per Call](patterns/parallel.md)
|
||||
7. [Scriptable Event Handler with State](patterns/events.md)
|
||||
8. [Dynamic Constants Provider](patterns/dynamic-const.md)
|
||||
2. [Working With Rust Enums](patterns/enums.md)
|
||||
3. [Loadable Configuration](patterns/config.md)
|
||||
4. [Control Layer](patterns/control.md)
|
||||
5. [Singleton Command](patterns/singleton.md)
|
||||
6. [Multi-Layer Functions](patterns/multi-layer.md)
|
||||
7. [One Engine Instance Per Call](patterns/parallel.md)
|
||||
8. [Scriptable Event Handler with State](patterns/events.md)
|
||||
9. [Dynamic Constants Provider](patterns/dynamic-const.md)
|
||||
9. [Advanced Topics](advanced.md)
|
||||
1. [Capture Scope for Function Call](language/fn-capture.md)
|
||||
2. [Low-Level API](rust/register-raw.md)
|
||||
|
@ -71,20 +71,25 @@ switch map {
|
||||
}
|
||||
```
|
||||
|
||||
Switching on [arrays] is very useful when working with Rust enums (see [this chapter]({{rootUrl}}/patterns/enums.md)
|
||||
for more details).
|
||||
|
||||
Difference From If-Else Chain
|
||||
-----------------------------
|
||||
|
||||
Although a `switch` expression looks _almost_ the same as an `if`-`else` chain,
|
||||
Difference From `if` - `else if` Chain
|
||||
-------------------------------------
|
||||
|
||||
Although a `switch` expression looks _almost_ the same as an `if`-`else if` chain,
|
||||
there are subtle differences between the two.
|
||||
|
||||
### Look-up Table vs `x == y`
|
||||
|
||||
A `switch` expression matches through _hashing_ via a look-up table.
|
||||
Therefore, matching is very fast. Walking down an `if`-`else` chain
|
||||
will be _much_ slower.
|
||||
Therefore, matching is very fast. Walking down an `if`-`else if` chain
|
||||
is _much_ slower.
|
||||
|
||||
On the other hand, operators can be [overloaded][operator overloading] in Rhai,
|
||||
meaning that it is possible to override the `==` operator for integers such
|
||||
that `if x == y` returns a different result from the built-in default.
|
||||
that `x == y` returns a different result from the built-in default.
|
||||
|
||||
`switch` expressions do _not_ use the `==` operator for comparison;
|
||||
instead, they _hash_ the data values and jump directly to the correct
|
||||
@ -95,3 +100,12 @@ the `==` operator will have no effect.
|
||||
Therefore, in environments where it is desirable to [overload][operator overloading]
|
||||
the `==` operator - though it is difficult to think of valid scenarios where you'd want
|
||||
`1 == 1` to return something other than `true` - avoid using the `switch` expression.
|
||||
|
||||
### Efficiency
|
||||
|
||||
Because the `switch` expression works through a look-up table, it is very efficient
|
||||
even for _large_ number of cases; in fact, switching is an O(1) operation regardless
|
||||
of the size of the data and number of cases to match.
|
||||
|
||||
A long `if`-`else if` chain becomes increasingly slower with each additional case
|
||||
because essentially an O(n) _linear scan_ is performed.
|
||||
|
64
doc/src/patterns/enums.md
Normal file
64
doc/src/patterns/enums.md
Normal file
@ -0,0 +1,64 @@
|
||||
Working With Rust Enums
|
||||
=======================
|
||||
|
||||
{{#include ../links.md}}
|
||||
|
||||
Enums in Rust are typically used with _pattern matching_. Rhai is dynamic, so although
|
||||
it integrates with Rust enum variants just fine (treated transparently as [custom types]),
|
||||
it is impossible (short of registering a complete API) to distinguish between individual
|
||||
enum variants or to extract internal data from them.
|
||||
|
||||
|
||||
Switch Through Arrays
|
||||
---------------------
|
||||
|
||||
An easy way to work with Rust enums is through exposing the internal data of each enum variant
|
||||
as an [array], usually with the name of the variant as the first item:
|
||||
|
||||
```rust
|
||||
use rhai::{Engine, Array};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum MyEnum {
|
||||
Foo,
|
||||
Bar(i64),
|
||||
Baz(String, bool)
|
||||
}
|
||||
|
||||
impl MyEnum {
|
||||
fn get_enum_data(&mut self) -> Array {
|
||||
match self {
|
||||
Self::Foo => vec![
|
||||
"Foo".into()
|
||||
] as Array,
|
||||
Self::Bar(num) => vec![
|
||||
"Bar".into(), (*num).into()
|
||||
] as Array,
|
||||
Self::Baz(name, option) => vec![
|
||||
"Baz".into(), name.clone().into(), (*option).into()
|
||||
] as Array
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
engine
|
||||
.register_type_with_name::<MyEnum>("MyEnum")
|
||||
.register_get("enum_data", MyEnum::get_enum_data);
|
||||
```
|
||||
|
||||
Then it is a simple matter to match an enum via the `switch` expression:
|
||||
|
||||
```c
|
||||
// Assume 'value' = 'MyEnum::Baz("hello", true)'
|
||||
// 'get_data' creates a variable-length array with 'MyEnum' data
|
||||
let x = switch value.enum_data {
|
||||
["Foo"] => 1,
|
||||
["Bar", 42] => 2,
|
||||
["Bar", 123] => 3,
|
||||
["Baz", "hello", false] => 4,
|
||||
["Baz", "hello", true] => 5,
|
||||
_ => 9
|
||||
};
|
||||
|
||||
x == 5;
|
||||
```
|
@ -191,3 +191,10 @@ engine.register_fn("==",
|
||||
let item = new_ts(); // construct a new 'TestStruct'
|
||||
item in array; // 'in' operator uses '=='
|
||||
```
|
||||
|
||||
|
||||
Working With Enums
|
||||
------------------
|
||||
|
||||
It is quite easy to use Rust enums with Rhai.
|
||||
See [this chapter]({{rootUrl}}/patterns/enums.md) for more details.
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Engine, EvalAltResult, Scope, INT};
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, Scope, INT};
|
||||
|
||||
#[test]
|
||||
fn test_switch() -> Result<(), Box<EvalAltResult>> {
|
||||
@ -62,3 +62,58 @@ fn test_switch() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
mod test_switch_enum {
|
||||
use super::*;
|
||||
use rhai::Array;
|
||||
#[derive(Debug, Clone)]
|
||||
enum MyEnum {
|
||||
Foo,
|
||||
Bar(i64),
|
||||
Baz(String, bool),
|
||||
}
|
||||
|
||||
impl MyEnum {
|
||||
fn get_enum_data(&mut self) -> Array {
|
||||
match self {
|
||||
Self::Foo => vec!["Foo".into()] as Array,
|
||||
Self::Bar(num) => vec!["Bar".into(), (*num).into()] as Array,
|
||||
Self::Baz(name, option) => {
|
||||
vec!["Baz".into(), name.clone().into(), (*option).into()] as Array
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch_enum() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine
|
||||
.register_type_with_name::<MyEnum>("MyEnum")
|
||||
.register_get("get_data", MyEnum::get_enum_data);
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("x", MyEnum::Baz("hello".to_string(), true));
|
||||
|
||||
assert_eq!(
|
||||
engine.eval_with_scope::<INT>(
|
||||
&mut scope,
|
||||
r#"
|
||||
switch x.get_data {
|
||||
["Foo"] => 1,
|
||||
["Bar", 42] => 2,
|
||||
["Bar", 123] => 3,
|
||||
["Baz", "hello", false] => 4,
|
||||
["Baz", "hello", true] => 5,
|
||||
_ => 9
|
||||
}
|
||||
"#
|
||||
)?,
|
||||
5
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user