Complete ImmutableString.
This commit is contained in:
parent
95e67c48bd
commit
b34d5fe3a1
@ -514,9 +514,9 @@ This is useful on some 32-bit targets where using 64-bit integers incur a perfor
|
|||||||
|
|
||||||
If no floating-point is needed or supported, use the [`no_float`] feature to remove it.
|
If no floating-point is needed or supported, use the [`no_float`] feature to remove it.
|
||||||
|
|
||||||
Strings in Rhai are _immutable_, meaning that they can be shared but not modified. In actual, the `ImmutableString` type
|
[Strings] in Rhai are _immutable_, meaning that they can be shared but not modified. In actual, the `ImmutableString` type
|
||||||
is implemented as an `Rc`- or `Arc`-wrapped `String`. Any modification done to a Rhai string will cause the string to be cloned
|
is an alias to `Rc<String>` or `Arc<String>` (depending on the [`sync`] feature).
|
||||||
and the modifications made to the copy.
|
Any modification done to a Rhai string will cause the string to be cloned and the modifications made to the copy.
|
||||||
|
|
||||||
The `to_string` function converts a standard type into a [string] for display purposes.
|
The `to_string` function converts a standard type into a [string] for display purposes.
|
||||||
|
|
||||||
@ -612,6 +612,7 @@ The following conversion traits are implemented for `Dynamic`:
|
|||||||
* `From<i64>` (`i32` if [`only_i32`])
|
* `From<i64>` (`i32` if [`only_i32`])
|
||||||
* `From<f64>` (if not [`no_float`])
|
* `From<f64>` (if not [`no_float`])
|
||||||
* `From<bool>`
|
* `From<bool>`
|
||||||
|
* `From<rhai::ImmutableString>`
|
||||||
* `From<String>`
|
* `From<String>`
|
||||||
* `From<char>`
|
* `From<char>`
|
||||||
* `From<Vec<T>>` (into an [array])
|
* `From<Vec<T>>` (into an [array])
|
||||||
|
10
RELEASES.md
10
RELEASES.md
@ -4,8 +4,8 @@ Rhai Release Notes
|
|||||||
Version 0.14.2
|
Version 0.14.2
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Regression
|
Regression fix
|
||||||
----------
|
--------------
|
||||||
|
|
||||||
* Do not optimize script with `eval_expression` - it is assumed to be one-off and short.
|
* Do not optimize script with `eval_expression` - it is assumed to be one-off and short.
|
||||||
|
|
||||||
@ -18,6 +18,10 @@ Breaking changes
|
|||||||
* Default maximum limit on levels of nested function calls is fine-tuned and set to a different value.
|
* Default maximum limit on levels of nested function calls is fine-tuned and set to a different value.
|
||||||
* Some operator functions are now built in (see _Speed enhancements_ below), so they are available even
|
* Some operator functions are now built in (see _Speed enhancements_ below), so they are available even
|
||||||
under `Engine::new_raw`.
|
under `Engine::new_raw`.
|
||||||
|
* Strings are now immutable. The type `rhai::ImmutableString` is used instead of `std::string::String`.
|
||||||
|
This is to avoid excessive cloning of strings. All native-Rust functions taking string parameters
|
||||||
|
should switch to `rhai::ImmutableString` (which is either `Rc<String>` or `Arc<String>` depending on
|
||||||
|
whether the `sync` feature is used).
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -35,6 +39,8 @@ Speed enhancements
|
|||||||
significant speed-up.
|
significant speed-up.
|
||||||
* Implementations of common operators for standard types are removed from the `ArithmeticPackage` and `LogicPackage`
|
* Implementations of common operators for standard types are removed from the `ArithmeticPackage` and `LogicPackage`
|
||||||
(and therefore the `CorePackage`) because they are now always available, even under `Engine::new_raw`.
|
(and therefore the `CorePackage`) because they are now always available, even under `Engine::new_raw`.
|
||||||
|
* Operator-assignment statements (e.g. `+=`) are now handled directly and much faster.
|
||||||
|
* Strings are now _immutable_ and use the `rhai::ImmutableString` type, eliminating large amounts of cloning.
|
||||||
|
|
||||||
|
|
||||||
Version 0.14.1
|
Version 0.14.1
|
||||||
|
20
src/any.rs
20
src/any.rs
@ -1,6 +1,5 @@
|
|||||||
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
||||||
|
|
||||||
use crate::fn_native::shared_unwrap;
|
|
||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
||||||
|
|
||||||
@ -21,9 +20,7 @@ use crate::stdlib::{
|
|||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt,
|
fmt,
|
||||||
rc::Rc,
|
|
||||||
string::String,
|
string::String,
|
||||||
sync::Arc,
|
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -417,13 +414,10 @@ impl Dynamic {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Unit(value) => unsafe_try_cast(value),
|
Union::Unit(value) => unsafe_try_cast(value),
|
||||||
Union::Bool(value) => unsafe_try_cast(value),
|
Union::Bool(value) => unsafe_try_cast(value),
|
||||||
Union::Str(value) => {
|
Union::Str(value) if type_id == TypeId::of::<ImmutableString>() => {
|
||||||
if type_id == TypeId::of::<ImmutableString>() {
|
|
||||||
unsafe_try_cast(value)
|
unsafe_try_cast(value)
|
||||||
} else {
|
|
||||||
unsafe_try_cast((*value).clone())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Union::Str(value) => unsafe_try_cast(value.into_owned()),
|
||||||
Union::Char(value) => unsafe_try_cast(value),
|
Union::Char(value) => unsafe_try_cast(value),
|
||||||
Union::Int(value) => unsafe_try_cast(value),
|
Union::Int(value) => unsafe_try_cast(value),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -464,12 +458,10 @@ impl Dynamic {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Unit(value) => unsafe_try_cast(value).unwrap(),
|
Union::Unit(value) => unsafe_try_cast(value).unwrap(),
|
||||||
Union::Bool(value) => unsafe_try_cast(value).unwrap(),
|
Union::Bool(value) => unsafe_try_cast(value).unwrap(),
|
||||||
Union::Str(value) => if type_id == TypeId::of::<ImmutableString>() {
|
Union::Str(value) if type_id == TypeId::of::<ImmutableString>() => {
|
||||||
unsafe_try_cast(value)
|
unsafe_try_cast(value).unwrap()
|
||||||
} else {
|
|
||||||
unsafe_try_cast((*value).clone())
|
|
||||||
}
|
}
|
||||||
.unwrap(),
|
Union::Str(value) => unsafe_try_cast(value.into_owned()).unwrap(),
|
||||||
Union::Char(value) => unsafe_try_cast(value).unwrap(),
|
Union::Char(value) => unsafe_try_cast(value).unwrap(),
|
||||||
Union::Int(value) => unsafe_try_cast(value).unwrap(),
|
Union::Int(value) => unsafe_try_cast(value).unwrap(),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -588,7 +580,7 @@ impl Dynamic {
|
|||||||
/// Returns the name of the actual type if the cast fails.
|
/// Returns the name of the actual type if the cast fails.
|
||||||
pub fn take_string(self) -> Result<String, &'static str> {
|
pub fn take_string(self) -> Result<String, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Str(s) => Ok(shared_unwrap(s).unwrap_or_else(|s| (*s).clone())),
|
Union::Str(s) => Ok(s.into_owned()),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2055,6 +2055,7 @@ fn run_builtin_binary_op(
|
|||||||
let y = y.downcast_ref::<ImmutableString>().unwrap();
|
let y = y.downcast_ref::<ImmutableString>().unwrap();
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
|
"+" => return Ok(Some((x + y).into())),
|
||||||
"==" => return Ok(Some((x == y).into())),
|
"==" => return Ok(Some((x == y).into())),
|
||||||
"!=" => return Ok(Some((x != y).into())),
|
"!=" => return Ok(Some((x != y).into())),
|
||||||
">" => return Ok(Some((x > y).into())),
|
">" => return Ok(Some((x > y).into())),
|
||||||
|
@ -19,6 +19,8 @@ pub type Shared<T> = Rc<T>;
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type Shared<T> = Arc<T>;
|
pub type Shared<T> = Arc<T>;
|
||||||
|
|
||||||
|
/// Consume a `Shared` resource and return a mutable reference to the wrapped value.
|
||||||
|
/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
|
||||||
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
{
|
{
|
||||||
@ -30,14 +32,19 @@ pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shared_unwrap<T: Clone>(value: Shared<T>) -> Result<T, Shared<T>> {
|
/// Consume a `Shared` resource, assuming that it is unique (i.e. not shared).
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the resource is shared (i.e. has other outstanding references).
|
||||||
|
pub fn shared_take<T: Clone>(value: Shared<T>) -> T {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
{
|
{
|
||||||
Rc::try_unwrap(value)
|
Rc::try_unwrap(value).map_err(|_| ()).unwrap()
|
||||||
}
|
}
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
{
|
{
|
||||||
Arc::try_unwrap(value)
|
Arc::try_unwrap(value).map_err(|_| ()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use crate::fn_native::{CallableFunction, FnAny, FnCallArgs};
|
|||||||
use crate::parser::FnAccess;
|
use crate::parser::FnAccess;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
|
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString};
|
use crate::stdlib::{any::TypeId, boxed::Box, mem};
|
||||||
|
|
||||||
/// Trait to register custom functions with the `Engine`.
|
/// Trait to register custom functions with the `Engine`.
|
||||||
pub trait RegisterFn<FN, ARGS, RET> {
|
pub trait RegisterFn<FN, ARGS, RET> {
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
//! ## Optional features
|
//! ## Optional features
|
||||||
//!
|
//!
|
||||||
//! | Feature | Description |
|
//! | Feature | Description |
|
||||||
//! | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
//! | ------------- | ----------------------------------------------------------------------------------------------------------------------------------|
|
||||||
//! | `unchecked` | Exclude arithmetic checking (such as overflows and division by zero). Beware that a bad script may panic the entire system! |
|
//! | `unchecked` | Exclude arithmetic checking (such as overflows and division by zero). Beware that a bad script may panic the entire system! |
|
||||||
//! | `no_function` | Disable script-defined functions if not needed. |
|
//! | `no_function` | Disable script-defined functions if not needed. |
|
||||||
//! | `no_index` | Disable arrays and indexing features if not needed. |
|
//! | `no_index` | Disable arrays and indexing features if not needed. |
|
||||||
|
@ -269,141 +269,69 @@ macro_rules! reg_op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
||||||
// Checked basic arithmetic
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
{
|
|
||||||
// reg_op!(lib, "+", add, INT);
|
|
||||||
// reg_op!(lib, "-", sub, INT);
|
|
||||||
// reg_op!(lib, "*", mul, INT);
|
|
||||||
// reg_op!(lib, "/", div, INT);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
// reg_op!(lib, "+", add, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
#[cfg(not(feature = "unchecked"))]
|
||||||
// reg_op!(lib, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
{
|
||||||
// reg_op!(lib, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
// Checked basic arithmetic
|
||||||
// reg_op!(lib, "/", div, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "+", add, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "+", add, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
reg_op!(lib, "-", sub, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "-", sub, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
reg_op!(lib, "*", mul, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "*", mul, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
reg_op!(lib, "/", div, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "/", div, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unchecked basic arithmetic
|
|
||||||
#[cfg(feature = "unchecked")]
|
|
||||||
{
|
|
||||||
// reg_op!(lib, "+", add_u, INT);
|
|
||||||
// reg_op!(lib, "-", sub_u, INT);
|
|
||||||
// reg_op!(lib, "*", mul_u, INT);
|
|
||||||
// reg_op!(lib, "/", div_u, INT);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
{
|
|
||||||
// reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Basic arithmetic for floating-point - no need to check
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
{
|
|
||||||
// reg_op!(lib, "+", add_u, f32, f64);
|
|
||||||
// reg_op!(lib, "-", sub_u, f32, f64);
|
|
||||||
// reg_op!(lib, "*", mul_u, f32, f64);
|
|
||||||
// reg_op!(lib, "/", div_u, f32, f64);
|
|
||||||
reg_op!(lib, "+", add_u, f32);
|
|
||||||
reg_op!(lib, "-", sub_u, f32);
|
|
||||||
reg_op!(lib, "*", mul_u, f32);
|
|
||||||
reg_op!(lib, "/", div_u, f32);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bit operations
|
|
||||||
// reg_op!(lib, "|", binary_or, INT);
|
|
||||||
// reg_op!(lib, "&", binary_and, INT);
|
|
||||||
// reg_op!(lib, "^", binary_xor, INT);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
{
|
|
||||||
// reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checked bit shifts
|
// Checked bit shifts
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
{
|
|
||||||
// reg_op!(lib, "<<", shl, INT);
|
|
||||||
// reg_op!(lib, ">>", shr, INT);
|
|
||||||
// reg_op!(lib, "%", modulo, INT);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
|
||||||
#[cfg(not(feature = "only_i64"))]
|
|
||||||
{
|
|
||||||
// reg_op!(lib, "<<", shl, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, ">>", shr, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "<<", shl, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "<<", shl, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
reg_op!(lib, ">>", shr, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, ">>", shr, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
reg_op!(lib, "%", modulo, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "%", modulo, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Unchecked bit shifts
|
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
{
|
{
|
||||||
// reg_op!(lib, "<<", shl_u, INT, INT);
|
// Unchecked basic arithmetic
|
||||||
// reg_op!(lib, ">>", shr_u, INT, INT);
|
reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
// reg_op!(lib, "%", modulo_u, INT);
|
reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
#[cfg(not(feature = "only_i32"))]
|
reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
#[cfg(not(feature = "only_i64"))]
|
// Unchecked bit shifts
|
||||||
{
|
|
||||||
// reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checked power
|
// Basic arithmetic for floating-point - no need to check
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
// lib.set_fn_2("~", pow_i_i);
|
reg_op!(lib, "+", add_u, f32);
|
||||||
|
reg_op!(lib, "-", sub_u, f32);
|
||||||
|
reg_op!(lib, "*", mul_u, f32);
|
||||||
|
reg_op!(lib, "/", div_u, f32);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
{
|
||||||
|
reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
|
reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
// Checked power
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
lib.set_fn_2("~", pow_f_i);
|
lib.set_fn_2("~", pow_f_i);
|
||||||
}
|
|
||||||
|
|
||||||
// Unchecked power
|
// Unchecked power
|
||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
{
|
|
||||||
// lib.set_fn_2("~", pow_i_i_u);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
lib.set_fn_2("~", pow_f_i_u);
|
lib.set_fn_2("~", pow_f_i_u);
|
||||||
}
|
|
||||||
|
|
||||||
// Floating-point modulo and power
|
// Floating-point modulo and power
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
{
|
|
||||||
// reg_op!(lib, "%", modulo_u, f32, f64);
|
|
||||||
reg_op!(lib, "%", modulo_u, f32);
|
reg_op!(lib, "%", modulo_u, f32);
|
||||||
// lib.set_fn_2("~", pow_f_f);
|
|
||||||
|
// Floating-point unary
|
||||||
|
reg_unary!(lib, "-", neg_u, f32, f64);
|
||||||
|
reg_unary!(lib, "abs", abs_u, f32, f64);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checked unary
|
// Checked unary
|
||||||
@ -433,11 +361,4 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
|||||||
reg_unary!(lib, "abs", abs_u, i8, i16, i32, i64, i128);
|
reg_unary!(lib, "abs", abs_u, i8, i16, i32, i64, i128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Floating-point unary
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
{
|
|
||||||
reg_unary!(lib, "-", neg_u, f32, f64);
|
|
||||||
reg_unary!(lib, "abs", abs_u, f32, f64);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::module::FuncReturn;
|
use crate::module::FuncReturn;
|
||||||
use crate::parser::{ImmutableString, INT};
|
|
||||||
|
|
||||||
// Comparison operators
|
// Comparison operators
|
||||||
pub fn lt<T: PartialOrd>(x: T, y: T) -> FuncReturn<bool> {
|
pub fn lt<T: PartialOrd>(x: T, y: T) -> FuncReturn<bool> {
|
||||||
@ -23,12 +22,6 @@ pub fn ne<T: PartialEq>(x: T, y: T) -> FuncReturn<bool> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Logic operators
|
// Logic operators
|
||||||
fn and(x: bool, y: bool) -> FuncReturn<bool> {
|
|
||||||
Ok(x && y)
|
|
||||||
}
|
|
||||||
fn or(x: bool, y: bool) -> FuncReturn<bool> {
|
|
||||||
Ok(x || y)
|
|
||||||
}
|
|
||||||
fn not(x: bool) -> FuncReturn<bool> {
|
fn not(x: bool) -> FuncReturn<bool> {
|
||||||
Ok(!x)
|
Ok(!x)
|
||||||
}
|
}
|
||||||
@ -40,30 +33,9 @@ macro_rules! reg_op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||||
// reg_op!(lib, "<", lt, INT, char);
|
|
||||||
// reg_op!(lib, "<=", lte, INT, char);
|
|
||||||
// reg_op!(lib, ">", gt, INT, char);
|
|
||||||
// reg_op!(lib, ">=", gte, INT, char);
|
|
||||||
// reg_op!(lib, "==", eq, INT, char, bool, ());
|
|
||||||
// reg_op!(lib, "!=", ne, INT, char, bool, ());
|
|
||||||
|
|
||||||
// Special versions for strings - at least avoid copying the first string
|
|
||||||
// lib.set_fn_2("<", |x: ImmutableString, y: ImmutableString| Ok(*x < y));
|
|
||||||
// lib.set_fn_2("<=", |x: ImmutableString, y: ImmutableString| Ok(*x <= y));
|
|
||||||
// lib.set_fn_2(">", |x: ImmutableString, y: ImmutableString| Ok(*x > y));
|
|
||||||
// lib.set_fn_2(">=", |x: ImmutableString, y: ImmutableString| Ok(*x >= y));
|
|
||||||
// lib.set_fn_2("==", |x: ImmutableString, y: ImmutableString| Ok(*x == y));
|
|
||||||
// lib.set_fn_2("!=", |x: ImmutableString, y: ImmutableString| Ok(*x != y));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
#[cfg(not(feature = "only_i64"))]
|
#[cfg(not(feature = "only_i64"))]
|
||||||
{
|
{
|
||||||
// reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
// reg_op!(lib, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
|
||||||
reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||||
@ -74,12 +46,6 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
{
|
{
|
||||||
// reg_op!(lib, "<", lt, f32, f64);
|
|
||||||
// reg_op!(lib, "<=", lte, f32, f64);
|
|
||||||
// reg_op!(lib, ">", gt, f32, f64);
|
|
||||||
// reg_op!(lib, ">=", gte, f32, f64);
|
|
||||||
// reg_op!(lib, "==", eq, f32, f64);
|
|
||||||
// reg_op!(lib, "!=", ne, f32, f64);
|
|
||||||
reg_op!(lib, "<", lt, f32);
|
reg_op!(lib, "<", lt, f32);
|
||||||
reg_op!(lib, "<=", lte, f32);
|
reg_op!(lib, "<=", lte, f32);
|
||||||
reg_op!(lib, ">", gt, f32);
|
reg_op!(lib, ">", gt, f32);
|
||||||
@ -88,12 +54,5 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
|||||||
reg_op!(lib, "!=", ne, f32);
|
reg_op!(lib, "!=", ne, f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `&&` and `||` are treated specially as they short-circuit.
|
|
||||||
// They are implemented as special `Expr` instances, not function calls.
|
|
||||||
//reg_op!(lib, "||", or, bool);
|
|
||||||
//reg_op!(lib, "&&", and, bool);
|
|
||||||
|
|
||||||
// lib.set_fn_2("|", or);
|
|
||||||
// lib.set_fn_2("&", and);
|
|
||||||
lib.set_fn_1("!", not);
|
lib.set_fn_1("!", not);
|
||||||
});
|
});
|
||||||
|
@ -6,10 +6,7 @@ use crate::engine::Map;
|
|||||||
use crate::module::FuncReturn;
|
use crate::module::FuncReturn;
|
||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{string::ToString, vec::Vec};
|
||||||
string::{String, ToString},
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn map_get_keys(map: &mut Map) -> FuncReturn<Vec<Dynamic>> {
|
fn map_get_keys(map: &mut Map) -> FuncReturn<Vec<Dynamic>> {
|
||||||
Ok(map.iter().map(|(k, _)| k.to_string().into()).collect())
|
Ok(map.iter().map(|(k, _)| k.to_string().into()).collect())
|
||||||
|
@ -4,7 +4,7 @@ use crate::fn_native::{CallableFunction, IteratorFn, Shared};
|
|||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::utils::StaticVec;
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc, vec::Vec};
|
use crate::stdlib::any::TypeId;
|
||||||
|
|
||||||
pub(crate) mod arithmetic;
|
pub(crate) mod arithmetic;
|
||||||
mod array_basic;
|
mod array_basic;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::engine::{FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
|
use crate::engine::{FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
|
||||||
use crate::fn_native::shared_make_mut;
|
|
||||||
use crate::module::FuncReturn;
|
use crate::module::FuncReturn;
|
||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
|
|
||||||
@ -13,7 +12,7 @@ use crate::engine::Map;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
fmt::{Debug, Display},
|
fmt::{Debug, Display},
|
||||||
format,
|
format,
|
||||||
string::{String, ToString},
|
string::ToString,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register print and debug
|
// Register print and debug
|
||||||
@ -79,58 +78,9 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
|
|||||||
lib.set_fn_1_mut(KEYWORD_DEBUG, format_map);
|
lib.set_fn_1_mut(KEYWORD_DEBUG, format_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
lib.set_fn_2(
|
lib.set_fn_2("+", |s: ImmutableString, ch: char| Ok(s + ch));
|
||||||
"+",
|
lib.set_fn_2_mut("+=", |s: &mut ImmutableString, ch: char| { *s += ch; Ok(()) });
|
||||||
|s: ImmutableString, ch: char| {
|
lib.set_fn_2_mut("append", |s: &mut ImmutableString, ch: char| { *s += ch; Ok(()) });
|
||||||
if s.is_empty() {
|
lib.set_fn_2_mut("+=", |s: &mut ImmutableString, s2: ImmutableString| { *s += &s2; Ok(()) });
|
||||||
return Ok(ch.to_string().into());
|
lib.set_fn_2_mut("append", |s: &mut ImmutableString, s2: ImmutableString| { *s += &s2; Ok(()) });
|
||||||
}
|
|
||||||
|
|
||||||
let mut s = (*s).clone();
|
|
||||||
s.push(ch);
|
|
||||||
Ok(s)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
lib.set_fn_2(
|
|
||||||
"+",
|
|
||||||
|s:ImmutableString, s2:ImmutableString| {
|
|
||||||
if s.is_empty() {
|
|
||||||
return Ok(s2);
|
|
||||||
} else if s2.is_empty() {
|
|
||||||
return Ok(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut s = (*s).clone();
|
|
||||||
s.push_str(s2.as_str());
|
|
||||||
Ok(s.into())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
lib.set_fn_2_mut("+=", |s: &mut ImmutableString, ch: char| {
|
|
||||||
shared_make_mut(s).push(ch);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
lib.set_fn_2_mut("append", |s: &mut ImmutableString, ch: char| {
|
|
||||||
shared_make_mut(s).push(ch);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
lib.set_fn_2_mut(
|
|
||||||
"+=",
|
|
||||||
|s: &mut ImmutableString, s2: ImmutableString| {
|
|
||||||
if !s2.is_empty() {
|
|
||||||
shared_make_mut(s).push_str(s2.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
);
|
|
||||||
lib.set_fn_2_mut(
|
|
||||||
"append",
|
|
||||||
|s: &mut ImmutableString, s2: ImmutableString| {
|
|
||||||
if !s2.is_empty() {
|
|
||||||
shared_make_mut(s).push_str(s2.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::fn_native::shared_make_mut;
|
|
||||||
use crate::module::FuncReturn;
|
use crate::module::FuncReturn;
|
||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
use crate::utils::StaticVec;
|
use crate::utils::StaticVec;
|
||||||
@ -48,12 +47,12 @@ fn sub_string(s: ImmutableString, start: INT, len: INT) -> FuncReturn<ImmutableS
|
|||||||
}
|
}
|
||||||
fn crop_string(s: &mut ImmutableString, start: INT, len: INT) -> FuncReturn<()> {
|
fn crop_string(s: &mut ImmutableString, start: INT, len: INT) -> FuncReturn<()> {
|
||||||
let offset = if s.is_empty() || len <= 0 {
|
let offset = if s.is_empty() || len <= 0 {
|
||||||
shared_make_mut(s).clear();
|
s.make_mut().clear();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else if start < 0 {
|
} else if start < 0 {
|
||||||
0
|
0
|
||||||
} else if (start as usize) >= s.chars().count() {
|
} else if (start as usize) >= s.chars().count() {
|
||||||
shared_make_mut(s).clear();
|
s.make_mut().clear();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
start as usize
|
start as usize
|
||||||
@ -67,7 +66,7 @@ fn crop_string(s: &mut ImmutableString, start: INT, len: INT) -> FuncReturn<()>
|
|||||||
len as usize
|
len as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
let copy = shared_make_mut(s);
|
let copy = s.make_mut();
|
||||||
copy.clear();
|
copy.clear();
|
||||||
copy.extend(chars.iter().skip(offset).take(len));
|
copy.extend(chars.iter().skip(offset).take(len));
|
||||||
|
|
||||||
@ -166,17 +165,17 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
lib.set_fn_1_mut("clear", |s: &mut ImmutableString| {
|
lib.set_fn_1_mut("clear", |s: &mut ImmutableString| {
|
||||||
shared_make_mut(s).clear();
|
s.make_mut().clear();
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
lib.set_fn_2_mut("append", |s: &mut ImmutableString, ch: char| {
|
lib.set_fn_2_mut("append", |s: &mut ImmutableString, ch: char| {
|
||||||
shared_make_mut(s).push(ch);
|
s.make_mut().push(ch);
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
lib.set_fn_2_mut(
|
lib.set_fn_2_mut(
|
||||||
"append",
|
"append",
|
||||||
|s: &mut ImmutableString, add: ImmutableString| {
|
|s: &mut ImmutableString, add: ImmutableString| {
|
||||||
shared_make_mut(s).push_str(add.as_str());
|
s.make_mut().push_str(add.as_str());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -198,11 +197,11 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
|s: &mut ImmutableString, len: INT| {
|
|s: &mut ImmutableString, len: INT| {
|
||||||
if len > 0 {
|
if len > 0 {
|
||||||
let chars: StaticVec<_> = s.chars().collect();
|
let chars: StaticVec<_> = s.chars().collect();
|
||||||
let copy = shared_make_mut(s);
|
let copy = s.make_mut();
|
||||||
copy.clear();
|
copy.clear();
|
||||||
copy.extend(chars.into_iter().take(len as usize));
|
copy.extend(chars.into_iter().take(len as usize));
|
||||||
} else {
|
} else {
|
||||||
shared_make_mut(s).clear();
|
s.make_mut().clear();
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
@ -210,7 +209,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
|||||||
lib.set_fn_3_mut(
|
lib.set_fn_3_mut(
|
||||||
"pad",
|
"pad",
|
||||||
|s: &mut ImmutableString, len: INT, ch: char| {
|
|s: &mut ImmutableString, len: INT, ch: char| {
|
||||||
let copy = shared_make_mut(s);
|
let copy = s.make_mut();
|
||||||
for _ in 0..copy.chars().count() - len as usize {
|
for _ in 0..copy.chars().count() - len as usize {
|
||||||
copy.push(ch);
|
copy.push(ch);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ use crate::any::{Dynamic, Union};
|
|||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
use crate::engine::{make_getter, make_setter, Engine, FunctionsLib};
|
use crate::engine::{make_getter, make_setter, Engine, FunctionsLib};
|
||||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||||
use crate::fn_native::Shared;
|
|
||||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::{Position, Token, TokenIterator};
|
use crate::token::{Position, Token, TokenIterator};
|
||||||
@ -49,11 +48,10 @@ pub type INT = i32;
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub type FLOAT = f64;
|
pub type FLOAT = f64;
|
||||||
|
|
||||||
/// The system immutable string type.
|
|
||||||
pub type ImmutableString = Shared<String>;
|
|
||||||
|
|
||||||
type PERR = ParseErrorType;
|
type PERR = ParseErrorType;
|
||||||
|
|
||||||
|
pub use crate::utils::ImmutableString;
|
||||||
|
|
||||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||||
///
|
///
|
||||||
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||||
|
276
src/utils.rs
276
src/utils.rs
@ -4,14 +4,18 @@
|
|||||||
//!
|
//!
|
||||||
//! The `StaticVec` type has some `unsafe` blocks to handle conversions between `MaybeUninit` and regular types.
|
//! The `StaticVec` type has some `unsafe` blocks to handle conversions between `MaybeUninit` and regular types.
|
||||||
|
|
||||||
|
use crate::fn_native::{shared_make_mut, shared_take, Shared};
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
|
borrow::Borrow,
|
||||||
fmt,
|
fmt,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter::FromIterator,
|
iter::FromIterator,
|
||||||
mem,
|
mem,
|
||||||
mem::MaybeUninit,
|
mem::MaybeUninit,
|
||||||
ops::{Drop, Index, IndexMut},
|
ops::{Add, AddAssign, Deref, Drop, Index, IndexMut},
|
||||||
|
str::FromStr,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -560,3 +564,273 @@ impl<T> From<Vec<T>> for StaticVec<T> {
|
|||||||
arr
|
arr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The system immutable string type.
|
||||||
|
///
|
||||||
|
/// An `ImmutableString` wraps an `Rc<String>` (or `Arc<String>` under the `sync` feature)
|
||||||
|
/// so that it can be simply shared and not cloned.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::ImmutableString;
|
||||||
|
///
|
||||||
|
/// let s1: ImmutableString = "hello".into();
|
||||||
|
///
|
||||||
|
/// // No actual cloning of the string is involved below.
|
||||||
|
/// let s2 = s1.clone();
|
||||||
|
/// let s3 = s2.clone();
|
||||||
|
///
|
||||||
|
/// assert_eq!(s1, s2);
|
||||||
|
///
|
||||||
|
/// // Clones the underlying string (because it is already shared) and extracts it.
|
||||||
|
/// let mut s: String = s1.into_owned();
|
||||||
|
///
|
||||||
|
/// // Changing the clone has no impact on the previously shared version.
|
||||||
|
/// s.push_str(", world!");
|
||||||
|
///
|
||||||
|
/// // The old version still exists.
|
||||||
|
/// assert_eq!(s2, s3);
|
||||||
|
/// assert_eq!(s2.as_str(), "hello");
|
||||||
|
///
|
||||||
|
/// // Not equals!
|
||||||
|
/// assert_ne!(s2.as_str(), s.as_str());
|
||||||
|
/// assert_eq!(s, "hello, world!");
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
||||||
|
pub struct ImmutableString(Shared<String>);
|
||||||
|
|
||||||
|
impl Deref for ImmutableString {
|
||||||
|
type Target = String;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<String> for ImmutableString {
|
||||||
|
fn as_ref(&self) -> &String {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<str> for ImmutableString {
|
||||||
|
fn borrow(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for ImmutableString {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Self(value.to_string().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<String> for ImmutableString {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Box<String>> for ImmutableString {
|
||||||
|
fn from(value: Box<String>) -> Self {
|
||||||
|
Self(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ImmutableString> for String {
|
||||||
|
fn from(value: ImmutableString) -> Self {
|
||||||
|
value.into_owned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ImmutableString {
|
||||||
|
type Err = ();
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Self(s.to_string().into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromIterator<char> for ImmutableString {
|
||||||
|
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||||
|
Self(iter.into_iter().collect::<String>().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||||
|
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||||
|
Self(iter.into_iter().cloned().collect::<String>().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||||
|
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||||
|
Self(iter.into_iter().collect::<String>().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromIterator<String> for ImmutableString {
|
||||||
|
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
||||||
|
Self(iter.into_iter().collect::<String>().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ImmutableString {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(self.0.as_str(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ImmutableString {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(self.0.as_str(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(mut self, rhs: Self) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs
|
||||||
|
} else {
|
||||||
|
self.make_mut().push_str(rhs.0.as_str());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self.clone()
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs.clone()
|
||||||
|
} else {
|
||||||
|
let mut s = self.clone();
|
||||||
|
s.make_mut().push_str(rhs.0.as_str());
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign<&ImmutableString> for ImmutableString {
|
||||||
|
fn add_assign(&mut self, rhs: &ImmutableString) {
|
||||||
|
if !rhs.is_empty() {
|
||||||
|
if self.is_empty() {
|
||||||
|
self.0 = rhs.0.clone();
|
||||||
|
} else {
|
||||||
|
self.make_mut().push_str(rhs.0.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<&str> for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(mut self, rhs: &str) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self
|
||||||
|
} else {
|
||||||
|
self.make_mut().push_str(rhs);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<&str> for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
fn add(self, rhs: &str) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self.clone()
|
||||||
|
} else {
|
||||||
|
let mut s = self.clone();
|
||||||
|
s.make_mut().push_str(rhs);
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign<&str> for ImmutableString {
|
||||||
|
fn add_assign(&mut self, rhs: &str) {
|
||||||
|
if !rhs.is_empty() {
|
||||||
|
self.make_mut().push_str(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<String> for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(mut self, rhs: String) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs.into()
|
||||||
|
} else {
|
||||||
|
self.make_mut().push_str(&rhs);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<String> for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
fn add(self, rhs: String) -> Self::Output {
|
||||||
|
if rhs.is_empty() {
|
||||||
|
self.clone()
|
||||||
|
} else if self.is_empty() {
|
||||||
|
rhs.into()
|
||||||
|
} else {
|
||||||
|
let mut s = self.clone();
|
||||||
|
s.make_mut().push_str(&rhs);
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<char> for ImmutableString {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn add(mut self, rhs: char) -> Self::Output {
|
||||||
|
self.make_mut().push(rhs);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<char> for &ImmutableString {
|
||||||
|
type Output = ImmutableString;
|
||||||
|
|
||||||
|
fn add(self, rhs: char) -> Self::Output {
|
||||||
|
let mut s = self.clone();
|
||||||
|
s.make_mut().push(rhs);
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign<char> for ImmutableString {
|
||||||
|
fn add_assign(&mut self, rhs: char) {
|
||||||
|
self.make_mut().push(rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImmutableString {
|
||||||
|
/// Consume the `ImmutableString` and convert it into a `String`.
|
||||||
|
/// If there are other references to the same string, a cloned copy is returned.
|
||||||
|
pub fn into_owned(mut self) -> String {
|
||||||
|
self.make_mut(); // Make sure it is unique reference
|
||||||
|
shared_take(self.0) // Should succeed
|
||||||
|
}
|
||||||
|
/// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references).
|
||||||
|
/// Then return a mutable reference to the `String`.
|
||||||
|
pub fn make_mut(&mut self) -> &mut String {
|
||||||
|
shared_make_mut(&mut self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user