I have a vector of structures and i would like to update one structure with values in another. For my use case, I prefer to do it in a loop. I'm hitting the borrow-checker but it seems like there must be a simple solution to this type of problem.
#[derive(Debug)]
struct Column {
header: String,
amount: i32,
}
fn main() {
let mut spreadsheet: Vec<Column> = Vec::new();
spreadsheet.push(Column {
header: "Car".to_string(),
amount: 30300,
});
spreadsheet.push(Column {
header: "House".to_string(),
amount: 210800,
});
spreadsheet.push(Column {
header: "Total".to_string(),
amount: 0,
});
for column in &mut spreadsheet {
//mutable borrow here
if column.header == "Total" {
column.amount = spreadsheet[0].amount //immutable borrow here
+ spreadsheet[1].amount;
} else {
column.amount -= 300;
}
}
for column in spreadsheet {
println!("{:?}", column);
}
}
You are trying to set spreadsheet vector element while iterating inside of it. Since you always wanting to use spreadsheet[0].amount and spreadsheet[1].amount you can clone this values into another variable and work with them instead of using them inside of spreadsheet.
Here is the working code:
#[derive(Debug)]
struct Column {
header: String,
amount: i32,
}
fn main() {
let mut spreadsheet: Vec<Column> = Vec::new();
spreadsheet.push(Column {
header: "Car".to_string(),
amount: 30300,
});
spreadsheet.push(Column {
header: "House".to_string(),
amount: 210800,
});
spreadsheet.push(Column {
header: "Total".to_string(),
amount: 0,
});
let car_amount = spreadsheet[0].amount;
let header_amount = spreadsheet[1].amount;
spreadsheet.iter_mut().for_each(|column| {
if column.header == "Total" {
column.amount = car_amount + header_amount;
} else {
column.amount -= 300;
}
});
for column in spreadsheet {
println!("{:?}", column);
}
}
Playground with using iter()
Since you want to do these operations in a for loop instead of iterator you can change the spreadsheet.iter_mut()... code block to the following:
for column in &mut spreadsheet {
if column.header == "Total" {
column.amount = car_amount + header_amount;
} else {
column.amount -= 300;
}
}
Playground with using for loop
Related
I encountered the following (reduced) example:
enum Thing {
A { value: f32 },
B { value: f32 },
}
fn main() {
let mut thing = Thing::A { value: 0. };
let thing_mut_ref = &mut thing;
if let Thing::A {value} = thing_mut_ref {
*thing_mut_ref = Thing::B { value: value * 2.0};
}
}
The following does not compile because value is captured as a &mut f32, and thus does not support multiplying by 2.0.
What surprised me was that adding ref to the matched pattern suddenly makes it compile, i.e. captures by value (dereferences value):
enum Thing {
A { value: f32 },
B { value: f32 },
}
fn main() {
let mut thing = Thing::A { value: 0. };
let thing_mut_ref = &mut thing;
if let Thing::A {ref value} = thing_mut_ref {
*thing_mut_ref = Thing::B { value: value * 2.0};
}
}
I know ref for usually doing the opposite - stating that we do not want to capture by value. How does this explain what is going on here?
It does not in fact dereference but convert the mutable reference into a shared one.
And as you can see from the documentation of Mul it is implemented for &f32 and f32 but not for &mut f32
I wish that enums in Rust can be used like Haskell's productive type. I want to
access a field's value directly
assign a field's value directly or make a clone with the changing value.
Directly means that not using too long pattern matching code, but just could access like let a_size = a.size.
In Haskell:
data TypeAB = A {size::Int, name::String} | B {size::Int, switch::Bool} deriving Show
main = do
let a = A 1 "abc"
let b = B 1 True
print (size a) -- could access a field's value directly
print (name a) -- could access a field's value directly
print (switch b) -- could access a field's value directly
let aa = a{size=2} -- could make a clone directly with the changing value
print aa
I tried two styles of Rust enum definition like
Style A:
#[derive(Debug)]
enum EntryType {
A(TypeA),
B(TypeB),
}
#[derive(Debug)]
struct TypeA {
size: u32,
name: String,
}
#[derive(Debug)]
struct TypeB {
size: u32,
switch: bool,
}
fn main() {
let mut ta = TypeA {
size: 3,
name: "TAB".to_string(),
};
println!("{:?}", &ta);
ta.size = 2;
ta.name = "TCD".to_string();
println!("{:?}", &ta);
let mut ea = EntryType::A(TypeA {
size: 1,
name: "abc".to_string(),
});
let mut eb = EntryType::B(TypeB {
size: 1,
switch: true,
});
let vec_ab = vec![&ea, &eb];
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
// Want to do like `ta.size = 2` for ea
// Want to do like `ta.name = "bcd".to_string()` for ea
// Want to do like `tb.switch = false` for eb
// ????
println!("{:?}", &ea);
println!("{:?}", &eb);
println!("{:?}", &vec_ab);
}
Style B:
#[derive(Debug)]
enum TypeCD {
TypeC { size: u32, name: String },
TypeD { size: u32, switch: bool },
}
fn main() {
// NOTE: Rust requires representative struct name before each constructor
// TODO: Check constructor name can be duplicated
let mut c = TypeCD::TypeC {
size: 1,
name: "abc".to_string(),
};
let mut d = TypeCD::TypeD {
size: 1,
switch: true,
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
// Can't access a field's value like
// let c_size = c.size
let c_size = c.size; // [ERROR]: No field `size` on `TypeCD`
let c_name = c.name; // [ERROR]: No field `name` on `TypeCD`
let d_switch = d.switch; // [ERROR]: No field `switch` on `TypeCD`
// Can't change a field's value like
// c.size = 2;
// c.name = "cde".to_string();
// d.switch = false;
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
}
I couldn't access/assign values directly in any style. Do I have to implement functions or a trait just to access a field's value? Is there some way of deriving things to help this situation?
What about style C:
#[derive(Debug)]
enum Color {
Green { name: String },
Blue { switch: bool },
}
#[derive(Debug)]
struct Something {
size: u32,
color: Color,
}
fn main() {
let c = Something {
size: 1,
color: Color::Green {
name: "green".to_string(),
},
};
let d = Something {
size: 2,
color: Color::Blue { switch: true },
};
let vec_cd = vec![&c, &d];
println!("{:?}", &c);
println!("{:?}", &d);
println!("{:?}", &vec_cd);
let _ = c.size;
}
If all variant have something in common, why separate them?
Of course, I need to access not common field too.
This would imply that Rust should define what to do when the actual type at runtime doesn't contain the field you required. So, I don't think Rust would add this one day.
You could do it yourself. It will require some lines of code, but that matches the behavior of your Haskell code. However, I don't think this is the best thing to do. Haskell is Haskell, I think you should code in Rust and not try to code Haskell by using Rust. That a general rule, some feature of Rust come directly from Haskell, but what you want here is very odd in my opinion for Rust code.
#[derive(Debug)]
enum Something {
A { size: u32, name: String },
B { size: u32, switch: bool },
}
impl Something {
fn size(&self) -> u32 {
match self {
Something::A { size, .. } => *size,
Something::B { size, .. } => *size,
}
}
fn name(&self) -> &String {
match self {
Something::A { name, .. } => name,
Something::B { .. } => panic!("Something::B doesn't have name field"),
}
}
fn switch(&self) -> bool {
match self {
Something::A { .. } => panic!("Something::A doesn't have switch field"),
Something::B { switch, .. } => *switch,
}
}
fn new_size(&self, size: u32) -> Something {
match self {
Something::A { name, .. } => Something::A {
size,
name: name.clone(),
},
Something::B { switch, .. } => Something::B {
size,
switch: *switch,
},
}
}
// etc...
}
fn main() {
let a = Something::A {
size: 1,
name: "Rust is not haskell".to_string(),
};
println!("{:?}", a.size());
println!("{:?}", a.name());
let b = Something::B {
size: 1,
switch: true,
};
println!("{:?}", b.switch());
let aa = a.new_size(2);
println!("{:?}", aa);
}
I think there is currently no built-in way of accessing size directly on the enum type. Until then, enum_dispatch or a macro-based solution may help you.
Say I have a vector of items where each item has an id, like in the example below. I can of course get all the items in the vector with a given id using something like large_vector.iter().filter(|item| item.id == given_id). However, for improved performance I can do some preprocessing and sort the vector by item id and store the bounds for each id, like in the example below. This way I can quickly access a slice of the vector for any given id. I end up doing this alot but feel like I am reinventing the wheel and needlessly opening myself up to bugs. Is there a better way to do this directly, preferably using the standard library else some other library?
use std::{collections::HashMap, ops::Range};
#[derive(Debug)]
struct Item {
id: String,
val: f64,
}
impl Item {
fn new(id: &str, val: f64) -> Item {
Item { id: id.into(), val }
}
}
fn main() {
let mut large_vector = vec![
Item::new("C", 2.21),
Item::new("A", 34.2),
Item::new("B", 23.54),
Item::new("C", 34.34),
Item::new("C", 45.21),
Item::new("B", 21.34),
];
// first sort by id
large_vector.sort_by(|item1, item2| item1.id.cmp(&item2.id));
dbg!(&large_vector);
// now create a HasMap storing bounds for each id
let mut lookup = HashMap::new();
let mut start: usize = 0;
let mut end: usize = 0;
if let Some(first_item) = large_vector.get(0) {
let mut current_id = first_item.id.clone();
// insert bound if entered new id section or is last item
for item in &large_vector {
if current_id != item.id {
lookup.insert(current_id.clone(), Range { start, end });
current_id = item.id.clone();
start = end;
}
end += 1;
}
lookup.insert(current_id.clone(), Range { start, end });
}
// test by getting the items for a given id
dbg!(&lookup);
let range = lookup.get("C").unwrap();
dbg!(range);
let items = large_vector[range.start..range.end]
.iter()
.collect::<Vec<_>>();
dbg!(items);
}
[src/main.rs:26] &large_vector = [
Item {
id: "A",
val: 34.2,
},
Item {
id: "B",
val: 23.54,
},
Item {
id: "B",
val: 21.34,
},
Item {
id: "C",
val: 2.21,
},
Item {
id: "C",
val: 34.34,
},
Item {
id: "C",
val: 45.21,
},
]
[src/main.rs:47] &lookup = {
"A": 0..1,
"B": 1..3,
"C": 3..6,
}
[src/main.rs:49] range = 3..6
[src/main.rs:53] items = [
Item {
id: "C",
val: 2.21,
},
Item {
id: "C",
val: 34.34,
},
Item {
id: "C",
val: 45.21,
},
]
Assuming that your items have to be in a vector, and you can only sort them, I can think of two possibilities:
The solution you proposed. It should be the fastest one for lookup, but has the drawback that the lookup tables get completely invalidated every time you insert/remove an item.
Keep the vector sorted and perform a log(n) based divide-and-conquer search to get the range. If you are interested in what I mean with that, I can provide you with some code.
But in general, I think a vector is simply the wrong data structure. I'd try to change that first.
I have a vector of structs, and I'm comparing every element in the vector against every other element, and in certain cases mutating the current element.
My issue is that you can't have both a mutable and immutable borrow happening at the same time, but I'm not sure how to reframe my problem to get around this without cloning either the current element or the entire vector, which seems like a waste since I'm only ever mutating the current element, and it doesn't need to be compared to itself (I skip that case).
I'm sure there's an idiomatic way to do this in Rust.
struct MyStruct {
a: i32,
}
fn main() {
let mut v = vec![MyStruct { a: 1 }, MyStruct { a: 2 }, MyStruct { a: 3 }];
for elem in v.iter_mut() {
for other_elem in v.iter() {
if other_elem.a > elem.a {
elem.a += 1;
}
}
}
}
The simplest way is to just use indices, which don't involve any long-lived borrows:
for i in 0..v.len() {
for j in 0..v.len() {
if i == j { continue; }
if v[j].a > v[i].a {
v[i].a += 1;
}
}
}
If you really, really want to use iterators, you can do it by dividing up the Vec into disjoint slices:
fn process(elem: &mut MyStruct, other: &MyStruct) {
if other.a > elem.a {
elem.a += 1;
}
}
for i in 0..v.len() {
let (left, mid_right) = v.split_at_mut(i);
let (mid, right) = mid_right.split_at_mut(1);
let elem = &mut mid[0];
for other in left {
process(elem, other);
}
for other in right {
process(elem, other);
}
}
If you can modify type type of v, and the elements of v are Copy, you can wrap MyStruct in Cell.
#[derive(Copy, Clone)]
struct MyStruct {
a: i32,
}
fn main() {
use std::cell::Cell;
let v = vec![
Cell::new(MyStruct { a: 1 }),
Cell::new(MyStruct { a: 2 }),
Cell::new(MyStruct { a: 3 }),
];
for elem in v.iter() {
for other_elem in v.iter() {
let mut e = elem.get();
if other_elem.get().a > e.a {
e.a += 1;
elem.set(e);
}
}
}
}
If instead you're passed a &mut to a slice (or &mut that can be converted into a slice), use Cell::from_mut and Cell::as_slice_of_cells and use the same trick as above (assuming the elements of the slice are Copy).
I want to do something along the lines of
enum A {
Type1 {
s: String
// ... some more fields
}
// ... some more variants
}
impl A {
fn consume(self) { println!("Consumed!"); }
}
fn fails() {
let b = A::Type1 { s: String::from("Arbitrary string") };
match b {
A::Type1 {
s, // (value moved here)
// ... more fields
} => {
let l = s.len(); // Something using the field from the enum
if l > 3 {
s.into_bytes(); // do something that requires ownership of s
} else {
b.consume(); // Value used here after move
}
}
// ... more cases
}
}
However, because I destructure b in the match case, I don't have access to it within the body of the match.
I can reconstruct b from the fields, but when b has lots of fields this is obviously not ideal. Is there any way to get around this issue without having to rebuild b?
No, you (almost literally) cannot have your cake and eat it too. Once you have destructured the value, the value no longer exists.
When non-lexical lifetimes happens, you can use a combination of NLL and match guards to prevent taking ownership in the first place:
#![feature(nll)]
enum A {
Type1 { s: String },
}
impl A {
fn consume(self) {
println!("Consumed!");
}
}
fn main() {
let b = A::Type1 {
s: String::from("Arbitrary string"),
};
match b {
A::Type1 { ref s } if s.len() <= 3 => {
b.consume();
}
A::Type1 { s } => {
s.into_bytes();
}
}
}