Pass a function to an object as creation parameter - rust

I am creating an object of tesla, that comes from the struct of Car.
The way each car gets energy to work is different, in the case of a Tesla, we plug in a cable and pass electricity, in the case of a Honda, we insert oil into a tank. I want my car struct to have a property of get_energy with a Fn datatype, so that when I create a new car I can pass in a function that will be called when I call tesla.get_energy()... the creation of tesla would ideally be something like let tesla = Car(get_energy: || => {grab_cable(), insert_cable(), pass_electricty()}) which would look different from the creation of a Honda. Can I somehow do that?

For the love of god I hope I'm not answering an undergrad's homework.
But assuming good faith, this is my approach. I would probably store that function/closure and box it. It will look like this.
struct Foo {
pub foo: Box<dyn Fn(usize) -> usize>,
}
impl Foo {
fn new(foo: impl Fn(usize) -> usize + 'static) -> Self {
Self { foo: Box::new(foo) }
}
}
fn main() {
let foo = Foo {
foo: Box::new(|a| a + 1),
};
(foo.foo)(42);
(Foo::new(|a| a + 1).foo)(42);
}
I of course stole it from here How do I store a closure in a struct in Rust?. Not going to flag the question yet because I'm not used to stackoverflow.

Related

Reduce handling of Optional by leveraging the type system?

I'm trying to access an api, where I can specify what kind of fields I want included in the result. (for example "basic", "advanced", "irrelevant"
the Rust Struct to represent that would look something like
Values {
a: Option<String>;
b: Option<String>;
c: Option<String>;
d: Option<String>;
}
or probably better:
Values {
a: Option<Basic>; // With field a
b: Option<Advanced>; // With fields b,c
c: Option<Irrelevant>; // With field d
}
Using this is possible, but I'd love to reduce the handling of Option for the caller.
Is it possible to leverage the type system to simplify the usage? (Or any other way I'm not realizing?)
My idea was something in this direction, but I think that might not be possible with rust (at least without macros):
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=093bdf1853978af61443d547082576ca
struct Values {
a: Option<&'static str>,
b: Option<&'static str>,
c: Option<&'static str>,
}
trait ValueTraits{}
impl ValueTraits for dyn Basic{}
impl ValueTraits for dyn Advanced{}
impl ValueTraits for Values{}
trait Basic {
fn a(&self) -> &'static str;
}
trait Advanced {
fn b(&self) -> &'static str;
fn c(&self) -> &'static str;
}
impl Basic for Values {
fn a(&self) -> &'static str {
self.a.unwrap()
}
}
impl Advanced for Values {
fn b(&self) -> &'static str {
self.b.unwrap()
}
fn c(&self) -> &'static str {
self.c.unwrap()
}
}
//Something like this is probably not possible, as far as I understand Rust
fn get_values<T1, T2>() -> T1 + T2{
Values {
a: "A",
b: "B",
c: "C"
}
}
fn main() {
let values = get_values::<Basic, Advanced>();
println!("{}, {}, {}", values.a(), values.b(), values.c());
}
Clarifications (Edit)
The Values struct contains deserialized json data from the api I called. I can request groups of fields to be included in the response(1-n requested fields groups), the fields are of different types.
If I knew beforehand, which of those fields are returned, I wouldn't need them to be Option, but as the caller decides which fields are returned, the fields needs to be Option (either directly, or grouped by the field groups)
There are too many possible combinations to create a struct for each of those.
I fully realize that this cannot work, it was just "peudorust":
fn get_values<T1, T2>() -> T1 + T2{
Values {
a: "A",
b: "B",
c: "C"
}
}
But my thought process was:
In theory, I could request the field groups via generics, so I could create a "dynamic" type, that implements these traits, because I know which traits are requested.
The Traits are supposed to act like a "view" into the actual struct, because if they are requested beforehand, I know I should request them from the api to include them in the Struct.
My knowledge of generics and traits isn't enough to confidently say "this isn't possible at all" and I couldn't find a conclusive answer before I asked here.
Sorry for the initial question not being clear of what the actual issue was, I hope the clarification helps with that.
I can't quite gauge whether or not you want to be able to request and return fields of multiple different types from the question. But if all the information being returned is of a single type you could try using a HashMap:
use std::collections::HashMap;
fn get_values(fields: &[&'static str]) -> HashMap<&'static str, &'static str> {
let mut selected = HashMap::new();
for field in fields {
let val = match *field {
"a" => "Value of a",
"b" => "Value of b",
"c" => "Value of c",
// Skip requested fields that don't exist.
_ => continue,
};
selected.insert(*field, val);
}
selected
}
fn main() {
let fields = ["a","c"];
let values = get_values(&fields);
for (field, value) in values.iter() {
println!("`{}` = `{}`", field, value);
}
}
Additionally you've given me the impression that you haven't quite been able to form a relationship between generics and traits yet. I highly recommend reading over the book's "Generic Types, Traits, and Lifetimes" section.
The gist of it is that generics exist to generalize a function, struct, enum, or even a trait to any type, and traits are used to assign behaviour to a type. Traits cannot be passed as a generic parameter, because traits are not types, they are behaviours. Which is why doing: get_values::<Basic, Advanced>(); doesn't work. Basic and Advanced are both traits, not types.
If you want practice with generics try generalizing get_values so that it can accept any type which can be converted into an iterator that yields &'static strs.
Edit:
The clarification is appreciated. The approach you have in mind is possible, but I wouldn't recommend it because it's implementing it is extremely verbose and will panic the moment the format of the json you're parsing changes. Though if you really need to use traits for some reason you could try something like this:
// One possible construct returned to you.
struct All {
a: Option<i32>,
b: Option<i32>,
c: Option<i32>,
}
// A variation that only returned b and c
struct Bc {
b: Option<i32>,
c: Option<i32>,
}
// impl Advanced + Basic + Default for All {...}
// impl Advanced + Default for Bc {...}
fn get_bc<T: Advanced + Default>() -> T {
// Here you would set the fields on T.
Default::default()
}
fn get_all<T: Basic + Advanced + Default>() -> T {
Default::default()
}
fn main() {
// This isn't really useful unless you want to create multiple structs that
// are supposed to hold b and c but otherwise have different fields.
let bc = get_bc::<Bc>();
let all = get_all::<All>();
// Could also do something like:
let bc = get_bc::<All>();
// but that could get confusing.
}
I think the above is how you're looking to solve your problem. Though if you can, I would still recommend using a HashMap with a trait object like this:
use std::collections::HashMap;
use std::fmt::Debug;
// Here define the behaviour you need to interact with the data. In this case
// it's just ability to print to console.
trait Value: Debug {}
impl Value for &'static str {}
impl Value for i32 {}
impl<T: Debug> Value for Vec<T> {}
fn get_values(fields: &[&'static str]) -> HashMap<&'static str, Box<dyn Value>> {
let mut selected = HashMap::new();
for field in fields {
let val = match *field {
"a" => Box::new("Value of a") as Box<dyn Value>,
"b" => Box::new(2) as Box<dyn Value>,
"c" => Box::new(vec![1,3,5,7]) as Box<dyn Value>,
// Skip requested fields that don't exist.
_ => continue,
};
selected.insert(*field, val);
}
selected
}
fn main() {
let fields = ["a","c"];
let values = get_values(&fields);
for (field, value) in values.iter() {
println!("`{}` = `{:?}`", field, value);
}
}

Can you store functions as fields in a struct? How to store instructions?

In my model, I have a Petgraph graph which stores as nodes a struct with fields as followed:
struct ControlBloc
{
name:String,
message_inbox:Vec<MessageObj>,
blocked:bool,
instruct:String,
inbox_capacity:f64,
buffer:Vec<MessageObj>,
number_discarded:u32,
clock_queue:SendingQueue,
clock_speed:f64,
}
In it there is a field called instruct in which I want to store instructions. I want to code the model in a way such that after some time, all the nodes will execute the instructions that are stored in the struct. Instructions can be for example send messages to other nodes, computing something... I want something versatile.
Is there a way to store functions as fields in a struct? and then after some time, the function stored can be called and whatever function will be executed?
One way that I see doing this is maybe using enum to store all the function names then using a function to map whatever enum to the corresponding function, for example:
enum FuncName {
SendMessage,
ComputeSize,
StoreSomething,
DoNothing,
}
fn exec_function(func:FuncName)
{
match func {
FuncName::SendMessage => send_message_function(input1,input2),
FuncName::ComputeSize => compute_size_function(input1,input2,input3),
FuncName::StoreSomething => store_something_funtion(input1),
FuncName::DoNothing => (),
}
}
However in this case you can't really customize the inputs of the FuncName function and they either have to be always preset to the same thing or in the input of exec_function you add all the different inputs fields of all the functions in FuncName but that seems rather overkill, even then, I dont really see how to pass them and store in the struct.
Is there then a way to directly add the functions or something in the struct? I know I'm breaking many Rust rules but say for example I had a variable already declared let bloc = ControlBloc::new(...); then you could set the function as for example bloc.instruct = send_message_function(node1,node2); and then when you called bloc.instruct then that would call whatever function is stored there.
Is something like this possible or am I dreaming or like very difficult (I am still learning the language)?
What you can do is storing Box<dyn Fn()> in your struct:
struct Foo {
instruct: Box<dyn Fn(Vec<i32>)>
}
fn sum(vec: Vec<i32>) {
let sum: i32 = vec.into_iter().sum();
println!("{}", sum);
}
fn main() {
let foo = Foo {
instruct: Box::new(|vec| {
let sum: i32 = vec.into_iter().sum();
println!("{}", sum);
})
};
(foo.instruct)(vec![1, 2, 3, 4]);
let foo = Foo {
instruct: Box::new(sum)
};
(foo.instruct)(vec![1, 2, 3, 4]);
}
Fn is implemented automatically by closures which only take immutable references to captured variables or don’t capture anything at all, as well as (safe) function pointers (with some caveats, see their documentation for more details). Additionally, for any type F that implements Fn, &F implements Fn, too.
#EDIT
In my example I used Vec<i32> as an abstract for multiple arguments. However if you are going to have some set of instructions that have different count of arguments, but within itself always the same, you might consider creating a trait Instruct and create struct for every different instruct that will implement this.
Playground
struct Foo<T> {
instruct: Box<dyn Instruct<T>>
}
trait Instruct<T> {
fn run(&self) -> T;
}
struct CalcSum {
f: Box<dyn Fn() -> i32>
}
impl CalcSum {
fn new(arg: Vec<i32>) -> CalcSum {
CalcSum {
f: Box::new(move || arg.iter().sum::<i32>()),
}
}
}
impl Instruct<i32> for CalcSum {
fn run(&self) -> i32 {
(self.f)()
}
}

Look up a struct from a user input string

I am writing a program which will receive user input from CSV or JSON (that doesn't really matter). There are potentially many inputs (each line of a CSV for example), which would reference different structs. So, I need to return an instance of a struct for each input string, but I don't know upfront which struct that would be. My attempt (code doesn't compile):
fn main () {
let zoo: Vec<Box<dyn Animal>>;
let user_input = "Cat,Persik";
let user_input = user_input.split(",");
match user_input.nth(0) {
"Cat" => zoo.push(Cat(user_input.nth(0))),
_ => zoo.push(Dog(user_input.nth(0))) //here user would be expected to provide a u8
}
}
trait Animal {}
struct Dog {
age: u8,
}
impl Animal for Dog {}
struct Cat {
name: String,
}
impl Animal for Cat {}
One way to do it is with if statements like this. But if there are hundreds of animals that would make the code pretty ugly. I have a macro which returns struct name for an instance, but I couldn't figure out a way to use that. I also thought about using enum for this, but couldn't figure out either.
Is there a shorter and more concise way of doing this?
Doesn't this way limit me in using only methods defined in the Animal trait on items of zoo? If so, is there a way around this constraint?
Essentially, I want to get a vector of structs, and to be able to use their methods freely. I don't know how many there will be, and I don't know in advance which structs exactly.
It's often helpful to use helper functions for parsing things. We can implement this function on the trait itself to keep the parsing function associated with the trait.
We'll have the function return Result<Box<dyn animal>, ()> since it's possible for parsing to fail. (We'd probably want a proper error type instead of () in real code.)
trait animal{}
impl dyn animal {
fn try_parse(kind: &str, data: &str) -> Result<Box<dyn animal>, ()> {
match kind {
"Cat" => Ok(Box::new(Cat { name: data.into() })),
"Dog" => Ok(Box::new(Dog { age: data.parse().map_err(|_e| ())? })),
_ => Err(()),
}
}
}
Ok, so now we have a function that can be used to parse a single animal, and has a way to signal failure. We could now parse a comma-separated string building off of this function, again signaling errors if the string doesn't contain a comma:
impl dyn animal {
// try_parse()
fn try_parse_comma_separated(input: &str) -> Result<Box<dyn animal>, ()> {
let split = input.split(',');
let parts = (split.next(), split.next());
// parts is a tuple of two Option<&str>. We can only proceed if both
// are Some.
match parts {
(Some(kind), Some(data)) => Self::try_parse(kind, data),
_ => Err(()),
}
}
}
Now our main() is trivial:
fn main() {
let mut zoo: Vec<Box<dyn animal>> = vec![];
let user_input = "Cat,Persik";
zoo.push(<dyn animal>::try_parse_comma_separated(user_input).unwrap());
}
Separating things out like this allows us to reuse these functions in other interesting ways. Let's say you wanted to parse a string like "Cat,Persik,Dog,5" as two values. That can now be done by using iterators and mapping over our parse function:
fn main() {
let user_input = "Cat,Persik,Dog,5";
let zoo = user_input.split(',').collect::<Vec<_>>()
.chunks_exact(2) // Group the input into slices of 2 elements each
.map(|s| <dyn animal>::try_parse(s[0], s[1]).unwrap())
.collect::<Vec<_>>();
}
To answer your question about a better way to do this when managing many implementors of animal, you could move the implementation-specific parsing logic into a similar function on each implementation instead, and call that functionality from <dyn animal>::try_parse(). The parsing logic has to live somewhere.
Doesn't this way limit me in using only methods defined in animal Trait on items of zoo? If so, is there a way around this constraint?
Without downcasting, yes. Generally when you have a collection of polymorphic values like dyn animal, you want to use them polymorphically -- invoking only methods defined on the animal trait. Each implementation of the trait on a specific type can implement the trait's interface however it makes sense for that animal.
Downcasting is non-trivial, but with a helper trait it becomes a bit more palatable:
trait AsAny {
fn as_any(&self) -> &dyn Any;
}
impl<T: 'static + animal> AsAny for T {
fn as_any(&self) -> &dyn Any { self }
}
trait animal: AsAny { }
Now, given an animal: Box<dyn Animal> you can use animal.as_any().downcast_ref::<Dog>() for example, which gives you back an Option<&Dog>. This will be None if the boxed animal isn't a dog. Based on the zoo in the last example (with a dog and a cat):
let dogs = zoo.iter()
// Filter down the zoo to just dogs (produces a sequence of &Dog)
.filter_map(|animal| animal.as_any().downcast_ref::<Dog>());
// We should only find one dog in the zoo.
assert_eq!(dogs.count(), 1);
But this should be an absolute last resort when using your animals polymorphically isn't an option.

Factory pattern in Rust

I have a Terminal and a TerminalFactory type. What I want to achieve is a factory pattern or at least an implementation that is similar to it in regards to some key aspects.
In particular, I want every Terminal to have specific properties (e.G. id) that are set by TerminalFactory. In GRASP terms, TerminalFactory is the Creator and the Information Expert because it knows next_id.
TerminalFactory is not a singleton, it is an instance that will be injected in a container that will be handed around where necessary (I'm trying to implement an application based on the composite pattern to achieve the OCP of SOLID).
It looks like everything is in line with the fact that Rust discourages static state unless it's unsafe code.
The problem now is that instances of Terminal have an id which should not be set by arbitrary code. Therefore, it's not pub. On the other hand, it can be gotten. So I added a public getter, like this:
pub struct Terminal {
id: u32, // private
pub name: String
}
impl Terminal {
pub fn get_id(self: &Self) -> u32 {
self.id
}
}
As far as I can tell, there is no friend keyword in rust, no static state I can keep in the Terminal type and TerminalFactory cannot set Terminal::id during creation because it's not public.
I have the impression I can't implement the factory pattern correctly. Maybe I shouldn't even try to transfer "classical patterns" to Rust? Then what about the SOLID principles which are absolutely necessary to manage change in medium-scale applications (say, 50+ types and beyond 10000 lines of code)?
By adding an associate function new(id : u32) to Terminal I completely defeat the purpose of the factory. So this won't make sense, either.
As all of this code will be in a library that is consumed by other Rust code, how can I protect the consumer code from creating broken instances?
Put both types in the same module:
mod terminal {
pub struct Terminal {
id: u32,
pub name: String
}
impl Terminal {
pub fn get_id(&self) -> u32 {
self.id
}
}
pub struct TerminalFactory {
next_id: u32,
}
impl TerminalFactory {
pub fn new() -> Self {
Self {
next_id: 0,
}
}
pub fn new_terminal(&mut self, name: &str) -> Terminal {
let next_id = self.next_id;
self.next_id += 1;
Terminal {
id: next_id,
name: name.to_owned(),
}
}
}
}
Then this is OK:
let mut tf = terminal::TerminalFactory::new();
let t0 = tf.new_terminal("foo");
let t1 = tf.new_terminal("bar");
println!("{} {}", t0.get_id(), t0.name);
println!("{} {}", t1.get_id(), t1.name);
But this is not:
println!("{} {}", t0.id, t0.name); // error: private field
See Visibility and privacy for other scenarios.

How best to deal with struct field that can change types

I'm working with a library that uses Rust types to keep track of state. As a simplified example, say you have two structs:
struct FirstStruct {}
struct SecondStruct {}
impl FirstStruct {
pub fn new() -> FirstStruct {
FirstStruct {}
}
pub fn second(self) -> SecondStruct {
SecondStruct {}
}
// configuration methods defined in this struct
}
impl SecondStruct {
pub fn print_something(&self) {
println!("something");
}
pub fn first(self) -> FirstStruct {
FirstStruct {}
}
}
And to actually use these structs you usually follow a pattern like so, after printing you may stay in second state or go back to first state depending on how you're using the library:
fn main() {
let first = FirstStruct::new();
let second = first.second(); // consumes first
second.print_something();
// go back to default state
let _first = second.first();
}
I want to create my own struct that handles the state changes internally and simplifies the interface. This also lets me have a single mutable reference around that I can pass to other functions and call the print method. Using it should look something like this:
fn main() {
let mut combined = CombinedStruct::new(FirstStruct::new());
combined.print();
}
I've come up with the following solution that works, at least in this simplified example:
enum StructState {
First(FirstStruct),
Second(SecondStruct),
}
struct CombinedStruct {
state: Option<StructState>,
}
impl CombinedStruct {
pub fn new(first: FirstStruct) -> CombinedStruct {
CombinedStruct {
state: Some(StructState::First(first)),
}
}
pub fn print(&mut self) {
let s = match self.state.take() {
Some(s) => match s {
StructState::First(first) => first.second(),
StructState::Second(second) => second,
},
None => panic!(),
};
s.print_something();
// If I forget to do this, then I lose access to my struct
// and next call will panic
self.state = Some(StructState::First(s.first()));
}
}
I'm still pretty new to Rust but this doesn't look right to me. I'm not sure if there's a concept I'm missing that could simplify this or if this solution could lead to ownership problems as my application gets more complicated. Is there a better way to do this?
Playground link
I once had a similar problem and went basically with your solution, but I avoided the Option.
I.e. I basically kept your
enum StructState {
First(FirstStruct),
Second(SecondStruct),
}
If an operation tries to convert a FirstStruct to a SecondStruct, I introduced a function try_to_second roughly as follows:
impl StructState {
fn try_to_second(self) -> Result<SecondState, StructState> {
/// implementation
}
}
In this case, an Err indicates that the StructState has not been converted to SecondStruct and preserves the status quo, while an Ok value indicates successfull conversion.
As an alternative, you could try to define try_to_second on FirstStruct:
impl FirstStruct {
fn try_to_second(self) -> Result<FirstStruct, SecondStruct> {
/// implementation
}
}
Again, Err/Ok denote failure/success, but in this case, you have more concrete information encoded in the type.

Resources