Mutating a value in an array list in Zig - arraylist

Noob question:
I want to mutate a value that exists in an array list. I initially tried to just grab the indexed item and directly change its field value.
const Foo = struct {
const Self = #This();
foo: u8,
};
pub fn main() anyerror!void {
const foo = Foo {
.foo = 1,
};
const allocator = std.heap.page_allocator;
var arr = ArrayList(Foo).init(allocator);
arr.append(foo) catch unreachable;
var a = arr.items[0];
std.debug.warn("a: {}", .{a});
a.foo = 2;
std.debug.warn("a: {}", .{a});
std.debug.warn("arr.items[0]: {}", .{arr.items[0]});
//In order to update the memory in [0] I have to reassign it to a.
//arr.items[0] = a;
}
However, the result is unexpected to me:
a: Foo{ .foo = 1 }
a: Foo{ .foo = 2 }
arr.items[0]: Foo{ .foo = 1 }
I would have thought that arr.items[0] would now equal Foo{ .foo = 2 }.
This is probably because I misunderstand slices.
Does a not point to the same memory as arr.items[0]?
Does arr.items[0] return a pointer to a copied item?

var a = arr.items[0];
That is making a copy of the item in arr.items[0].
If you want a reference, write var a = &arr.items[0]; instead.

Related

How can I print data in a way that consumes local variables when an assert fails in Rust?

I have some tests which have some variables that hold some important data and I'd like to print their data when an assertion fails. Getting the data I need consumes the variables, so the printing code must own the variables. In this example, I'd want to call dump_foo_data once an assertion fails:
struct Foo();
fn dump_foo_data(f: Foo) {
eprintln!("Behold, Foo data: ");
}
#[test]
fn my_test() {
let f = Foo();
eprintln!("begin");
// do a test
&f;
let success = true;
assert!(success);
// do another test
&f;
let success = false;
assert!(success);
}
I can make a very bad solution by making dump_foo_data non-returning and panic:
fn dump_foo_data(f: Foo) -> ! {
eprintln!("Behold, Foo data: ");
panic!();
}
Then instead of using assert!, I check the failure with an if and maybe call dump_foo_data:
let success = true;
if !success {
dump_foo_data(f);
}
This is too many lines of code, and I need to specify f. In reality, I have more than one variable like f that I need to dump data from, so it's not very nice to list out single relevant local variable in every check.
I couldn't figure out how to write a macro to make this better because I'd still need to pass every relevant local variable to the macro.
I couldn't think of a way to use std::panic either. update_hook would need to take ownership of f, then I couldn't use it in tests.
Is there any good way to do this in Rust?
Edit: I've thought of another approach: put each relevant local in an Rc then pass each of those to std::panic::update_hook. I've not confirmed whether this'll work yet.
Edit 2: Maybe I could abuse break to do what I explained with goto in a comment.
One way that doesn't use any macro or shared-interior-mutability-reference magic might be to repossess f:
fn check_or_dump(success: bool, f: Foo) -> Foo {
match success {
true => f,
false => panic!("Behold foo data: {:?}", dump_foo_data(f)),
}
}
You use it like this:
let f = Foo();
let success = true;
let f = check_or_dump(success, f);
let success = false;
let f = check_or_dump(success, f);
// and so on.
Here's a solution without macro or interior mutability and that doesn't require you to list all the variables on each check. It is inspired by this answer:
struct Foo();
fn dump_foo_data(_f: Foo) {
eprintln!("Behold, Foo data: ");
}
#[test]
fn my_test() {
let f = Foo();
let doit = || -> Option<()> {
eprintln!("begin");
// do a test
&f;
let success = true;
success.then_some(())?;
// do another test
&f;
let success = false;
success.then_some(())?;
Some(())
};
if let None = doit() {
dump_foo_data (f);
panic!("Test failure");
}
}
Playground
I've worked out a solution using the panic handler:
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use std::panic::PanicInfo;
thread_local! {
static TL_PANIC_TARGETS: RefCell<Vec<Rc<dyn PanicTrigger>>> = RefCell::new(vec![]);
}
pub trait PanicTrigger {
fn panic_trigger(self: Rc<Self>);
}
pub fn register_panic_trigger<P: PanicTrigger + 'static>(p: Rc<P>) {
TL_PANIC_TARGETS.with(|v: _| {
v.borrow_mut().push(p.clone());
});
}
#[ctor::ctor]
fn set_panic_hook() {
let old_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |pi: &PanicInfo| {
run_panic_triggers(pi);
old_hook(pi);
}));
}
fn run_panic_triggers(_: &PanicInfo) {
TL_PANIC_TARGETS.with(|v: _| {
for pt in v.take() {
pt.panic_trigger();
}
});
}
struct Foo();
fn dump_foo_data(_f: Foo) {
eprintln!("Behold, Foo data: ");
}
impl PanicTrigger for Cell<Option<Foo>> {
fn panic_trigger(self: Rc<Self>) {
if let Some(f) = self.take() {
dump_foo_data(f);
}
}
}
#[test]
fn my_test() {
let f = Rc::new(Cell::new(Some(Foo())));
register_panic_trigger(f.clone());
let success = true;
assert!(success);
let success = false;
assert!(success);
}
fn main() { }
Basically, you put the relevant data in an Rc and keep a local reference and put one in TLS for the panic handler. You need to put it in an Option in a Cell so that you can move out of it.
Types that don't need to be owned to print relevant data can be registered too, and you don't need to implement PanicTrigger on a Cell<Option<T>>, just T.
This is thread-safe.
Because the data is so wrapped up, it's harder to manipulate in the test body. But now you can use normal assert!. It's a trade-off.

Pass by value. Array

I have two arrays. But when I change second - first change too.
I tried
.clone()
.copyOf()
but it didn't work for me.
object MatrixObject {
var table: Array<Array<Int>>? = null
fun randOf(n: Int) {
table= Array(n, { Array(n, { Random().nextInt(100 - 0) + 0 }) })
}
var tableF: Array<Array<Int>>? = null
get() {
if (field==null)
factorization()
return field
}
fun factorization() {
tableF = table!!
... //here I change elements of tableF
}
}
I tried
for(row in 0 until table!!.size)
tableF!![row] = Arrays.copyOf(table!![row], table!![row].size)
and
for(row in 0 until table!!.size)
tableF!![row] = table!![row].clone() // and copyOf()
but it still doesn't work.
I found the solution.I initialized the array.
tableF= Array(table!!.size, { Array(table!!.size, {0}) })
for(row in 0 until table!!.size)
tableF!![row] = table!![row].clone()

How to merge node in yaml-cpp

I have two node object, like this:
school:
grade:
class:
name: bob
school:
grade:
class:
age: 18
I want to merge it, the result like this:
school:
grade:
class:
name: bob
age: 18
How to merge it? when the node size and depth do not kown.
Here is my attempt:
#include <yaml-cpp/yaml.h>
inline const YAML::Node & cnode(const YAML::Node &n) {
return n;
}
YAML::Node merge_nodes(YAML::Node a, YAML::Node b)
{
if (!b.IsMap()) {
// If b is not a map, merge result is b, unless b is null
return b.IsNull() ? a : b;
}
if (!a.IsMap()) {
// If a is not a map, merge result is b
return b;
}
if (!b.size()) {
// If a is a map, and b is an empty map, return a
return a;
}
// Create a new map 'c' with the same mappings as a, merged with b
auto c = YAML::Node(YAML::NodeType::Map);
for (auto n : a) {
if (n.first.IsScalar()) {
const std::string & key = n.first.Scalar();
auto t = YAML::Node(cnode(b)[key]);
if (t) {
c[n.first] = merge_nodes(n.second, t);
continue;
}
}
c[n.first] = n.second;
}
// Add the mappings from 'b' not already in 'c'
for (auto n : b) {
if (!n.first.IsScalar() || !cnode(c)[n.first.Scalar()]) {
c[n.first] = n.second;
}
}
return c;
}
For non-scalar keys I have opted to ignore node equivalence. Please note that this version does not modify a. It returns a new map c which is a merge of b into a. Values from b will replace identically keyed non-map values from a in the c map.
I found an issue with md5i's answer that it doesn't merge the second node's unique sub nodes. My fix was calling the function again in the for loop of node b (which I renamed the override node). I also made everything const, because I'm not editing anything here, and so I don't have to cast it. I'm also reinforcing the fact the second node is overriding the other.
const YAML::Node mergeNodes(const YAML::Node& defaultNode, const YAML::Node& overrideNode)
{
if (!overrideNode.IsMap()) {
// If overrideNode is not a map, merge result is overrideNode, unless overrideNode is null
return overrideNode.IsNull() ? defaultNode : overrideNode;
}
if (!defaultNode.IsMap()) {
// If defaultNode is not a map, merge result is overrideNode
return overrideNode;
}
if (!defaultNode.size()) {
return YAML::Node(overrideNode);
}
// Create a new map 'newNode' with the same mappings as defaultNode, merged with overrideNode
auto newNode = YAML::Node(YAML::NodeType::Map);
for (auto node : defaultNode) {
if (node.first.IsScalar()) {
const std::string& key = node.first.Scalar();
if (overrideNode[key]) {
newNode[node.first] = mergeNodes(node.second, overrideNode[key]);
continue;
}
}
newNode[n.first] = node.second;
}
// Add the mappings from 'overrideNode' not already in 'newNode'
for (auto node : overrideNode) {
if (!node.first.IsScalar()) {
const std::string& key = node.first.Scalar();
if (defaultNode[key]) {
newNode[node.first] = mergeNodes(defaultNode[key], node.second);
continue;
}
}
newNode[node.first] = node.second;
}
return YAML::Node(newNode);
}
This will probably not do the N thingy any favours, but it will take the nodes from node b/overrideNode even if they have no instance in node a/defaultNode.

Return Option inside Loop

The program aims to use a loop to check if the index of a iterator variable meets certain criteria (i.g., index == 3). If find the desired index, return Some(123), else return None.
fn main() {
fn foo() -> Option<i32> {
let mut x = 5;
let mut done = false;
while !done {
x += x - 3;
if x % 5 == 0 {
done = true;
}
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
return None; //capture all other other possibility. So the while loop would surely return either a Some or a None
}
}
}
The compiler gives this error:
error[E0308]: mismatched types
--> <anon>:7:9
|
7 | while !done {
| ^ expected enum `std::option::Option`, found ()
|
= note: expected type `std::option::Option<i32>`
= note: found type `()`
I think the error source might be that a while loop evaluates to a (), thus it would return a () instead of Some(123). I don't know how to return a valid Some type inside a loop.
The value of any while true { ... } expression is always (). So the compiler expects your foo to return an Option<i32> but finds the last value in your foo body is ().
To fix this, you can add a return None outside the original while loop. You can also use the loop construct like this:
fn main() {
// run the code
foo();
fn foo() -> Option<i32> {
let mut x = 5;
loop {
x += x - 3;
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
if x % 5 == 0 {
return None;
}
}
}
}
The behaviour of while true { ... } statements is maybe a bit quirky and there have been a few requests to change it.

A function inside a class modifies prototype instead of just returning an object

I have this code in LiveScript (1.1.1):
class A
b = -> { a: 1 b: 2 }
It compiles into this:
var A;
A = (function(){
A.displayName = 'A';
var b, prototype = A.prototype, constructor = A;
b = function(){
return prototype.a = 1, prototype.b = 2, prototype;
};
function A(){}
return A;
}());
Why does b() modify prototype? I expected it to just return a { a: 1, b: 2 } associative array.
It's because of this :
https://github.com/gkz/LiveScript/commit/d49b3ee8e8e2d5d7b9f128fa98c210b582e095fe
Which should probably be removed then, mmh.