This question already has answers here:
Reducing match indentation for deeply nested properties
(2 answers)
How do I unwrap an arbitrary number of nested Option types?
(3 answers)
Are nested matches a bad practice in idiomatic Rust?
(2 answers)
How do I borrow a reference to what is inside an Option<T>?
(2 answers)
Closed 2 years ago.
How should I go about reading the value that's nested within two Options?
I have this code:
struct Person<T> {
name: &'static str,
preferences: Vec<&'static str>,
variant: Option<T>,
}
struct Driver {
seats: usize,
}
fn main() {
let list: Vec<Person<Driver>> = todo!();
let name = "some-name";
// add a bunch of Person<Driver> drivers to the `list`
let found = list.iter().find(|candidate| candidate.name == name);
// other things
}
What's the cleanest or most recommended way to access the field seats from Option<Person<Driver>> in found?
The two ways I've done it is with a nested if let or a nested match. The nested match looks a little ugly, so this is what I'm going with:
if let Some(person) = found {
if let Some(driver) = &person.variant {
println!("{:?}", driver.seats);
}
}
Is there a better way of going about it?
The reason I have an Option field called variant is since I also have Rider structs and they're literally just a Person. Since both drivers and riders have almost all the same fields, I structured it this way instead of opting to say a person: Person field in each Rider and Driver struct which felt odd.
Related
This question already has answers here:
How to filter a vector of custom structs?
(1 answer)
How do I conditionally check if an enum is one variant or another?
(2 answers)
Closed 2 years ago.
I can filter the active classes as follows, but how can I achieve bringing all the classes where their status is not Online? Status for not active can be Cancelled, Deferred, or Optional.
pub enum Status {
Online,
Cancelled,
Deferred,
Optional,
}
pub struct CustomFilter {
pub status: Option<Status>,
}
fn example() {
let mut custom_filter = CustomFilter::default();
custom_filter.status = Some(Status::Online);
let online_classes = self.get_classes_with_filter(custom_filter).await?;
// where Status -> Cancelled, Deferred, Optional
let non_active_classes = todo!("??");
}
How about something along the lines of:
let foo: Vec<_> = whatever;
let active_classes: Vec<_> = foo.iter().filter(|x| x.status == Status::Active).collect();
let non_active_classes: Vec<_> = foo.iter().filter(|x| x.status != Status::Active).collect();
Not sure if this is what you intended.
If your question is "Can I store !Active or Cancelled | Deferred | Optional in an Status?" then the answer is no. Maybe if your enum worked like bitflags but that doesn't appear to be the case here.
You'd have to store them individually in a Vec or something more complicated like:
enum StatusFilter {
Include(Vec<Status>),
Exclude(Vec<Status>),
}
pub struct CustomFilter {
pub status: StatusFilter,
}
custom_filter.status = StatusFilter::Exclude(vec![Status::Active]);
I'd recommend not doing this and use functor-based filtering like #Luke has suggested if at all possible. Its much more straightforward, more flexible to use and implement, and it follows conventions in the standard library.
This question already has answers here:
Return local String as a slice (&str)
(7 answers)
Proper way to return a new string in Rust
(2 answers)
Is there any way to return a reference to a variable created in a function?
(5 answers)
Why can't I store a value and a reference to that value in the same struct?
(4 answers)
Closed 2 years ago.
I'm working with an external type (i.e., not my code), that has a bunch of &str members:
#[derive(Default, Clone)]
pub struct SomeExternalThingie<'a> {
pub name: &'a str,
pub description: Option<&'a str>,
pub author: Option<&'a str>,
}
I want to make my own function which constructs and returns one of these things. I want to dynamically construct some of the strings used as members:
fn my_func_to_make_a_thingie<'a>(name: &'a str) -> MyThingie<'a> {
let mydesc = format!("Hi, I'm your friendly neighborhood {}!",
name);
SomeExternalThingie {
name: name,
description: Some(mydesc.as_str()),
..Default::default()
}
}
Of course, this won't compile, because the lifetime of mydesc ends when the function does. What's the best practice for dealing with a situation like this? I'm obviously new to Rust, but it seems like this will be a common scenario.
My only thought to change the return type to return both the desired structure and the storage for the string(s)... either by returning a tuple, or making a custom struct. This feels awkward, though, and I was wondering if there was a better way, or at least a convention of sorts.
For some context - the actual use case that prompted this was that I wanted to return a clap::App object. I wanted to do this mostly for organizational reasons - I know that in this case I can just include it in my main function, without the sub-function (even though it makes it longer than I'd like), and I'll probably end up doing this. However, this seems like a common pattern, and I wanted to learn the "Rust way" to deal with this.
This question already has answers here:
Why can't I store a value and a reference to that value in the same struct?
(4 answers)
Implement graph-like data structure in Rust
(3 answers)
What is the right smart pointer to have multiple strong references and allow mutability?
(1 answer)
Mutating one field while iterating over another immutable field
(3 answers)
How can I borrow from a HashMap to read and write at the same time?
(2 answers)
Closed 3 years ago.
I am trying to implement the JVM in Rust as a fun project but I am struggling with an issue involving references. When classes are loaded from class files, their references to other classes are represented as Strings. After the loading phase, a JVM should link classes with actual references to each other. I cannot figure out how to do this with Rust's references. Here is a stripped down version of what I am trying to achieve. (an MCV example)
use ClassRef::{Symbolic, Static};
use std::collections::HashMap;
fn main() {
let mut loader = ClassLoader { classes: HashMap::new() };
loader.load_class("java/lang/String", "java/lang/Object");
// java.lang.Object is the only class without a direct superclass
loader.load_class("java/lang/Object", "");
loader.link_classes();
}
struct ClassLoader<'a> {
classes: HashMap<String, Class<'a>>
}
impl<'a> ClassLoader<'a> {
pub fn load_class(&mut self, name: &str, super_name: &str) {
self.classes.insert(name.to_owned(), Class::new(name, super_name));
}
pub fn link_classes(&mut self) {
// Issue 1: Editing the stored class requires a mutable borrow
for (name, class) in self.classes.iter_mut() {
// Issue 2: I am not allowed to move this value to itself
class.super_class = match class.super_class {
// Issue 3: Storing a reference to another value in the map
Symbolic(ref super_name) => Static(self.classes.get(super_name)),
a => a
}
}
}
}
struct Class<'a> {
super_class: ClassRef<'a>,
name: String
}
impl<'a> Class<'a> {
pub fn new(name: &str, super_name: &str) -> Self {
Class {
name: name.to_owned(),
super_class: Symbolic(super_name.to_owned())
}
}
}
enum ClassRef<'a> {
Symbolic(String),
Static(Option<&'a Class<'a>>)
}
Currently, there are three issues with the process that I am unable to solve.
First, when I iterate through the list of loaded classes, I need to be able to change the field Class#super_class, so I need a mutable iterator. This means that I have a mutable borrow on self.classes. I also need an immutable borrow on self.classes during the iteration to look up a reference to the superclass. The way I feel like this should be solved is having a way of proving to the compiler that I am not mutating the map itself, but only a key. I feel like it has to do with std::cell::Cell as shown in Choosing your Guarantees, but the examples seem so simple I don't know how to correlate it to what I am trying to do here.
Second, I need the previous value of Class#super_class in order to calculate the new one. Essentially, I am moving the previous value out, changing it, and then moving it back in. I feel like this could be solved with some sort of higher order Map function to work on the value in place, but once again I'm not sure.
Third, when I give one class a reference to another class, there is no way of proving to the compiler that the other class won't be removed from the hashmap, causing the reference to point to deallocated memory. When classes are loaded, one class name will always point to the same class and classes are never unloaded, so I know that the value will always be there. The way I think this should be solved is with a Hashmap implementation which doesn't allow values to be overwritten or removed. I tried to poke around for such a thing but I couldn't find it.
EDIT: Solution
The duplicates linked were certainly helpful, but I ended up not using reference counting pointers (Rc<>) because the relationships between classes can sometimes be cyclic, so trying to drop the ClassLoader would end up leaking memory. The solution I settled on is using an immutable reference to a
Typed-Arena allocator. This allocator is an insertion only allocator which ensures the references it distributes are valid for the entire life of the reference to the allocator, as long as said reference is immutable. I then used the hashmap to store the references. That way everything is dropped when I drop the ClassLoader. Hopefully anyone that trying to implement a relationship similar to this finds the following code helpful.
extern crate typed_arena;
use ClassRef::{Symbolic, Static};
use std::collections::HashMap;
use std::cell::RefCell;
use core::borrow::BorrowMut;
use typed_arena::Arena;
fn main() {
let mut loader = ClassLoader { class_map: HashMap::new(), classes: &Arena::new() };
loader.load_class("java/lang/String", "java/lang/Object");
// java.lang.Object is the only class without a direct superclass
loader.load_class("java/lang/Object", "");
loader.link_classes();
}
struct ClassLoader<'a> {
classes: &'a Arena<RefCell<Class<'a>>>,
class_map: HashMap<String, &'a RefCell<Class<'a>>>
}
impl<'a> ClassLoader<'a> {
pub fn load_class(&mut self, name: &str, super_name: &str) {
let super_opt = if super_name.len() > 0 {
Some(super_name)
} else {
None
};
let class = Class::new(name, super_opt);
let class_ref = self.classes.alloc(RefCell::new(class));
self.class_map.insert(name.to_owned(), class_ref);
}
pub fn link_classes(&mut self) {
for (_name, class_ref) in &self.class_map {
let mut class = (*class_ref).borrow_mut();
if let Some(Symbolic(super_name)) = &class.super_class {
let super_class = self.class_map.get(super_name);
let super_class = super_class.map(|c| Static(c.clone()));
*class.super_class.borrow_mut() = super_class;
}
}
}
}
struct Class<'a> {
super_class: Option<ClassRef<'a>>,
name: String
}
impl<'a> Class<'a> {
pub fn new(name: &str, super_name: Option<&str>) -> Self {
Class {
name: name.to_owned(),
super_class: super_name.map(|name| Symbolic(name.to_owned()))
}
}
}
enum ClassRef<'a> {
Symbolic(String),
Static(&'a RefCell<Class<'a>>)
}
This question already has answers here:
Generate sequential IDs for each instance of a struct
(2 answers)
How to check if two variables point to the same object in memory?
(1 answer)
Closed 4 years ago.
I would like to give my object a unique ID (to be able to compare them). I figured to do something along these lines:
pub struct Player {
id: i32,
score: usize,
}
impl Player {
fn new() -> Player {
let mut player = Player {};
player.id = &player as *const i32;
player
}
}
I run into the problem that I need to set the variables when defining the Player, but there is no memory address at that point.
I could make the id mutable, but there is no need to change the variable after initialisation.
How can I do something like this?
This question already has answers here:
Why does compilation not fail when a member of a moved value is assigned to?
(2 answers)
Why is assigning to a member of a pointer still valid after the pointer is moved?
(1 answer)
Closed 4 years ago.
#[derive(Debug)]
struct A {
x: i32
}
fn main() {
let mut x = A{x: 1};
drop(x);
// this is fine?
x.x = 100;
// this is not? ERROR: "use of moved value x.x"
println!("{}", x.x);
}
I've read some answers here that says if you re-initialize the struct after move, then you can use it again. I am re-initializing it fully here and yet can't use it. Why is this feature (ability to write to struct fields after move) even there?