Rename downcast
to try_cast
and add cast
for Dynamic.
This commit is contained in:
94
README.md
94
README.md
@@ -279,7 +279,6 @@ The following primitive types are supported natively:
|
||||
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
||||
| **Nothing/void/nil/null** (or whatever you want to call it) | `()` | `"()"` | `""` _(empty string)_ |
|
||||
|
||||
[`Dynamic`]: #values-and-types
|
||||
[`()`]: #values-and-types
|
||||
|
||||
All types are treated strictly separate by Rhai, meaning that `i32` and `i64` and `u32` are completely different - they even cannot be added together. This is very similar to Rust.
|
||||
@@ -293,7 +292,7 @@ If no floating-point is needed or supported, use the [`no_float`] feature to rem
|
||||
|
||||
The `to_string` function converts a standard type into a string for display purposes.
|
||||
|
||||
The `type_of` function detects the actual type of a value. This is useful because all variables are `Dynamic`.
|
||||
The `type_of` function detects the actual type of a value. This is useful because all variables are [`Dynamic`] in nature.
|
||||
|
||||
```rust
|
||||
// Use 'type_of()' to get the actual types of values
|
||||
@@ -313,6 +312,74 @@ if type_of(x) == "string" {
|
||||
}
|
||||
```
|
||||
|
||||
Dynamic values
|
||||
--------------
|
||||
|
||||
[`Dynamic`]: #dynamic-values
|
||||
|
||||
A `Dynamic` value can be _any_ type.
|
||||
|
||||
Because [`type_of()`] a `Dynamic` value returns the type of the actual value, it is usually used to perform type-specific
|
||||
actions based on the actual value's type.
|
||||
|
||||
```rust
|
||||
let mystery = get_some_dynamic_value();
|
||||
|
||||
if type_of(mystery) == "i64" {
|
||||
print("Hey, I got an integer here!");
|
||||
} else if type_of(mystery) == "f64" {
|
||||
print("Hey, I got a float here!");
|
||||
} else if type_of(mystery) == "string" {
|
||||
print("Hey, I got a string here!");
|
||||
} else if type_of(mystery) == "bool" {
|
||||
print("Hey, I got a boolean here!");
|
||||
} else if type_of(mystery) == "array" {
|
||||
print("Hey, I got an array here!");
|
||||
} else if type_of(mystery) == "map" {
|
||||
print("Hey, I got an object map here!");
|
||||
} else if type_of(mystery) == "TestStruct" {
|
||||
print("Hey, I got the TestStruct custom type here!");
|
||||
} else {
|
||||
print("I don't know what this is: " + type_of(mystery));
|
||||
}
|
||||
```
|
||||
|
||||
In Rust, sometimes a `Dynamic` forms part of the return value - a good example is elements within an `Array` which are `Dynamic`,
|
||||
or property values in an object map. In order to get the _real_ value, the actual value type _must_ be known in advance.
|
||||
There is no easy way for Rust to detect, at run-time, what type the `Dynamic` value is (short of using the `type_name`
|
||||
function to get the textual name of the type and then matching on that).
|
||||
|
||||
To use a `Dynamic` value in Rust, use the `cast` method to convert the value into a specific, known type.
|
||||
Alternatively, use the `try_cast` method which does not panic but returns an error when the cast fails.
|
||||
|
||||
```rust
|
||||
use rhai::AnyExt; // Pull in the trait.
|
||||
|
||||
let list: Array = engine.eval("...")?; // return type is 'Array'
|
||||
let item = list[0]; // an element in an 'Array' is 'Dynamic'
|
||||
|
||||
let value = item.cast::<i64>(); // if the element is 'i64', this succeeds; otherwise it panics
|
||||
let value: i64 = item.cast(); // type can also be inferred
|
||||
|
||||
let value = item.try_cast::<i64>()?; // 'try_cast' does not panic when the cast fails, but returns an error
|
||||
```
|
||||
|
||||
The `type_name` method gets the name of the actual type as a string, which you may match against.
|
||||
|
||||
```rust
|
||||
use rhai::Any; // Pull in the trait.
|
||||
|
||||
let list: Array = engine.eval("...")?; // return type is 'Array'
|
||||
let item = list[0]; // an element in an 'Array' is 'Dynamic'
|
||||
|
||||
match item.type_name() { // 'type_name' returns the name of the actual Rust type
|
||||
"i64" => ...
|
||||
"std::string::String" => ...
|
||||
"bool" => ...
|
||||
"path::to::module::TestStruct" => ...
|
||||
}
|
||||
```
|
||||
|
||||
Value conversions
|
||||
-----------------
|
||||
|
||||
@@ -325,11 +392,11 @@ That's about it. For other conversions, register custom conversion functions.
|
||||
|
||||
```rust
|
||||
let x = 42;
|
||||
let y = x * 100.0; // <- error: cannot multiply i64 with f64
|
||||
let y = x.to_float() * 100.0; // works
|
||||
let z = y.to_int() + x; // works
|
||||
let y = x * 100.0; // <- error: cannot multiply i64 with f64
|
||||
let y = x.to_float() * 100.0; // works
|
||||
let z = y.to_int() + x; // works
|
||||
|
||||
let c = 'X'; // character
|
||||
let c = 'X'; // character
|
||||
print("c is '" + c + "' and its code is " + c.to_int()); // prints "c is 'X' and its code is 88"
|
||||
```
|
||||
|
||||
@@ -972,10 +1039,9 @@ Arrays
|
||||
|
||||
Arrays are first-class citizens in Rhai. Like C, arrays are accessed with zero-based, non-negative integer indices.
|
||||
Array literals are built within square brackets '`[`' ... '`]`' and separated by commas '`,`'.
|
||||
All elements stored in an array are [`Dynamic`], and the array can freely grow or shrink with elements added or removed.
|
||||
|
||||
The Rust type of a Rhai array is `rhai::Array`.
|
||||
|
||||
[`type_of()`] an array returns `"array"`.
|
||||
The Rust type of a Rhai array is `rhai::Array`. [`type_of()`] an array returns `"array"`.
|
||||
|
||||
Arrays are disabled via the [`no_index`] feature.
|
||||
|
||||
@@ -1053,7 +1119,7 @@ engine.register_fn("push", |list: &mut Array, item: MyType| list.push(Box::new(i
|
||||
Object maps
|
||||
-----------
|
||||
|
||||
Object maps are dictionaries. Properties of any type (`Dynamic`) can be freely added and retrieved.
|
||||
Object maps are dictionaries. Properties are all [`Dynamic`] and can be freely added and retrieved.
|
||||
Object map literals are built within braces '`#{`' ... '`}`' (_name_ `:` _value_ syntax similar to Rust)
|
||||
and separated by commas '`,`'. The property _name_ can be a simple variable name following the same
|
||||
naming rules as [variables], or an arbitrary string literal.
|
||||
@@ -1064,9 +1130,7 @@ The index notation allows setting/getting properties of arbitrary names (even th
|
||||
|
||||
**Important:** Trying to read a non-existent property returns `()` instead of causing an error.
|
||||
|
||||
The Rust type of a Rhai object map is `rhai::Map`.
|
||||
|
||||
[`type_of()`] an object map returns `"map"`.
|
||||
The Rust type of a Rhai object map is `rhai::Map`. [`type_of()`] an object map returns `"map"`.
|
||||
|
||||
Object maps are disabled via the [`no_object`] feature.
|
||||
|
||||
@@ -1642,9 +1706,9 @@ Function volatility considerations
|
||||
---------------------------------
|
||||
|
||||
Even if a custom function does not mutate state nor cause side effects, it may still be _volatile_, i.e. it _depends_ on the external
|
||||
environment and not _pure_. A perfect example is a function that gets the current time - obviously each run will return a different value!
|
||||
environment and is not _pure_. A perfect example is a function that gets the current time - obviously each run will return a different value!
|
||||
The optimizer, when using [`OptimizationLevel::Full`], _assumes_ that all functions are _pure_, so when it finds constant arguments
|
||||
it will eagerly run execute the function call. This causes the script to behave differently from the intended semantics because
|
||||
it will eagerly execute the function call. This causes the script to behave differently from the intended semantics because
|
||||
essentially the result of the function call will always be the same value.
|
||||
|
||||
Therefore, **avoid using [`OptimizationLevel::Full`]** if you intend to register non-_pure_ custom types and/or functions.
|
||||
|
Reference in New Issue
Block a user