diff --git a/README.md b/README.md index 97e27192..fbb08cce 100644 --- a/README.md +++ b/README.md @@ -733,12 +733,14 @@ fn main() -> Result<(), EvalAltResult> // First create the state let mut scope = Scope::new(); - // Then push some initialized variables into the state - // NOTE: Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64. - // Better stick to them or it gets hard working with the script. + // Then push (i.e. add) some initialized variables into the state. + // Remember the system number types in Rhai are i64 (i32 if 'only_i32') ond f64. + // Better stick to them or it gets hard working with the script. scope.push("y", 42_i64); scope.push("z", 999_i64); - scope.push("s", "hello, world!".to_string()); // remember to use 'String', not '&str' + + // 'set_value' adds a variable when one doesn't exist + scope.set_value("s", "hello, world!".to_string()); // remember to use 'String', not '&str' // First invocation engine.eval_with_scope::<()>(&mut scope, r" @@ -749,10 +751,14 @@ fn main() -> Result<(), EvalAltResult> // Second invocation using the same state let result = engine.eval_with_scope::(&mut scope, "x")?; - println!("result: {}", result); // prints 979 + println!("result: {}", result); // prints 979 - // Variable y is changed in the script - assert_eq!(scope.get_value::("y").expect("variable x should exist"), 1); + // Variable y is changed in the script - read it with 'get_value' + assert_eq!(scope.get_value::("y").expect("variable y should exist"), 1); + + // We can modify scope variables directly with 'set_value' + scope.set_value("y", 42_i64); + assert_eq!(scope.get_value::("y").expect("variable y should exist"), 42); Ok(()) } diff --git a/src/scope.rs b/src/scope.rs index 14f53cb5..6b44ec6e 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -179,16 +179,44 @@ impl<'a> Scope<'a> { } /// Get the value of an entry in the Scope, starting from the last. - pub fn get_value(&self, key: &str) -> Option { + pub fn get_value(&self, name: &str) -> Option { self.0 .iter() .enumerate() .rev() // Always search a Scope in reverse order - .find(|(_, Entry { name, .. })| name == key) + .find(|(_, Entry { name: key, .. })| name == key) .and_then(|(_, Entry { value, .. })| value.downcast_ref::()) .map(T::clone) } + /// Update the value of the named variable. + /// Search starts from the last, and only the last variable matching the specified name is updated. + /// If no variable matching the specified name is found, a new variable is added. + /// + /// # Panics + /// + /// Panics when trying to update the value of a constant. + pub fn set_value(&mut self, name: &'a str, value: T) { + match self.get(name) { + Some(( + EntryRef { + typ: EntryType::Constant, + .. + }, + _, + )) => panic!("variable {} is constant", name), + Some(( + EntryRef { + index, + typ: EntryType::Normal, + .. + }, + _, + )) => self.0.get_mut(index).unwrap().value = value.into_dynamic(), + None => self.push(name, value.into_dynamic()), + } + } + /// Get a mutable reference to an entry in the Scope. pub(crate) fn get_mut(&mut self, key: EntryRef) -> &mut Dynamic { let entry = self.0.get_mut(key.index).expect("invalid index in Scope"); diff --git a/tests/var_scope.rs b/tests/var_scope.rs index 6b660d34..39cf5b76 100644 --- a/tests/var_scope.rs +++ b/tests/var_scope.rs @@ -9,8 +9,12 @@ fn test_var_scope() -> Result<(), EvalAltResult> { assert_eq!(engine.eval_with_scope::(&mut scope, "x")?, 9); engine.eval_with_scope::<()>(&mut scope, "x = x + 1; x = x + 2;")?; assert_eq!(engine.eval_with_scope::(&mut scope, "x")?, 12); + + scope.set_value("x", 42 as INT); + assert_eq!(engine.eval_with_scope::(&mut scope, "x")?, 42); + engine.eval_with_scope::<()>(&mut scope, "{let x = 3}")?; - assert_eq!(engine.eval_with_scope::(&mut scope, "x")?, 12); + assert_eq!(engine.eval_with_scope::(&mut scope, "x")?, 42); Ok(()) }