From c8c4ca21adb94918a4647c3cc148bff0bc06e8af Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 19 Oct 2020 14:26:15 +0800 Subject: [PATCH] Update docs. --- README.md | 13 ++- doc/src/SUMMARY.md | 4 +- doc/src/about/related.md | 12 +- doc/src/images/rhai.png | Bin 0 -> 5158 bytes doc/src/plugins/module.md | 2 +- doc/src/rust/custom.md | 167 +++++++++++++-------------- doc/src/rust/getters-setters.md | 29 ++--- doc/src/rust/indexers.md | 47 +++++--- doc/src/rust/register-raw.md | 2 +- examples/arrays_and_structs.rs | 2 +- examples/custom_types_and_methods.rs | 2 +- src/api.rs | 30 +++-- src/engine.rs | 13 ++- tests/arrays.rs | 2 +- tests/float.rs | 2 +- tests/get_set.rs | 2 +- tests/method_call.rs | 2 +- tests/mismatched_op.rs | 2 +- 18 files changed, 181 insertions(+), 152 deletions(-) create mode 100644 doc/src/images/rhai.png diff --git a/README.md b/README.md index cc68898c..73ca1bb5 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,13 @@ Rhai - Embedded Scripting for Rust [![crates.io](https://img.shields.io/crates/v/rhai.svg)](https://crates.io/crates/rhai/) [![crates.io](https://img.shields.io/crates/d/rhai)](https://crates.io/crates/rhai/) [![API Docs](https://docs.rs/rhai/badge.svg)](https://docs.rs/rhai/) +[![chat](https://img.shields.io/discord/767611025456889857.svg?logo=discord)](https://discord.gg/yZMKAQ) +[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/Rhai)](https://www.reddit.com/r/Rhai) Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way to add scripting to any application. + Supported targets and builds --------------------------- @@ -19,6 +22,7 @@ Supported targets and builds * `no-std` * Minimum Rust version 1.45 + Standard features ----------------- @@ -41,13 +45,15 @@ Standard features * Serialization/deserialization support via [serde](https://crates.io/crates/serde) (requires the `serde` feature). * Support for [minimal builds](https://schungx.github.io/rhai/start/builds/minimal.html) by excluding unneeded language [features](https://schungx.github.io/rhai/start/features.html). -Protection against attacks --------------------------- + +Protected against attacks +------------------------- * Sand-boxed - the scripting engine, if declared immutable, cannot mutate the containing environment unless [explicitly permitted](https://schungx.github.io/rhai/patterns/control.html). * Rugged - protected against malicious attacks (such as [stack-overflow](https://schungx.github.io/rhai/safety/max-call-stack.html), [over-sized data](https://schungx.github.io/rhai/safety/max-string-size.html), and [runaway scripts](https://schungx.github.io/rhai/safety/max-operations.html) etc.) that may come from untrusted third-party user-land scripts. * Track script evaluation [progress](https://schungx.github.io/rhai/safety/progress.html) and manually terminate a script run. + For those who actually want their own language --------------------------------------------- @@ -56,6 +62,7 @@ For those who actually want their own language * Define [custom operators](https://schungx.github.io/rhai/engine/custom-op.html). * Extend the language with [custom syntax](https://schungx.github.io/rhai/engine/custom-syntax.html). + Documentation ------------- @@ -65,12 +72,14 @@ To build _The Book_, first install [`mdbook`](https://github.com/rust-lang/mdBoo and [`mdbook-tera`](https://github.com/avitex/mdbook-tera) (for templating). Running `mdbook build` builds it. + Playground ---------- An [Online Playground](https://alvinhochun.github.io/rhai-demo/) is available with syntax-highlighting editor. Scripts can be evaluated directly from the editor. + License ------- diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 1e59b3e7..962bdca1 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -36,8 +36,8 @@ The Rhai Scripting Language 4. [Register a Fallible Rust Function](rust/fallible.md) 6. [Override a Built-in Function](rust/override.md) 7. [Operator Overloading](rust/operators.md) - 8. [Register a Custom Type and its Methods](rust/custom.md) - 1. [Getters and Setters](rust/getters-setters.md) + 8. [Register any Rust Type and its Methods](rust/custom.md) + 1. [Property Getters and Setters](rust/getters-setters.md) 2. [Indexers](rust/indexers.md) 3. [Disable Custom Types](rust/disable-custom.md) 4. [Printing Custom Types](rust/print-custom.md) diff --git a/doc/src/about/related.md b/doc/src/about/related.md index 8174676b..a284c7f0 100644 --- a/doc/src/about/related.md +++ b/doc/src/about/related.md @@ -3,7 +3,9 @@ Related Resources {{#include ../links.md}} -Other online documentation resources for Rhai: + +Other Online Resources for Rhai +------------------------------ * [`crates.io`](https://crates.io/crates/rhai) - Rhai crate @@ -13,7 +15,13 @@ Other online documentation resources for Rhai: * [Online Playground][playground] - Run scripts directly from editor -Other cool projects to check out: +* [Discord Chat](https://discord.gg/yZMKAQ) - Rhai channel + +* [Reddit](https://www.reddit.com/r/Rhai) - Rhai community + + +Other Cool Projects +------------------- * [ChaiScript](http://chaiscript.com) - A strong inspiration for Rhai. An embedded scripting language for C++. diff --git a/doc/src/images/rhai.png b/doc/src/images/rhai.png new file mode 100644 index 0000000000000000000000000000000000000000..73d16a99e6fd65d5a44ee093098571ba2bd1ede4 GIT binary patch literal 5158 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 U5WF%)4FCWD07*qoM6N<$f+NY&3IG5A literal 0 HcmV?d00001 diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index 81195d47..6d07abdc 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -8,7 +8,7 @@ Prelude ------- When using the plugins system, the entire `rhai::plugin` module must be imported as a prelude -because code generated will these imports. +because code generated will need these imports. ```rust use rhai::plugin::*; diff --git a/doc/src/rust/custom.md b/doc/src/rust/custom.md index eb548f69..59815079 100644 --- a/doc/src/rust/custom.md +++ b/doc/src/rust/custom.md @@ -1,124 +1,118 @@ -Register a Custom Type and its Methods +Register any Rust Type and its Methods ===================================== {{#include ../links.md}} -Rhai works seamlessly with _any_ complex Rust type. The type can be registered with the `Engine`, as below. + +Free Typing +----------- + +Rhai works seamlessly with _any_ Rust type. The type can be _anything_; it does not +have any prerequisites other than being `Clone`. It does not need to implement +any other trait or use any custom `#[derive]`. + +This allows Rhai to be integrated into an existing code base with as little plumbing +as possible, usually silently and seamlessly. External types that are not defined +within the same crate (and thus cannot implement special Rhai traits or +use special `#[derive]`) can also be used easily with Rhai. + +The reason why it is termed a _custom_ type throughout this documentation is that +Rhai natively supports a number of data types with fast, internal treatment (see +the list of [standard types]). Any type outside of this list is considered _custom_. + +Any type not supported natively by Rhai is stored as a Rust _trait object_, with no +restrictions other than being `Clone` (plus `Send + Sync` under the [`sync`] feature). +It runs slightly slower than natively-supported types as it does not have built-in, +optimized implementations for commonly-used functions, but for all other purposes has +no difference. Support for custom types can be turned off via the [`no_object`] feature. -```rust -use rhai::{Engine, EvalAltResult}; -use rhai::RegisterFn; // remember 'RegisterFn' is needed -#[derive(Clone)] -struct TestStruct { - field: i64 -} +Register a Custom Type and its Methods +------------------------------------- -impl TestStruct { - fn update(&mut self) { - self.field += 41; - } +Any custom type must implement the `Clone` trait as this allows the [`Engine`] to pass by value. - fn new() -> Self { - TestStruct { field: 1 } - } -} - -let mut engine = Engine::new(); - -engine - .register_type::() // most API's can be chained up - .register_fn("update", TestStruct::update) - .register_fn("new_ts", TestStruct::new); - -let result = engine.eval::("let x = new_ts(); x.update(); x")?; - -println!("result: {}", result.field); // prints 42 -``` - - -Register a Custom Type ---------------------- - -A custom type must implement `Clone` as this allows the [`Engine`] to pass by value. +If the [`sync`] feature is used, it must also be `Send + Sync`. Notice that the custom type needs to be _registered_ using `Engine::register_type` or `Engine::register_type_with_name`. +To use native methods on custom types in Rhai scripts, it is common to register an API +for the type using one of the `Engine::register_XXX` functions. + ```rust +use rhai::{Engine, EvalAltResult}; +use rhai::RegisterFn; // remember 'RegisterFn' is needed + #[derive(Clone)] struct TestStruct { field: i64 } impl TestStruct { - fn update(&mut self) { // methods take &mut as first parameter - self.field += 41; + fn new() -> Self { + Self { field: 1 } } - fn new() -> Self { - TestStruct { field: 1 } + fn update(&mut self, x: i64) { // methods take &mut as first parameter + self.field += x; } } let mut engine = Engine::new(); -engine.register_type::(); -``` - - -Methods on the Custom Type -------------------------- - -To use native custom types, methods and functions in Rhai scripts, simply register them -using one of the `Engine::register_XXX` API. - -Below, the `update` and `new` methods are registered using `Engine::register_fn`. - -```rust +// Most Engine API's can be chained up. engine - .register_fn("update", TestStruct::update) // registers 'update(&mut TestStruct)' - .register_fn("new_ts", TestStruct::new); // registers 'new()' + .register_type::() // register custom type + .register_fn("new_ts", TestStruct::new) + .register_fn("update", TestStruct::update); + +// Cast result back to custom type. +let result = engine.eval::( + r" + let x = new_ts(); // calls 'TestStruct::new' + x.update(41); // calls 'TestStruct::update' + x // 'x' holds a 'TestStruct' + " +)?; + +println!("result: {}", result.field); // prints 42 ``` -***Note**: Rhai follows the convention that methods of custom types take a `&mut` first parameter -so that invoking methods can update the types. All other parameters in Rhai are passed by value (i.e. clones).* +Rhai follows the convention that methods of custom types take a `&mut` first parameter +to that type, so that invoking methods can always update it. + +All other parameters in Rhai are passed by value (i.e. clones). **IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.** -Use the Custom Type in Scripts ------------------------------ - -The custom type is then ready for use in scripts. Scripts can see the functions and methods registered earlier. -Get the evaluation result back out just as before, this time casting to the custom type: - -```rust -let result = engine.eval::("let x = new_ts(); x.update(); x")?; - -println!("result: {}", result.field); // prints 42 -``` - - Method-Call Style vs. Function-Call Style ---------------------------------------- Any function with a first argument that is a `&mut` reference can be used as method calls because internally they are the same thing: methods on a type is implemented as a functions taking a `&mut` first argument. + This design is similar to Rust. ```rust -fn foo(ts: &mut TestStruct) -> i64 { - ts.field +impl TestStruct { + fn foo(&mut self) -> i64 { + self.field + } } -engine.register_fn("foo", foo); // register a Rust native function +engine.register_fn("foo", TestStruct::foo); let result = engine.eval::( - "let x = new_ts(); x.foo()" // 'foo' can be called like a method on 'x' + r" + let x = new_ts(); + foo(x); // normal call to 'foo' + x.foo() // 'foo' can also be called like a method on 'x' + " )?; println!("result: {}", result); // prints 1 @@ -128,8 +122,9 @@ Under [`no_object`], however, the _method_ style of function calls (i.e. calling a function as an object-method) is no longer supported. ```rust -// Below is a syntax error under 'no_object' because 'clear' cannot be called in method style. -let result = engine.eval::<()>("let x = [1, 2, 3]; x.clear()")?; +// Below is a syntax error under 'no_object'. +let result = engine.eval("let x = [1, 2, 3]; x.clear();")?; + // ^ cannot call in method style under 'no_object' ``` @@ -143,18 +138,16 @@ with a special "pretty-print" name, [`type_of()`] will return that name instead. ```rust engine - .register_type::() - .register_fn("new_ts", TestStruct::new); + .register_type::() + .register_fn("new_ts1", TestStruct1::new) + .register_type_with_name::("MyType") + .register_fn("new_ts2", TestStruct2::new); -let x = new_ts(); -x.type_of() == "path::to::module::TestStruct"; +let ts1_type = engine.eval::(r#"let x = new_ts1(); x.type_of()"#)?; +let ts2_type = engine.eval::(r#"let x = new_ts2(); x.type_of()"#)?; -engine - .register_type_with_name::("Hello") - .register_fn("new_ts", TestStruct::new); - -let x = new_ts(); -x.type_of() == "Hello"; +println!("{}", ts1_type); // prints 'path::to::TestStruct' +println!("{}", ts1_type); // prints 'MyType' ``` @@ -190,7 +183,9 @@ the `==` operator must be registered for the custom type: ```rust // Assume 'TestStruct' implements `PartialEq` -engine.register_fn("==", |item1: &mut TestStruct, item2: TestStruct| item1 == item2); +engine.register_fn("==", + |item1: &mut TestStruct, item2: TestStruct| item1 == &item2 +); // Then this works in Rhai: let item = new_ts(); // construct a new 'TestStruct' diff --git a/doc/src/rust/getters-setters.md b/doc/src/rust/getters-setters.md index 586070af..334c7438 100644 --- a/doc/src/rust/getters-setters.md +++ b/doc/src/rust/getters-setters.md @@ -1,29 +1,32 @@ -Custom Type Getters and Setters -============================== +Custom Type Property Getters and Setters +======================================= {{#include ../links.md}} -A custom type can also expose members by registering `get` and/or `set` functions. +A [custom type] can also expose properties by registering `get` and/or `set` functions. Getters and setters each take a `&mut` reference to the first parameter. Getters and setters are disabled when the [`no_object`] feature is used. -| `Engine` API | Description | Return Value of Function | -| --------------------- | ------------------------------------------------- | :-----------------------------------: | -| `register_get` | register a getter | _any_ `T: Clone` | -| `register_set` | register a setter | _none_ | -| `register_get_set` | short-hand to register both a getter and a setter | _none_ | -| `register_get_result` | register a getter | `Result>` | -| `register_set_result` | register a setter | `Result<(), Box>` | +| `Engine` API | Function signature(s)
(`T: Clone` = custom type,
`V: Clone` = data type) | Can mutate `T`? | +| --------------------- | -------------------------------------------------------------------------------- | :----------------------------: | +| `register_get` | `Fn(&mut T) -> V` | yes, but not advised | +| `register_set` | `Fn(&mut T, V)` | yes | +| `register_get_set` | getter: `Fn(&mut T) -> V`
setter: `Fn(&mut T, V)` | yes, but not advised in getter | +| `register_get_result` | `Fn(&mut T) -> Result>` | yes, but not advised | +| `register_set_result` | `Fn(&mut T, V) -> Result<(), Box>` | yes | + +By convention, property getters are not supposed to mutate the [custom type], although there is nothing +that prevents this mutation. Cannot Override Object Maps -------------------------- -Getters and setters are only intended for [custom types]. +Property getters and setters are mainly intended for [custom types]. -Any getter or setter function registered for [object maps] is simply ignored because +Any getter or setter function registered for [object maps] is simply _ignored_ because the get/set calls will be interpreted as properties on the [object maps]. @@ -47,7 +50,7 @@ impl TestStruct { } fn new() -> Self { - TestStruct { field: "hello" } + Self { field: "hello" } } } diff --git a/doc/src/rust/indexers.md b/doc/src/rust/indexers.md index 2f76277f..f7cff1af 100644 --- a/doc/src/rust/indexers.md +++ b/doc/src/rust/indexers.md @@ -3,23 +3,30 @@ Custom Type Indexers {{#include ../links.md}} -A custom type can also expose an _indexer_ by registering an indexer function. +A [custom type] can also expose an _indexer_ by registering an indexer function. -A custom type with an indexer function defined can use the bracket notation to get a property value: +A [custom type] with an indexer function defined can use the bracket notation to get a property value: > _object_ `[` _index_ `]` -Like getters and setters, indexers take a `&mut` reference to the first parameter. +Like property [getters/setters], indexers take a `&mut` reference to the first parameter. + +They also take an additional parameter of any type that serves as the _index_ within brackets. Indexers are disabled when the [`no_index`] feature is used. -| `Engine` API | Description | Return Value of Function | -| ----------------------------- | -------------------------------------------------------- | :-----------------------------------: | -| `register_indexer_get` | register an index getter | _any_ `T: Clone` | -| `register_indexer_set` | register an index setter | _none_ | -| `register_indexer_get_set` | short-hand to register both an index getter and a setter | _none_ | -| `register_indexer_get_result` | register an index getter | `Result>` | -| `register_indexer_set_result` | register an index setter | `Result<(), Box>` | +| `Engine` API | Function signature(s)
(`T: Clone` = custom type,
`X: Clone` = index type,
`V: Clone` = data type) | Can mutate `T`? | +| ----------------------------- | ------------------------------------------------------------------------------------------------------------- | :----------------------------: | +| `register_indexer_get` | `Fn(&mut T, X) -> V` | yes, but not advised | +| `register_indexer_set` | `Fn(&mut T, X, V)` | yes | +| `register_indexer_get_set` | getter: `Fn(&mut T, X) -> V`
setter: `Fn(&mut T, X, V)` | yes, but not advised in getter | +| `register_indexer_get_result` | `Fn(&mut T, X) -> Result>` | yes, but not advised | +| `register_indexer_set_result` | `Fn(&mut T, X, V) -> Result<(), Box>` | yes | + +By convention, index getters are not supposed to mutate the [custom type], although there is nothing +that prevents this mutation. + +**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.** Cannot Override Arrays, Object Maps and Strings @@ -42,15 +49,15 @@ struct TestStruct { impl TestStruct { // Remember &mut must be used even for getters - fn get_field(&mut self, index: i64) -> i64 { - self.fields[index as usize] + fn get_field(&mut self, index: String) -> i64 { + self.fields[index.len()] } - fn set_field(&mut self, index: i64, value: i64) { - self.fields[index as usize] = value + fn set_field(&mut self, index: String, value: i64) { + self.fields[index.len()] = value } fn new() -> Self { - TestStruct { fields: vec![1, 2, 3, 4, 5] } + Self { fields: vec![1, 2, 3, 4, 5] } } } @@ -63,9 +70,13 @@ engine .register_indexer_get(TestStruct::get_field) .register_indexer_set(TestStruct::set_field); -let result = engine.eval::("let a = new_ts(); a[2] = 42; a[2]")?; +let result = engine.eval::( + r#" + let a = new_ts(); + a["xyz"] = 42; // these indexers use strings + a["xyz"] // as the index type + "# +)?; println!("Answer: {}", result); // prints 42 ``` - -**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.** diff --git a/doc/src/rust/register-raw.md b/doc/src/rust/register-raw.md index dc4e3ca2..87854acc 100644 --- a/doc/src/rust/register-raw.md +++ b/doc/src/rust/register-raw.md @@ -65,7 +65,7 @@ The function signature passed to `Engine::register_raw_fn` takes the following f where: -* `T: Variant + Clone` - return type of the function. +* `T: Clone` - return type of the function. * `context: NativeCallContext` - the current _native call context_, which exposes the following: diff --git a/examples/arrays_and_structs.rs b/examples/arrays_and_structs.rs index 6aa9fafa..0ad34051 100644 --- a/examples/arrays_and_structs.rs +++ b/examples/arrays_and_structs.rs @@ -11,7 +11,7 @@ impl TestStruct { } fn new() -> Self { - TestStruct { x: 1 } + Self { x: 1 } } } diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index 4a8e260a..4b0118fa 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -11,7 +11,7 @@ impl TestStruct { } fn new() -> Self { - TestStruct { x: 1 } + Self { x: 1 } } } diff --git a/src/api.rs b/src/api.rs index 031e06b7..07f0cef1 100644 --- a/src/api.rs +++ b/src/api.rs @@ -88,7 +88,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { field: 1 } } + /// fn new() -> Self { Self { field: 1 } } /// fn update(&mut self, offset: i64) { self.field += offset; } /// } /// @@ -130,7 +130,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { field: 1 } } + /// fn new() -> Self { Self { field: 1 } } /// } /// /// # fn main() -> Result<(), Box> { @@ -200,7 +200,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { field: 1 } } + /// fn new() -> Self { Self { field: 1 } } /// // Even a getter must start with `&mut self` and not `&self`. /// fn get_field(&mut self) -> i64 { self.field } /// } @@ -252,7 +252,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { field: 1 } } + /// fn new() -> Self { Self { field: 1 } } /// // Even a getter must start with `&mut self` and not `&self`. /// fn get_field(&mut self) -> Result> { /// Ok(self.field.into()) @@ -295,7 +295,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { field: 1 } } + /// fn new() -> Self { Self { field: 1 } } /// fn set_field(&mut self, new_val: i64) { self.field = new_val; } /// } /// @@ -348,7 +348,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { field: 1 } } + /// fn new() -> Self { Self { field: 1 } } /// fn set_field(&mut self, new_val: i64) -> Result<(), Box> { /// self.field = new_val; /// Ok(()) @@ -386,8 +386,7 @@ impl Engine { U: Variant + Clone, { self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| { - callback(obj, value)?; - Ok(().into()) + callback(obj, value).map(Into::into) }) } @@ -405,7 +404,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { field: 1 } } + /// fn new() -> Self { Self { field: 1 } } /// // Even a getter must start with `&mut self` and not `&self`. /// fn get_field(&mut self) -> i64 { self.field } /// fn set_field(&mut self, new_val: i64) { self.field = new_val; } @@ -462,7 +461,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } } + /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// // Even a getter must start with `&mut self` and not `&self`. /// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] } /// } @@ -534,7 +533,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } } + /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// // Even a getter must start with `&mut self` and not `&self`. /// fn get_field(&mut self, index: i64) -> Result> { /// Ok(self.fields[index as usize].into()) @@ -600,7 +599,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } } + /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; } /// } /// @@ -672,7 +671,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } } + /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box> { /// self.fields[index as usize] = value; /// Ok(()) @@ -724,8 +723,7 @@ impl Engine { } self.register_result_fn(FN_IDX_SET, move |obj: &mut T, index: X, value: U| { - callback(obj, index, value)?; - Ok(().into()) + callback(obj, index, value).map(Into::into) }) } @@ -745,7 +743,7 @@ impl Engine { /// } /// /// impl TestStruct { - /// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } } + /// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } } /// // Even a getter must start with `&mut self` and not `&self`. /// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] } /// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; } diff --git a/src/engine.rs b/src/engine.rs index bc7063dc..c526b561 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -934,7 +934,9 @@ impl Engine { level, ) .map_err(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(_, _) => { + EvalAltResult::ErrorFunctionNotFound(fn_sig, _) + if fn_sig.ends_with("]=") => + { EvalAltResult::ErrorIndexingType( self.map_type_name(val_type_name).into(), Position::none(), @@ -1381,9 +1383,12 @@ impl Engine { ) .map(|(v, _)| v.into()) .map_err(|err| match *err { - EvalAltResult::ErrorFunctionNotFound(_, _) => Box::new( - EvalAltResult::ErrorIndexingType(type_name.into(), Position::none()), - ), + EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with("]") => { + Box::new(EvalAltResult::ErrorIndexingType( + type_name.into(), + Position::none(), + )) + } _ => err, }) } diff --git a/tests/arrays.rs b/tests/arrays.rs index cbd4daf5..65811a23 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -84,7 +84,7 @@ fn test_array_with_structs() -> Result<(), Box> { } fn new() -> Self { - TestStruct { x: 1 } + Self { x: 1 } } } diff --git a/tests/float.rs b/tests/float.rs index ea87ece3..534115e3 100644 --- a/tests/float.rs +++ b/tests/float.rs @@ -51,7 +51,7 @@ fn test_struct_with_float() -> Result<(), Box> { } fn new() -> Self { - TestStruct { x: 1.0 } + Self { x: 1.0 } } } diff --git a/tests/get_set.rs b/tests/get_set.rs index ca5c0c47..b654348c 100644 --- a/tests/get_set.rs +++ b/tests/get_set.rs @@ -25,7 +25,7 @@ fn test_get_set() -> Result<(), Box> { } fn new() -> Self { - TestStruct { + Self { x: 1, y: 0, array: vec![1, 2, 3, 4, 5], diff --git a/tests/method_call.rs b/tests/method_call.rs index 20612146..e7b863e6 100644 --- a/tests/method_call.rs +++ b/tests/method_call.rs @@ -15,7 +15,7 @@ fn test_method_call() -> Result<(), Box> { } fn new() -> Self { - TestStruct { x: 1 } + Self { x: 1 } } } diff --git a/tests/mismatched_op.rs b/tests/mismatched_op.rs index 01373df2..5ef401f0 100644 --- a/tests/mismatched_op.rs +++ b/tests/mismatched_op.rs @@ -20,7 +20,7 @@ fn test_mismatched_op_custom_type() { impl TestStruct { fn new() -> Self { - TestStruct { x: 1 } + Self { x: 1 } } }