Mutating arrays
In addition to the immutable accessors we've seen in the previous section, jlrs also provides mutable accessors.
Methods like (try_)bits_data_mut
and types like BitsAccessorMut
exist analogously to the immutable variants. The methods provided by the mutable accessors have names similar to their immutable counterparts. Some accessors don't provide direct mutable access to an element; they don't implement IndexMut
or provide a set_mut
method. In this case we'll need to use AccessorMut::set_value
.
Mutating managed data from Rust is generally unsafe in jlrs. The reason essentially boils down to "just because you have access to a mutable value doesn't mean you're allowed to mutate it." Strings are a good example, they're mutable but it's UB to mutate them. Array accessors are a bit special in this regard: it's unsafe to create a mutable accessor, but many of their mutating methods are safe. The safety-requirements are implied by the requirements of creating a mutable accessor in the first place.
The array types implement Copy
so it's trivial to create two mutable accessors to the same array, or multiple mutable and immutable accessor in general. It's your responsibility to ensure this doesn't happen. It's possible to avoid this issue to a degree by tracking the array, which we'll cover later in this chapter.
use jlrs::prelude::*;
fn main() {
let handle = Builder::new().start_local().expect("cannot init Julia");
// IndeterminateAccessorMut
handle.local_scope::<_, 4>(|mut frame| {
let data = [1.0f64, 2., 3., 4.];
let f64_ty = DataType::float64_type(&frame).as_value();
let mut arr = RankedArray::<2>::from_slice_copied_for(&mut frame, f64_ty, &data, [2, 2])
.expect("incompatible type and layout")
.expect("invalid size");
// Safety: this is the only accessor to this data.
let mut accessor = unsafe { arr.indeterminate_data_mut() };
let v = Value::new(&mut frame, 5.0f64);
accessor.set_value(&mut frame, [1, 0], v).expect("index out of bounds").expect("caught exception");
let elem = accessor
.get_value(&mut frame, [1, 0])
.expect("out of bounds")
.expect("undefined reference")
.unbox::<f64>()
.expect("wrong type");
assert_eq!(elem, 5.);
});
// BitsAccessorMut
handle.local_scope::<_, 1>(|mut frame| {
let data = [1.0f64, 2., 3., 4.];
let mut arr = TypedArray::<f64>::from_slice_copied(&mut frame, &data, [2, 2])
.expect("incompatible type and layout")
.expect("invalid size");
// Safety: this is the only accessor to this data.
let mut accessor = unsafe { arr.bits_data_mut() };
let elem = accessor[[1, 0]];
assert_eq!(elem, 2.);
accessor[[1, 0]] = 4.;
let elem = accessor[[1, 0]];
assert_eq!(elem, 4.);
});
// InlineAccessorMut
handle.local_scope::<_, 2>(|mut frame| {
let data = [1.0f64, 2., 3., 4.];
let mut arr = TypedArray::<f64>::from_slice_copied(&mut frame, &data, [2, 2])
.expect("incompatible type and layout")
.expect("invalid size");
// Safety: this is the only accessor to this data.
let mut accessor = unsafe { arr.inline_data_mut() };
let elem = accessor[[1, 0]];
assert_eq!(elem, 2.);
let v = Value::new(&mut frame, 4.0f64);
accessor
.set_value(&mut frame, [1, 0], v)
.expect("index out of bounds")
.expect("caught an exception");
let elem = accessor[[1, 0]];
assert_eq!(elem, 4.);
});
// ValueAccessorMut
handle.local_scope::<_, 3>(|mut frame| {
// Safety: this code only allocates and returns an array
let mut arr = unsafe { Value::eval_string(&mut frame, "Any[:foo, :bar]") }
.expect("caught an exception")
.cast::<VectorAny>()
.expect("not a VectorAny");
// Safety: this is the only accessor to this data.
let mut accessor = unsafe { arr.value_data_mut() };
let v = Value::new(&mut frame, 1usize);
accessor
.set_value(&mut frame, 0, v)
.expect("out of bounds")
.expect("caught an exception");
let elem = accessor.get(&mut frame, 0).expect("out of bounds");
assert_eq!(v, elem);
});
// ManagedAccessorMut
handle.local_scope::<_, 4>(|mut frame| {
// Safety: this code only allocates and returns an array
let mut arr = unsafe { Value::eval_string(&mut frame, "Symbol[:foo, :bar]") }
.expect("caught an exception")
.cast::<TypedVector<Symbol>>()
.expect("not a TypedVector<Symbol>");
// Safety: this is the only accessor to this data.
let mut accessor = unsafe { arr.managed_data_mut() };
let sym = Symbol::new(&mut frame, "baz");
accessor
.set_value(&mut frame, 0, sym.as_value())
.expect("out of bounds")
.expect("caught an exception");
let elem = accessor.get(&mut frame, 0).expect("out of bounds");
assert_eq!(sym, elem);
});
// BitsUnionAccessorMut
handle.local_scope::<_, 1>(|mut frame| {
// Safety: this code only allocates and returns an array
let mut arr =
unsafe { Value::eval_string(&mut frame, "Union{Int, Float64}[1.0 2; 3 4.0]") }
.expect("caught an exception")
.cast::<Matrix>()
.expect("not a Matrix");
// Safety: this is the only accessor to this data.
let mut accessor = unsafe { arr.try_union_data_mut().expect("wrong accessor") };
accessor
.set([1, 0], DataType::float64_type(&frame), 4.0f64)
.expect("out of bounds or incompatible type")
.expect("caught an exception");
let elem = accessor
.get::<f64, _>([1, 0])
.expect("wrong layout")
.expect("out of bounds");
assert_eq!(elem, 4.0);
});
}