How to include a struct in a function's decision making? - struct

I am trying to learn struct usage and want to create a function that takes a struct name (citizen3030) and gives some information about that person.
Where I'm tripping up is declaring inbound type fn get_social_status(<what to put here>) when the type comes from the struct. I wish I knew how to describe it better.
Side Note: This is not real for any actual real project, I'm just trying to learn and had Star Citizen video playing in the background. Also, using static strings was just for experimenting.
#[derive(Debug)]
struct Citizen {
id_num: u32,
weight: u32,
height: u32,
first_name: &'static str,
last_name: &'static str,
phone_number: &'static str,
age: u32,
sex: &'static str,
marital_status: &'static str,
social_rating: u32
}
// it does not like any of the variables below
impl Citizen {
fn get_social_status(&self, &String) {
if Citizen.social_rating >= 7 {println!("Good");}
if Citizen.social_rating >= 5 && Citizen.age <= 20 {println!("Fair");}
else {println!("Not Great");}
}
}
fn main() {
let citizen3030 = Citizen {
id_num: 1,
weight: 100,
height: 55,
first_name: "Jake",
last_name: "Doe",
phone_number: "555-555-3232",
age: 17,
sex: "m",
marital_status: "Married",
social_rating: 5
};
let citizen3031 = Citizen {
id_num: 2,
weight: 255,
height: 78,
first_name: "Lex",
last_name: "Luther",
phone_number: "333-333-2323",
age: 56,
sex: "m",
marital_status: "Single",
social_rating: 7
};
println!("{:?}",citizen3030);
println!("{:?}",citizen3031);
println!("First Name of cit3030, {}", citizen3030.first_name );
let temps = citizen3031.first_name;
println!("{}", temps);
get_social_status(citizen3030);
}

Methods take in self (or variants like &self or &mut self) and refer to the value being called. So you can implement your function simply as:
impl Citizen {
fn get_social_status(&self) {
if self.social_rating >= 7 {
println!("Good");
} else if self.social_rating >= 5 && self.age <= 20 {
println!("Fair");
} else {
println!("Not Great");
}
}
}
and call it using:
citizen3030.get_social_status();
Perhaps you can read through Method Syntax from the Rust Programming Language Book.

Related

the trait `PartialEq<Option<_>>` is not implemented

I have a struct where I've derived a couple of things.
#[derive(PartialEq, Debug)]
struct Subscriber {
id: u16,
up_speed: u32,
down_speed: u32
}
However, when I try to use PartialEq, I get told it is not implemented.
for (id, subscriber) in &new_hashmap {
let original_subscriber = original_hashmap.get(id).unwrap();
if original_subscriber == None {
changed_hashmap.insert(subscriber.id, subscriber);
} else if subscriber != original_subscriber {
changed_hashmap.insert(subscriber.id, subscriber);
}
}
Here's the compiler error.
error[E0277]: can't compare `&Subscriber` with `Option<_>`
--> src/main.rs:34:32
|
34 | if original_subscriber == None {
| ^^ no implementation for `&Subscriber == Option<_>`
|
= help: the trait `PartialEq<Option<_>>` is not implemented for `&Subscriber`
= help: the trait `PartialEq` is implemented for `Subscriber`
If I rewrite it to not put original_subscriber into its own variable, then it works.
for (id, subscriber) in &new_hashmap {
if original_hashmap.get(id) == None {
changed_hashmap.insert(subscriber.id, subscriber);
} else if subscriber != original_hashmap.get(id).unwrap() {
changed_hashmap.insert(subscriber.id, subscriber);
}
}
The rest of the code is essentially doing the following.
Create HashMap of 2 Subscriber instances.
Create another HashMap of 3 Subscriber instances, 1 of which is new, 1 of which is the same, and 1 of which has the same key but an updated value.
That is original_hashmap HashMap and new_hashmap.
The goal is to get a third HashMap of items in new_hashmap that are new to original_hashmap or have changed values.
your code does not work for 2 reasons.
If you derive PartialEq it will only work for Subscriber == Subscriber checks. You need to implement PartialEq<Type>
You are using a reference when comparing. This means you need to implement PartialEq for &Subscriber and not subscriber
This should do the trick
#[derive(PartialEq, Debug)]
struct Subscriber {
id: u16,
up_speed: u32,
down_speed: u32,
}
let subscriber = Subscriber {
id: 1,
up_speed: 100,
down_speed: 100,
};
impl PartialEq<Option<Subscriber>> for &Subscriber {
fn eq(&self, other: &Option<Subscriber>) -> bool {
match other {
Some(other) => return other == *self,
None => return false,
}
}
}
if &subscriber == None {
println!("None");
} else {
println!("Some");
}
But I am not sure if this is really what you want. I will try to implement the same and edit my answer afterwards
I suppose that's what you want to implement
use std::collections::HashMap;
#[derive(Debug, PartialEq)]
struct Subscriber {
id: u16,
up_speed: u32,
down_speed: u32,
}
impl Subscriber {
fn new(id: u16, up_speed: u32, down_speed: u32) -> Subscriber {
Subscriber {
id,
up_speed,
down_speed,
}
}
}
fn main() {
let mut old_map = HashMap::new();
old_map.insert(1, Subscriber::new(1, 1, 1));
old_map.insert(2, Subscriber::new(2, 2, 2));
let mut new_map = HashMap::new();
new_map.insert(0, Subscriber::new(0, 0, 0)); //new
new_map.insert(1, Subscriber::new(1, 1, 1)); //Same
new_map.insert(2, Subscriber::new(3, 3, 3)); //Same key but different value
let mut changed_map = HashMap::new();
//
for (key, subscriber) in &new_map {
if old_map.contains_key(&key) {
if old_map[&key] != *subscriber {
changed_map.insert(key, subscriber);
}
} else {
changed_map.insert(key, subscriber);
}
}
println!("{:?}", changed_map);
}
It will return
{2: Subscriber { id: 3, up_speed: 3, down_speed: 3 }, 0: Subscriber { id: 0, up_speed: 0, down_speed: 0 }}
I used the deref operator to avoid impl PartialEq<Subscriber> for &Subscriber but you could have done that as well

How to access struct properties dynamically?

I am a Rust beginner and was wondering how to access a struct's fields dynamically:
use std::collections::HashMap;
struct User {
email: String,
name: String,
}
impl User {
fn new(attributes: &HashMap<String,String>) -> User {
let mut model = User {
email: "",
name: "",
};
for (attr_name,attr_value) in attributes {
// assign value "attr_value" to attribute "attr_name"
// no glue how to do this
// in php would be: $model->{$attr_name} = $attr_value;
model.*attr_name *= attr_value;
}
model;
}
}
fn main() {
let mut map: HashMap::new();
map.insert("email",String::from("foo#bar.de"));
map.insert("name",String::from("John doe"));
user_model = User::new(&map);
println!("{:?}",user_model);
}
How it is possible to initialize a struct by given HashMap?
Unless you change your User to contain a HashMap then Rust can't do that kind of "magic" (or it will require some proc macro usage, which is not beginner friendly).
Instead you can use a match, and match all the keys and update the User fields:
for (attr_name, attr_value) in attributes {
match attr_name {
"email" => model.email = attr_value.clone(),
"name" => model.name = attr_value.clone(),
_ => {}
}
}
However, instead of having empty Strings, I'd suggest using Option<String>.
struct User {
email: Option<String>,
name: Option<String>,
}
Then you can simplify your whole new method to just:
fn new(attributes: &HashMap<String, String>) -> User {
User {
email: attributes.get("email").cloned(),
name: attributes.get("name").cloned(),
}
}
Since you have some mixed String and &'static str usage, along with Debug not being implemented. Then here is the complete example:
use std::collections::HashMap;
#[derive(Debug)]
struct User {
email: Option<String>,
name: Option<String>,
}
impl User {
fn new(attributes: &HashMap<String, String>) -> User {
User {
email: attributes.get("email").cloned(),
name: attributes.get("name").cloned(),
}
}
}
fn main() {
let mut map = HashMap::new();
map.insert(String::from("email"), String::from("foo#bar.de"));
map.insert(String::from("name"), String::from("John doe"));
let user_model = User::new(&map);
println!("{:?}", user_model);
}

Struct with partial move errors

I've got a simple struct and two instances of it as below:
#[derive(Debug)]
struct User {
first: String,
last: String,
age: u32,
}
let u1 = User {
first: String::from("John"),
last: String::from("Doe"),
age: 22,
};
let u2 = User {
first: String::from("Mary"),
..u1
};
println!("user: {:#?}", u1);
Error message:
error[E0382]: borrow of moved value: `u1`
--> src/main.rs:20:29
|
15 | let u2 = User {
| ______________-
16 | | first: String::from("Mary"),
17 | | ..u1
18 | | };
| |_____- value moved here
19 |
20 | println!("user: {:#?}", u1);
| ^^ value borrowed here after partial move
|
= note: move occurs because `u1.last` has type `std::string::String`, which does not implement the `Copy` trait
I tried to revise it to ..&u1 hoping it will pass the borrow check so that I could spread in the base struct (u1) to u2, but to no avail, wondering is it even possible for what I wanted to do here?
I understand it's because u1.last is a String, hence needing to pass a reference, but I'm not sure how to make it work in this scenario.
Your User type contains the type String, which owns the string data that it has (and doesn't impl Copy), which is why two users can't point to the same name in memory.
The solution you probably want:
#[derive(Debug, Clone)]
struct User {
first: String,
last: String,
age: u32,
}
fn main() {
let u1 = User {
first: String::from("John"),
last: String::from("Doe"),
age: 22,
};
let u2 = User {
first: String::from("Mary"),
..u1.clone() // Copy the strings into the new user
// (it also copies first, which is then thrown away? Maybe the compiler optimizes this away)
};
println!("user: {:#?}", u1);
}
but if you really want to have two users point to the same name in memory (pretty sure you don't), there are a couple of options:
You can change String to &'static str. This however means that you have to specify it when you compile. (You can't have a user type in their name at runtime, and store it in user)
#[derive(Debug)]
struct User {
first: &'static str,
last: &'static str,
age: u32,
}
fn main() {
let u1 = User {
first: "John",
last: "Doe",
age: 22,
};
let u2 = User {
first: "Mary",
..u1
};
println!("user: {:#?}", u1);
}
You can use a Rc to keep track of the references to a piece of memory. This way you don't have to worry about lifetimes and who's owning what. (Comes at a tiny runtime cost though)
use std::rc::Rc;
#[derive(Debug, Clone)]
struct User {
first: Rc<String>,
last: Rc<String>,
age: u32,
}
fn main() {
let u1 = User {
first: Rc::new(String::from("John")),
last: Rc::new(String::from("Doe")),
age: 22,
};
let u2 = User {
first: Rc::new(String::from("Mary")),
..u1.clone() // Clone the references, not the actual string. For strings with just a couple characters, the time difference is completely negligible)
};
println!("user: {:#?}", u1);
}
Use a Rc<Mutex<String>> instead if you want to modify the name later, and have it change in both u1 and u2.

Should I parse text input to a "dynamic struct" to do logic on it?

I want to parse some text that I want to do logic on (count, sort, filter, and such). Should I parse the text input to a "dynamic struct"? From what I've gathered when searching the web this is not entirely straightforward to solve in Rust.
I have this input from a file:
#1:PERSON["#2", 180, 85, 28]
#2:EYES["green", false]
#3 ...
I want to instantiate Person structs, preferably without having to match on every possible struct:
struct ParseResult {
class: String,
params: String,
}
struct Person {
eyes: Eyes,
age: i32,
weight: i32,
height: i32,
}
struct Eyes {
color: String,
glasses: bool,
}
fn parse(input: String) -> Result<Person, &'static str> {
// ...parsing magic
let x = ParseResult {
class: "PERSON".to_string(),
params: "\"#2\", 180, 85, 28, \"#3\"".to_string(),
};
// I don't want to do this if I can avoid it:
let new_struct = match x.class {
"PERSON" => { /* instantiate new person */ }
"EYES" => { /* instantiate new eyes */ }
// ...
};
// ...
Ok(new_struct)
}
#[test]
fn parse_input() {
let p = parse("#1:PERSON[\"#2\", 180, 85, 28]\n#2:EYES[\"green\", false]".to_string()).unwrap();
assert_eq!(p.age, 28);
}
Is this the correct way to think about this problem? Should I think about this issue in some other way?

Formatting a struct field differently than its JSON key

The rustc-serialize package for Rust allows a struct to be serialized or deserialized to JSON automatically in some cases by deriving from RustcEncodable and RustcDecodable, respectively. For example, the following struct:
#[derive(RustcEncodable, RustcDecodable)]
struct Person {
first_name: String,
last_name: String,
age: u8,
}
would be represented as this JSON:
{
"first_name": "Joe",
"last_name": "Shmoe",
"age": 30
}
I need to deserialize some JSON that uses camel-cased keys. This seems to require that the struct fields be named similarly, which emits a compiler warning that struct fields should be snake-cased. I can silence this warning with #[allow(non_snake_case)], but I'd rather have the struct field snake-cased. Is there a way to do this without manually implementing JSON serialization/deserialization with the ToJson/Encodable/Decodable traits?
No, the #[derive] infrastructure provides no opportunities for customisation at all.
You can, however, know what the #[derive] stuff expands to with rustc -Z unstable-options --pretty expanded:
#![feature(no_std)]
#![no_std]
#[prelude_import]
use std::prelude::v1::*;
#[macro_use]
extern crate std as std;
struct Person {
first_name: String,
last_name: String,
age: u8,
}
#[automatically_derived]
impl ::rustc_serialize::Decodable for Person {
fn decode<__D: ::rustc_serialize::Decoder>(__arg_0: &mut __D)
-> ::std::result::Result<Person, __D::Error> {
__arg_0.read_struct("Person", 3usize, |_d| -> _ {
::std::result::Result::Ok(Person{first_name:
match _d.read_struct_field("first_name",
0usize,
::rustc_serialize::Decodable::decode)
{
::std::result::Result::Ok(__try_var)
=>
__try_var,
::std::result::Result::Err(__try_var)
=>
return ::std::result::Result::Err(__try_var),
},
last_name:
match _d.read_struct_field("last_name",
1usize,
::rustc_serialize::Decodable::decode)
{
::std::result::Result::Ok(__try_var)
=>
__try_var,
::std::result::Result::Err(__try_var)
=>
return ::std::result::Result::Err(__try_var),
},
age:
match _d.read_struct_field("age",
2usize,
::rustc_serialize::Decodable::decode)
{
::std::result::Result::Ok(__try_var)
=>
__try_var,
::std::result::Result::Err(__try_var)
=>
return ::std::result::Result::Err(__try_var),
},}) })
}
}
#[automatically_derived]
impl ::rustc_serialize::Encodable for Person {
fn encode<__S: ::rustc_serialize::Encoder>(&self, __arg_0: &mut __S)
-> ::std::result::Result<(), __S::Error> {
match *self {
Person {
first_name: ref __self_0_0,
last_name: ref __self_0_1,
age: ref __self_0_2 } =>
__arg_0.emit_struct("Person", 3usize, |_e| -> _ {
match _e.emit_struct_field("first_name",
0usize, |_e| -> _ {
(*__self_0_0).encode(_e)
}) {
::std::result::Result::Ok(__try_var) =>
__try_var,
::std::result::Result::Err(__try_var) =>
return ::std::result::Result::Err(__try_var),
};
match _e.emit_struct_field("last_name",
1usize, |_e| -> _ {
(*__self_0_1).encode(_e)
}) {
::std::result::Result::Ok(__try_var) =>
__try_var,
::std::result::Result::Err(__try_var) =>
return ::std::result::Result::Err(__try_var),
};
return _e.emit_struct_field("age", 2usize,
|_e| -> _ {
(*__self_0_2).encode(_e)
}); }),
}
}
}
Yeah, it’s pretty clumsy, all expanded like that, but it can be collapsed quite easily and altered to suit:
impl Decodable for Person {
fn decode<D: Decoder>(decoder: &mut D) -> Result<Person, D::Error> {
decoder.read_struct("Person", 3, |d| Ok(Person {
first_name: try!(d.read_struct_field("firstName", 0, Decodable::decode)),
last_name: try!(d.read_struct_field("lastName", 1, Decodable::decode)),
age: try!(d.read_struct_field("age", 2, Decodable::decode)),
}))
}
}
impl Encodable for Person {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("Person", 3, |e| {
try!(e.emit_struct_field("firstName", 0, |e| self.first_name.encode(e)));
try!(e.emit_struct_field("lastName", 1, |e| self.last_name.encode(e)));
e.emit_struct_field("age", 2, |e| self.age.encode(e))
})
}
}

Resources