The Molt Value Type
The Value type is the standard representation in Rust of Molt values. In the Tcl
language, "everything is a string"; which is to say, every value can be represented
as a string. Many values—e.g., numbers and lists—also have a binary data representation,
but a single value can move from one binary data representation to another depending
on how it is used by the user. Consider the following:
set x [expr {2 + 3}] ;# It's the integer 5.
puts "x=$x" ;# It's converted to a string.
set y [lindex $x 0] ;# It's converted to a one-element list.
Initially, the variable x contains a Value with only a data representation, the
integer 5. Then puts needs it as a string, and so the Value acquires a string
representation as well, but retains its integer representation. Then lindex needs
to look at it as a list, so the string is parsed into a Molt list and the 0th element
is returned. The integer representation is lost and replaced by the list
representation. The Value type manages all of these transformations internally, with the effect that string-to-binary and binary-to-string conversions happen only when
absolutely necessary.
Note: A Value's string representation is never lost, once acquired: semantically,
Values are immutable. The data transformations that go on under the hood are an
aid to performance, but in principle the value is unchanged.
Creating Values
Values can be created easily from a variety of kinds of input:
let a = Value::from("abc"); // &str
let b = Value::from("def".to_string()); // String
let c = Value::from(123); // MoltInt (i64)
let d = Value::from(45.67); // MoltFloat (f64)
let e = Value::from(true); // bool
let f = Value::from(&[Value::from(1), Value::from(2)]); // &[Value]
And in fact, a Value can contain any Rust type that supports the Display,
Debug, and FromStr types via the Value::from_other method. Such types are
called "external types" in the Molt documentation set.
Cloning Values
Because Values are immutable, they have been designed to be cheaply and easy cloned
with reference counting via the standard Rc type.
Retrieving Data from Values
It is always possible to retrieve a Value's data as a string:
let value = Value::from(5);
let text: String = value.to_string();
assert_eq!(&text, "5");
The to_string method creates a brand new String in the usual way; it is usually better to
use as_str, which returns an &str:
let value = Value::from(5);
let text = value.as_str();
assert_eq!(text, "5");
It is also possible to retrieve data representations; but since this isn't guaranteed to
work the relevant methods all return Result<_,ResultCode>. (See
The MoltResult type for a discussion of ResultCodes.) For
example,
let value = Value::from("123");
let x = value.as_int()?;
assert_eq!(x, 123);
Retrieving Values of External Types
Values of external types can be retrieved as well using the Value::as_copy or
Value::as_other method, depending on whether the type implements the Copy
trait. These are different than their peers, in that they return Option<T>
and Option<Rc<T>> rather than Result<T,ResultCode> or Result<Rc<T>,ResultCode>.
The reason is that Molt doesn't know what the appropriate
error message should be when it finds a value it can't convert into the external
type T and so returns None, leaving the error handling up to the client.
For this reason, when using an external type MyType with Molt it is usual to define a
function that converts a Value to a Result<MyType,ResultCode>. If MyType is an
enum, for example, you might write this:
# #![allow(unused_variables)] #fn main() { impl MyType { /// A convenience: retrieves the enumerated value, converting it from /// `Option<MyType>` into `Result<MyType,ResultCode>`. pub fn from_molt(value: &Value) -> Result<Self, ResultCode> { if let Some(x) = value.as_copy::<MyType>() { Ok(x) } else { Err(ResultCode::Error(Value::from("Not a MyType string"))) } } } #}