Convert function calls to method calls to use &mut first argument.
This commit is contained in:
parent
d7c69c4f51
commit
acd4685145
55
README.md
55
README.md
@ -1122,6 +1122,7 @@ Comments
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
Comments are C-style, including '`/*` ... `*/`' pairs and '`//`' for comments to the end of the line.
|
Comments are C-style, including '`/*` ... `*/`' pairs and '`//`' for comments to the end of the line.
|
||||||
|
Comments can be nested.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let /* intruder comment */ name = "Bob";
|
let /* intruder comment */ name = "Bob";
|
||||||
@ -1138,14 +1139,35 @@ let /* intruder comment */ name = "Bob";
|
|||||||
*/
|
*/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Keywords
|
||||||
|
--------
|
||||||
|
|
||||||
|
The following are reserved keywords in Rhai:
|
||||||
|
|
||||||
|
| Keywords | Usage | Not available under feature |
|
||||||
|
| ------------------------------------------------- | --------------------- | :-------------------------: |
|
||||||
|
| `true`, `false` | Boolean constants | |
|
||||||
|
| `let`, `const` | Variable declarations | |
|
||||||
|
| `if`, `else` | Control flow | |
|
||||||
|
| `while`, `loop`, `for`, `in`, `continue`, `break` | Looping | |
|
||||||
|
| `fn`, `private` | Functions | [`no_function`] |
|
||||||
|
| `return` | Return values | |
|
||||||
|
| `throw` | Return errors | |
|
||||||
|
| `import`, `export`, `as` | Modules | [`no_module`] |
|
||||||
|
|
||||||
|
Keywords cannot be the name of a [function] or [variable], unless the relevant exclusive feature is enabled.
|
||||||
|
For example, `fn` is a valid variable name if the [`no_function`] feature is used.
|
||||||
|
|
||||||
Statements
|
Statements
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Statements are terminated by semicolons '`;`' - they are mandatory, except for the _last_ statement where it can be omitted.
|
Statements are terminated by semicolons '`;`' and they are mandatory,
|
||||||
|
except for the _last_ statement in a _block_ (enclosed by '`{`' .. '`}`' pairs) where it can be omitted.
|
||||||
|
|
||||||
A statement can be used anywhere where an expression is expected. The _last_ statement of a statement block
|
A statement can be used anywhere where an expression is expected. These are called, for lack of a more
|
||||||
(enclosed by '`{`' .. '`}`' pairs) is always the return value of the statement. If a statement has no return value
|
creative name, "statement expressions." The _last_ statement of a statement block is _always_ the block's
|
||||||
(e.g. variable definitions, assignments) then the value will be [`()`].
|
return value when used as a statement.
|
||||||
|
If the last statement has no return value (e.g. variable definitions, assignments) then it is assumed to be [`()`].
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let a = 42; // normal assignment statement
|
let a = 42; // normal assignment statement
|
||||||
@ -1153,16 +1175,17 @@ let a = foo(42); // normal function call statement
|
|||||||
foo < 42; // normal expression as statement
|
foo < 42; // normal expression as statement
|
||||||
|
|
||||||
let a = { 40 + 2 }; // 'a' is set to the value of the statement block, which is the value of the last statement
|
let a = { 40 + 2 }; // 'a' is set to the value of the statement block, which is the value of the last statement
|
||||||
// ^ notice that the last statement does not require a terminating semicolon (although it also works with it)
|
// ^ the last statement does not require a terminating semicolon (although it also works with it)
|
||||||
// ^ notice that a semicolon is required here to terminate the assignment statement; it is syntax error without it
|
// ^ semicolon required here to terminate the assignment statement; it is a syntax error without it
|
||||||
|
|
||||||
4 * 10 + 2 // this is also a statement, which is an expression, with no ending semicolon because
|
4 * 10 + 2 // a statement which is just one expression; no ending semicolon is OK
|
||||||
// it is the last statement of the whole block
|
// because it is the last statement of the whole block
|
||||||
```
|
```
|
||||||
|
|
||||||
Variables
|
Variables
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
[variable]: #variables
|
||||||
[variables]: #variables
|
[variables]: #variables
|
||||||
|
|
||||||
Variables in Rhai follow normal C naming rules (i.e. must contain only ASCII letters, digits and underscores '`_`').
|
Variables in Rhai follow normal C naming rules (i.e. must contain only ASCII letters, digits and underscores '`_`').
|
||||||
@ -1307,8 +1330,8 @@ Strings and Chars
|
|||||||
[strings]: #strings-and-chars
|
[strings]: #strings-and-chars
|
||||||
[char]: #strings-and-chars
|
[char]: #strings-and-chars
|
||||||
|
|
||||||
String and char literals follow C-style formatting, with support for Unicode ('`\u`_xxxx_' or '`\U`_xxxxxxxx_') and
|
String and character literals follow C-style formatting, with support for Unicode ('`\u`_xxxx_' or '`\U`_xxxxxxxx_')
|
||||||
hex ('`\x`_xx_') escape sequences.
|
and hex ('`\x`_xx_') escape sequences.
|
||||||
|
|
||||||
Hex sequences map to ASCII characters, while '`\u`' maps to 16-bit common Unicode code points and '`\U`' maps the full,
|
Hex sequences map to ASCII characters, while '`\u`' maps to 16-bit common Unicode code points and '`\U`' maps the full,
|
||||||
32-bit extended Unicode code points.
|
32-bit extended Unicode code points.
|
||||||
@ -1388,7 +1411,7 @@ record == "Bob X. Davis: age 42 ❤\n";
|
|||||||
|
|
||||||
### Built-in functions
|
### Built-in functions
|
||||||
|
|
||||||
The following standard methods (defined in the [`MoreStringPackage`](#packages) but excluded if using a [raw `Engine`]) operate on strings:
|
The following standard methods (mostly defined in the [`MoreStringPackage`](#packages) but excluded if using a [raw `Engine`]) operate on strings:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
|
| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
|
||||||
@ -1463,7 +1486,7 @@ Arrays are disabled via the [`no_index`] feature.
|
|||||||
|
|
||||||
### Built-in functions
|
### Built-in functions
|
||||||
|
|
||||||
The following methods (defined in the [`BasicArrayPackage`](#packages) but excluded if using a [raw `Engine`]) operate on arrays:
|
The following methods (mostly defined in the [`BasicArrayPackage`](#packages) but excluded if using a [raw `Engine`]) operate on arrays:
|
||||||
|
|
||||||
| Function | Parameter(s) | Description |
|
| Function | Parameter(s) | Description |
|
||||||
| ----------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
| ----------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||||
@ -1963,6 +1986,9 @@ println!(result); // prints "Runtime error: 42 is too large! (line 5,
|
|||||||
Functions
|
Functions
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
[function]: #functions
|
||||||
|
[functions]: #functions
|
||||||
|
|
||||||
Rhai supports defining functions in script (unless disabled with [`no_function`]):
|
Rhai supports defining functions in script (unless disabled with [`no_function`]):
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -2008,7 +2034,8 @@ fn foo() { x } // <- syntax error: variable 'x' doesn't exist
|
|||||||
Functions defined in script always take [`Dynamic`] parameters (i.e. the parameter can be of any type).
|
Functions defined in script always take [`Dynamic`] parameters (i.e. the parameter can be of any type).
|
||||||
It is important to remember that all arguments are passed by _value_, so all functions are _pure_
|
It is important to remember that all arguments are passed by _value_, so all functions are _pure_
|
||||||
(i.e. they never modify their arguments).
|
(i.e. they never modify their arguments).
|
||||||
Any update to an argument will **not** be reflected back to the caller. This can introduce subtle bugs, if not careful.
|
Any update to an argument will **not** be reflected back to the caller.
|
||||||
|
This can introduce subtle bugs, if not careful, especially when using the _method-call_ style.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn change(s) { // 's' is passed by value
|
fn change(s) { // 's' is passed by value
|
||||||
@ -2016,7 +2043,7 @@ fn change(s) { // 's' is passed by value
|
|||||||
}
|
}
|
||||||
|
|
||||||
let x = 500;
|
let x = 500;
|
||||||
x.change(); // de-sugars to change(x)
|
x.change(); // de-sugars to 'change(x)'
|
||||||
x == 500; // 'x' is NOT changed!
|
x == 500; // 'x' is NOT changed!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -630,6 +630,14 @@ impl<T: Variant + Clone> From<Vec<T>> for Dynamic {
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
impl<T: Variant + Clone> From<&[T]> for Dynamic {
|
||||||
|
fn from(value: &[T]) -> Self {
|
||||||
|
Self(Union::Array(Box::new(
|
||||||
|
value.iter().cloned().map(Dynamic::from).collect(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
impl<T: Variant + Clone> From<HashMap<String, T>> for Dynamic {
|
impl<T: Variant + Clone> From<HashMap<String, T>> for Dynamic {
|
||||||
fn from(value: HashMap<String, T>) -> Self {
|
fn from(value: HashMap<String, T>) -> Self {
|
||||||
|
@ -1550,14 +1550,8 @@ impl Engine {
|
|||||||
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
|
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
|
||||||
let def_val = def_val.as_ref();
|
let def_val = def_val.as_ref();
|
||||||
|
|
||||||
let mut arg_values = args_expr
|
// Handle eval
|
||||||
.iter()
|
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
||||||
.map(|expr| self.eval_expr(scope, state, lib, expr, level))
|
|
||||||
.collect::<Result<StaticVec<_>, _>>()?;
|
|
||||||
|
|
||||||
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
|
||||||
|
|
||||||
if name == KEYWORD_EVAL && args.len() == 1 && args.get(0).is::<ImmutableString>() {
|
|
||||||
let hash_fn =
|
let hash_fn =
|
||||||
calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
|
calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
@ -1567,7 +1561,8 @@ impl Engine {
|
|||||||
let pos = args_expr.get(0).position();
|
let pos = args_expr.get(0).position();
|
||||||
|
|
||||||
// Evaluate the text string as a script
|
// Evaluate the text string as a script
|
||||||
let result = self.eval_script_expr(scope, state, lib, args.pop(), pos);
|
let script = self.eval_expr(scope, state, lib, args_expr.get(0), level)?;
|
||||||
|
let result = self.eval_script_expr(scope, state, lib, &script, pos);
|
||||||
|
|
||||||
if scope.len() != prev_len {
|
if scope.len() != prev_len {
|
||||||
// IMPORTANT! If the eval defines new variables in the current scope,
|
// IMPORTANT! If the eval defines new variables in the current scope,
|
||||||
@ -1580,6 +1575,46 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normal function call - except for eval (handled above)
|
// Normal function call - except for eval (handled above)
|
||||||
|
let mut arg_values: StaticVec<Dynamic>;
|
||||||
|
let mut args: StaticVec<_>;
|
||||||
|
|
||||||
|
if args_expr.is_empty() {
|
||||||
|
// No arguments
|
||||||
|
args = Default::default();
|
||||||
|
} else {
|
||||||
|
// See if the first argument is a variable, if so, convert to method-call style
|
||||||
|
// in order to leverage potential &mut first argument and avoid cloning the value
|
||||||
|
match args_expr.get(0) {
|
||||||
|
// func(x, ...) -> x.func(...)
|
||||||
|
lhs @ Expr::Variable(_) => {
|
||||||
|
arg_values = args_expr
|
||||||
|
.iter()
|
||||||
|
.skip(1)
|
||||||
|
.map(|expr| self.eval_expr(scope, state, lib, expr, level))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
let (target, _, typ, pos) = search_scope(scope, state, lhs)?;
|
||||||
|
self.inc_operations(state, pos)?;
|
||||||
|
|
||||||
|
match typ {
|
||||||
|
ScopeEntryType::Module => unreachable!(),
|
||||||
|
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
args = once(target).chain(arg_values.iter_mut()).collect();
|
||||||
|
}
|
||||||
|
// func(..., ...)
|
||||||
|
_ => {
|
||||||
|
arg_values = args_expr
|
||||||
|
.iter()
|
||||||
|
.map(|expr| self.eval_expr(scope, state, lib, expr, level))
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
args = arg_values.iter_mut().collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, name, *native, *hash, args, false, def_val, *pos, level,
|
state, lib, name, *native, *hash, args, false, def_val, *pos, level,
|
||||||
@ -2012,8 +2047,8 @@ fn run_builtin_binary_op(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if args_type == TypeId::of::<INT>() {
|
if args_type == TypeId::of::<INT>() {
|
||||||
let x = x.downcast_ref::<INT>().unwrap().clone();
|
let x = *x.downcast_ref::<INT>().unwrap();
|
||||||
let y = y.downcast_ref::<INT>().unwrap().clone();
|
let y = *y.downcast_ref::<INT>().unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
match op {
|
match op {
|
||||||
@ -2054,8 +2089,8 @@ fn run_builtin_binary_op(
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
} else if args_type == TypeId::of::<bool>() {
|
} else if args_type == TypeId::of::<bool>() {
|
||||||
let x = x.downcast_ref::<bool>().unwrap().clone();
|
let x = *x.downcast_ref::<bool>().unwrap();
|
||||||
let y = y.downcast_ref::<bool>().unwrap().clone();
|
let y = *y.downcast_ref::<bool>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"&" => return Ok(Some((x && y).into())),
|
"&" => return Ok(Some((x && y).into())),
|
||||||
@ -2079,8 +2114,8 @@ fn run_builtin_binary_op(
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
} else if args_type == TypeId::of::<char>() {
|
} else if args_type == TypeId::of::<char>() {
|
||||||
let x = x.downcast_ref::<char>().unwrap().clone();
|
let x = *x.downcast_ref::<char>().unwrap();
|
||||||
let y = y.downcast_ref::<char>().unwrap().clone();
|
let y = *y.downcast_ref::<char>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"==" => return Ok(Some((x == y).into())),
|
"==" => return Ok(Some((x == y).into())),
|
||||||
@ -2102,8 +2137,8 @@ fn run_builtin_binary_op(
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
if args_type == TypeId::of::<FLOAT>() {
|
if args_type == TypeId::of::<FLOAT>() {
|
||||||
let x = x.downcast_ref::<FLOAT>().unwrap().clone();
|
let x = *x.downcast_ref::<FLOAT>().unwrap();
|
||||||
let y = y.downcast_ref::<FLOAT>().unwrap().clone();
|
let y = *y.downcast_ref::<FLOAT>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+" => return Ok(Some((x + y).into())),
|
"+" => return Ok(Some((x + y).into())),
|
||||||
@ -2142,7 +2177,7 @@ fn run_builtin_op_assignment(
|
|||||||
|
|
||||||
if args_type == TypeId::of::<INT>() {
|
if args_type == TypeId::of::<INT>() {
|
||||||
let x = x.downcast_mut::<INT>().unwrap();
|
let x = x.downcast_mut::<INT>().unwrap();
|
||||||
let y = y.downcast_ref::<INT>().unwrap().clone();
|
let y = *y.downcast_ref::<INT>().unwrap();
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
match op {
|
match op {
|
||||||
@ -2178,7 +2213,7 @@ fn run_builtin_op_assignment(
|
|||||||
}
|
}
|
||||||
} else if args_type == TypeId::of::<bool>() {
|
} else if args_type == TypeId::of::<bool>() {
|
||||||
let x = x.downcast_mut::<bool>().unwrap();
|
let x = x.downcast_mut::<bool>().unwrap();
|
||||||
let y = y.downcast_ref::<bool>().unwrap().clone();
|
let y = *y.downcast_ref::<bool>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"&=" => return Ok(Some(*x = *x && y)),
|
"&=" => return Ok(Some(*x = *x && y)),
|
||||||
@ -2199,7 +2234,7 @@ fn run_builtin_op_assignment(
|
|||||||
{
|
{
|
||||||
if args_type == TypeId::of::<FLOAT>() {
|
if args_type == TypeId::of::<FLOAT>() {
|
||||||
let x = x.downcast_mut::<FLOAT>().unwrap();
|
let x = x.downcast_mut::<FLOAT>().unwrap();
|
||||||
let y = y.downcast_ref::<FLOAT>().unwrap().clone();
|
let y = *y.downcast_ref::<FLOAT>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
"+=" => return Ok(Some(*x += y)),
|
"+=" => return Ok(Some(*x += y)),
|
||||||
|
@ -1044,6 +1044,9 @@ mod stat {
|
|||||||
|
|
||||||
/// Module resolution service that serves modules added into it.
|
/// Module resolution service that serves modules added into it.
|
||||||
///
|
///
|
||||||
|
/// `StaticModuleResolver` is a smart pointer to a `HashMap<String, Module>`.
|
||||||
|
/// It can simply be treated as `&HashMap<String, Module>`.
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -101,22 +101,22 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
lib.set_fn_2("+", |x: ImmutableString, y: Array| Ok(format!("{}{:?}", x, y)));
|
lib.set_fn_2_mut("+", |x: &mut ImmutableString, y: Array| Ok(format!("{}{:?}", x, y)));
|
||||||
lib.set_fn_2_mut("+", |x: &mut Array, y: ImmutableString| Ok(format!("{:?}{}", x, y)));
|
lib.set_fn_2_mut("+", |x: &mut Array, y: ImmutableString| Ok(format!("{:?}{}", x, y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
lib.set_fn_1("len", |s: ImmutableString| Ok(s.chars().count() as INT));
|
lib.set_fn_1_mut("len", |s: &mut ImmutableString| Ok(s.chars().count() as INT));
|
||||||
lib.set_fn_2(
|
lib.set_fn_2_mut(
|
||||||
"contains",
|
"contains",
|
||||||
|s: ImmutableString, ch: char| Ok(s.contains(ch)),
|
|s: &mut ImmutableString, ch: char| Ok(s.contains(ch)),
|
||||||
);
|
);
|
||||||
lib.set_fn_2(
|
lib.set_fn_2_mut(
|
||||||
"contains",
|
"contains",
|
||||||
|s: ImmutableString, find: ImmutableString| Ok(s.contains(find.as_str())),
|
|s: &mut ImmutableString, find: ImmutableString| Ok(s.contains(find.as_str())),
|
||||||
);
|
);
|
||||||
lib.set_fn_3(
|
lib.set_fn_3_mut(
|
||||||
"index_of",
|
"index_of",
|
||||||
|s: ImmutableString, ch: char, start: INT| {
|
|s: &mut ImmutableString, ch: char, start: INT| {
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
0
|
0
|
||||||
} else if (start as usize) >= s.chars().count() {
|
} else if (start as usize) >= s.chars().count() {
|
||||||
@ -131,17 +131,17 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
.unwrap_or(-1 as INT))
|
.unwrap_or(-1 as INT))
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
lib.set_fn_2(
|
lib.set_fn_2_mut(
|
||||||
"index_of",
|
"index_of",
|
||||||
|s: ImmutableString, ch: char| {
|
|s: &mut ImmutableString, ch: char| {
|
||||||
Ok(s.find(ch)
|
Ok(s.find(ch)
|
||||||
.map(|index| s[0..index].chars().count() as INT)
|
.map(|index| s[0..index].chars().count() as INT)
|
||||||
.unwrap_or(-1 as INT))
|
.unwrap_or(-1 as INT))
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
lib.set_fn_3(
|
lib.set_fn_3_mut(
|
||||||
"index_of",
|
"index_of",
|
||||||
|s: ImmutableString, find: ImmutableString, start: INT| {
|
|s: &mut ImmutableString, find: ImmutableString, start: INT| {
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
0
|
0
|
||||||
} else if (start as usize) >= s.chars().count() {
|
} else if (start as usize) >= s.chars().count() {
|
||||||
@ -156,9 +156,9 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
.unwrap_or(-1 as INT))
|
.unwrap_or(-1 as INT))
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
lib.set_fn_2(
|
lib.set_fn_2_mut(
|
||||||
"index_of",
|
"index_of",
|
||||||
|s: ImmutableString, find: ImmutableString| {
|
|s: &mut ImmutableString, find: ImmutableString| {
|
||||||
Ok(s.find(find.as_str())
|
Ok(s.find(find.as_str())
|
||||||
.map(|index| s[0..index].chars().count() as INT)
|
.map(|index| s[0..index].chars().count() as INT)
|
||||||
.unwrap_or(-1 as INT))
|
.unwrap_or(-1 as INT))
|
||||||
|
Loading…
Reference in New Issue
Block a user