Generic Values
Some GObject-related functions rely on generic values for their arguments or return parameters.
Since GObject introspection works through a C interface, these functions cannot rely on any powerful Rust concepts.
In these cases glib::Value
or glib::Variant
are used.
Value
Let's start with Value
.
Conceptually, a Value
is similar to a Rust enum
defined like this:
enum Value <T> {
bool(bool),
i8(i8),
i32(i32),
u32(u32),
i64(i64),
u64(u64),
f32(f32),
f64(f64),
// boxed types
String(Option<String>),
Object(Option<dyn IsA<glib::Object>>),
}
For example, this is how you would use a Value
representing an i32
.
Filename: listings/g_object_values/1/main.rs
use gtk::prelude::*;
fn main() {
// Store `i32` as `Value`
let integer_value = 10.to_value();
// Retrieve `i32` from `Value`
let integer = integer_value
.get::<i32>()
.expect("The value needs to be of type `i32`.");
// Check if the retrieved value is correct
assert_eq!(integer, 10);
// Store string as `Value`
let string_value = "Hello!".to_value();
// Retrieve `String` from `Value`
let string = string_value
.get::<String>()
.expect("The value needs to be of type `String`.");
// Check if the retrieved value is correct
assert_eq!(string, "Hello!".to_string());
// Store `Option<String>` as `Value`
let string_some_value = "Hello!".to_value();
let string_none_value = None::<String>.to_value();
// Retrieve `String` from `Value`
let string_some = string_some_value
.get::<Option<String>>()
.expect("The value needs to be of type `Option<String>`.");
let string_none = string_none_value
.get::<Option<String>>()
.expect("The value needs to be of type `Option<String>`.");
// Check if the retrieved value is correct
assert_eq!(string_some, Some("Hello!".to_string()));
assert_eq!(string_none, None);
}
Also note that in the enum
above boxed types such as String
or glib::Object
are wrapped in an Option
.
This comes from C, where every boxed type can potentially be None
(or NULL
in C terms).
You can still access it the same way as with the i32
above.
get
will then not only return Err
if you specified the wrong type, but also if the Value
represents None
.
Filename: listings/g_object_values/1/main.rs
use gtk::prelude::*;
fn main() {
// Store `i32` as `Value`
let integer_value = 10.to_value();
// Retrieve `i32` from `Value`
let integer = integer_value
.get::<i32>()
.expect("The value needs to be of type `i32`.");
// Check if the retrieved value is correct
assert_eq!(integer, 10);
// Store string as `Value`
let string_value = "Hello!".to_value();
// Retrieve `String` from `Value`
let string = string_value
.get::<String>()
.expect("The value needs to be of type `String`.");
// Check if the retrieved value is correct
assert_eq!(string, "Hello!".to_string());
// Store `Option<String>` as `Value`
let string_some_value = "Hello!".to_value();
let string_none_value = None::<String>.to_value();
// Retrieve `String` from `Value`
let string_some = string_some_value
.get::<Option<String>>()
.expect("The value needs to be of type `Option<String>`.");
let string_none = string_none_value
.get::<Option<String>>()
.expect("The value needs to be of type `Option<String>`.");
// Check if the retrieved value is correct
assert_eq!(string_some, Some("Hello!".to_string()));
assert_eq!(string_none, None);
}
If you want to differentiate between specifying the wrong type and a Value
representing None
, just call get::<Option<T>>
instead.
Filename: listings/g_object_values/1/main.rs
use gtk::prelude::*;
fn main() {
// Store `i32` as `Value`
let integer_value = 10.to_value();
// Retrieve `i32` from `Value`
let integer = integer_value
.get::<i32>()
.expect("The value needs to be of type `i32`.");
// Check if the retrieved value is correct
assert_eq!(integer, 10);
// Store string as `Value`
let string_value = "Hello!".to_value();
// Retrieve `String` from `Value`
let string = string_value
.get::<String>()
.expect("The value needs to be of type `String`.");
// Check if the retrieved value is correct
assert_eq!(string, "Hello!".to_string());
// Store `Option<String>` as `Value`
let string_some_value = "Hello!".to_value();
let string_none_value = None::<String>.to_value();
// Retrieve `String` from `Value`
let string_some = string_some_value
.get::<Option<String>>()
.expect("The value needs to be of type `Option<String>`.");
let string_none = string_none_value
.get::<Option<String>>()
.expect("The value needs to be of type `Option<String>`.");
// Check if the retrieved value is correct
assert_eq!(string_some, Some("Hello!".to_string()));
assert_eq!(string_none, None);
}
We will use Value
when we deal with properties and signals later on.
Variant
A Variant
is used whenever data needs to be serialized, for example for sending it to another process or over the network, or for storing it on disk.
Although GVariant
supports arbitrarily complex types, the Rust bindings are currently limited to bool
, u8
, i16
, u16
, i32
, u32
, i64
, u64
, f64
, &str
/String
, and VariantDict
.
Containers of the above types are possible as well, such as HashMap
, Vec
, Option
, tuples up to 16 elements, and Variant
.
Variants can even be derived from Rust structs as long as its members can be represented by variants.
In the most simple case, converting Rust types to Variant
and vice-versa is very similar to the way it worked with Value
.
Filename: listings/g_object_values/2/main.rs
use gtk::prelude::*;
fn main() {
// Store `i32` as `Variant`
let integer_variant = 10.to_variant();
// Retrieve `i32` from `Variant`
let integer = integer_variant
.get::<i32>()
.expect("The variant needs to be of type `i32`.");
// Check if the retrieved value is correct
assert_eq!(integer, 10);
let variant = vec!["Hello", "there!"].to_variant();
assert_eq!(variant.n_children(), 2);
let vec = &variant
.get::<Vec<String>>()
.expect("The variant needs to be of type `String`.");
assert_eq!(vec[0], "Hello");
}
However, a Variant
is also able to represent containers such as HashMap
or Vec
.
The following snippet shows how to convert between Vec
and Variant
.
More examples can be found in the docs.
Filename: listings/g_object_values/2/main.rs
use gtk::prelude::*;
fn main() {
// Store `i32` as `Variant`
let integer_variant = 10.to_variant();
// Retrieve `i32` from `Variant`
let integer = integer_variant
.get::<i32>()
.expect("The variant needs to be of type `i32`.");
// Check if the retrieved value is correct
assert_eq!(integer, 10);
let variant = vec!["Hello", "there!"].to_variant();
assert_eq!(variant.n_children(), 2);
let vec = &variant
.get::<Vec<String>>()
.expect("The variant needs to be of type `String`.");
assert_eq!(vec[0], "Hello");
}
We will use Variant
when saving settings using gio::Settings
or activating actions via gio::Action
.