rust - methods as argument including a self reference - rust

I have the following problem:
I have two structs A and B, A implements a method, which takes another method as argument. I then create an instance of A within B. I then call the method of A with a method of B as argument. However, the passed method of B contains self as argument, since I have to access some other fields of B within the passed method.
My current plan would be to pass the method itself and self referencing an instance of B to the method in A. However, I struggle with the type definitions. I can hardcode the type of the second parameter to be B, however, I would like to keep it open, in case a struct C would also like to use the method in A. I therefore would like to set the type of the second parameter to the type of the struct, where the method in the first parameter is originating. Is there a way for achieving this? I was thinking about generics, however, I was not yet able to implement it.
Edit: added a minimal example in rust playground. What bothers me about this code, is, that I want to get rid of the hard coded &B in some_func_a, since this does not work with struct C.
struct A {}
impl A {
fn some_func_a(&self, passed_fn: fn(&B, i32), cur_self: &B) {
passed_fn(cur_self, 21);
}
}
struct B {
some_val: i32,
}
struct C {
some_val: u32, // different field here than in B
}
impl B {
fn call_fn_in_a(&self, a: A) {
a.some_func_a(B::some_func_b, self);
}
fn some_func_b(&self, passed_val: i32) {
println!("The value is {}, passed was {}", self.some_val, passed_val)
}
}
impl C {
fn call_fn_in_a(&self, a: A) {
// this line here breaks, since &B is hard coded
a.some_func_a(C::some_func_c, self);
}
fn some_func_c(&self, passed_val: i32) {
println!("this is a new function, val is {}, passed was {}", self.some_val, passed_val)
}
}
fn main() {
let a = A {};
let b = B {
some_val: 42,
};
let b = B {
some_val: 42,
};
b.call_fn_in_a(a);
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=53ac44cb660d180b3c7351e394355692

It sounds like you want a closure instead of a function pointer. This would allow you to eliminate the extra parameter of A::some_func_a() altogether:
impl A {
fn some_func_a(&self, passed_fn: impl FnOnce(i32) -> ()) {
passed_fn(21);
}
}
Now we can call this function with any closure matching the call signature. For example:
impl B {
fn call_fn_in_a(&self, a: A) {
a.some_func_a(|v| self.some_func_b(v));
}
fn some_func_b(&self, passed_val: i32) {
println!("The value is {}, passed was {}", self.some_val, passed_val)
}
}
And likewise for C.
Now there are no restrictions on what extra data the function needs. Before there wasn't even a way to express the situation where the callback didn't need the reference parameter. Now it doesn't matter because the closure can capture what it needs, and it's no longer A::some_func_a()'s responsibility.
(Playground)
Side note: you probably want your call_fn methods to take the A by reference (&A), not by value. Otherwise calling these functions consumes the A value, requiring you to make another one to call the functions again.

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)()
}
}

How to return a reference to a method and the struct it relates to

I have a function that will create one of several structs (all of which have a method of the same signature but have other, different, methods and traits); I would like to instance one of the structs in a function and return a reference to its method that can be called elsewhere.
// Pseudocode
type SizeGetter = fn()-> isize;
fn get_ref()-> &SizeGetter{
let catch = Fish{weight: 12};
&catch.get_weight()
//Fish.get_weight() is used here but it may be any struct.method() -> isize
}
fn main() {
let getit = get_ref();
println!("{}", getit());
}
In the above my goal is to define catch.getweight() in a function, return a reference to that function and then call it later to get the size.
That original attempt could not work because you cannot return a reference to something created in a function. In this case, returning something equivalent to a method to a locally created struct value requires the value to outlive the function's lifetime as well.
We can reference a method bar in a struct Foo with Foo::bar, but this one isn't bound to a receiver value. There is no syntax specifically for referencing a method call on a value. The solution instead is to create a closure that captures the value and calls the method.
let foo = Foo::new();
move || foo.bar()
Considering this Fish struct and implementation (adjusted to comply with naming conventions):
struct Fish {
weight: usize,
}
impl Fish {
fn weight(&self) -> usize {
self.weight
}
}
A function returning another self-sufficient function would be written like so:
fn fish_weight() -> impl Fn() -> usize {
let r#catch = Fish { weight: 12 };
move || r#catch.weight()
}
Using:
let get = fish_weight();
println!("Fish weight: {}", get());
Playground

How to handle the value I get from a HashMap

I want my getv() function return the value from a HashMap. I get errors no matter how I modify my code:
enum Type {
TyInt,
TyBool,
}
struct TypeEnv {
Env: HashMap<char, Type>,
}
impl TypeEnv {
fn set(&mut self, name: char, ty: Type) {
self.Env.insert(name, ty);
}
fn getv(self, name: char) -> Type {
match self.Env.get(&name) {
Some(Type) => Type, // <------- Always error here
None => Type::TyInt,
}
}
}
HashMap::get returns an Option<&Type>, not an Option<Type>, which is why just returning the matched value fails to compile.
get provides a reference because in Rust you cannot simply return the actual value that is in the hash table - you need to either clone it (make a copy, potentially expensive), remove it from the container and transfer it to the caller, or return a reference to the value inside. HashMap::get chooses the last option because it is cheap while providing the greatest flexibility to the caller, which can choose to either inspect the value or clone it.
For your enum that consists of a single machine word, copying the value would be the optimal approach. (For more complex enums, a better approach is to return a reference as shown in Simon's answer.) Copying requires:
making Type copyable, by marking it with the Copy trait using the #[derive(Copy, Clone)] directive at the type definition.
returning the copy from getv by dereferencing the matched value using *matched_value, or by using & in the pattern.
Finally, your getv method consumes the object, which means that you can call it only once. This is almost certainly not what was intended - it should accept &self instead. With those changes, the resulting code looks like this:
use std::collections::HashMap;
#[derive(Copy, Clone)]
enum Type {
TyInt,
TyBool,
}
struct TypeEnv {
env: HashMap<char, Type>,
}
impl TypeEnv {
fn set(&mut self, name: char, ty: Type) {
self.env.insert(name, ty);
}
fn getv(&self, name: char) -> Type {
match self.env.get(&name) {
Some(&v) => v,
None => Type::TyInt,
}
}
}
If the type needs to contain non-Copy data, then you can make it only Clone instead, and return v.clone().
You don't have to make your type Copy or Clone ... if you're happy working with the reference.
When things like this pop up while writing Rust, its generally best to re-evaluate how you're calling the code. Can it be made simpler?
You can either have the caller expect the Option<&V>, or just force something out... perhaps like this:
fn getv(&self, name: char) -> &Type {
self.env.get(&name).unwrap_or(&Type::TyInt)
}
This simplifies your code and makes it pretty clear what you're after. Here it is in action:
use std::collections::HashMap;
#[derive(Debug)]
enum Type {
TyInt,
TyBool,
}
struct TypeEnv {
env: HashMap<char, Type>,
}
impl TypeEnv {
fn set(&mut self, name: char, ty: Type) {
self.env.insert(name, ty);
}
fn getv(&self, name: char) -> &Type {
self.env.get(&name).unwrap_or(&Type::TyInt)
}
}
fn main() {
let mut te = TypeEnv {
env: HashMap::new(),
};
{
let arg = te.getv('c');
println!("{:?}", arg); // "TyInt" - because nothing was found
}
te.set('c', Type::TyBool);
let arg = te.getv('c'); // "TyBool" - because we stored it in the line above.
println!("{:?}", arg);
}
Also, your existing implementation of getv takes ownership of self ... notice how I added the reference getv(&self). If you don't have this, this type (as far as I can tell) becomes basically useless.

How do I get a function pointer from a trait in Rust?

How do I get over something like this:
struct Test {
foo: Option<fn()>
}
impl Test {
fn new(&mut self) {
self.foo = Option::Some(self.a);
}
fn a(&self) { /* can use Test */ }
}
I get this error:
error: attempted to take value of method `a` on type `&mut Test`
--> src/main.rs:7:36
|
7 | self.foo = Option::Some(self.a);
| ^
|
= help: maybe a `()` to call it is missing? If not, try an anonymous function
How do I pass a function pointer from a trait? Similar to what would happen in this case:
impl Test {
fn new(&mut self) {
self.foo = Option::Some(a);
}
}
fn a() { /* can't use Test */ }
What you're trying to do here is get a function pointer from a (to use Python terminology here, since Rust doesn't have a word for this) bound method. You can't.
Firstly, because Rust doesn't have a concept of "bound" methods; that is, you can't refer to a method with the invocant (the thing on the left of the .) already bound in place. If you want to construct a callable which approximates this, you'd use a closure; i.e. || self.a().
However, this still wouldn't work because closures aren't function pointers. There is no "base type" for callable things like in some other languages. Function pointers are a single, specific kind of callable; closures are completely different. Instead, there are traits which (when implemented) make a type callable. They are Fn, FnMut, and FnOnce. Because they are traits, you can't use them as types, and must instead use them from behind some layer of indirection, such as Box<FnOnce()> or &mut FnMut(i32) -> String.
Now, you could change Test to store an Option<Box<Fn()>> instead, but that still wouldn't help. That's because of the other, other problem: you're trying to store a reference to the struct inside of itself. This is not going to work well. If you manage to do this, you effectively render the Test value permanently unusable. More likely is that the compiler just won't let you get that far.
Aside: you can do it, but not without resorting to reference counting and dynamic borrow checking, which is out of scope here.
So the answer to your question as-asked is: you don't.
Let's change the question: instead of trying to crowbar a self-referential closure in, we can instead store a callable that doesn't attempt to capture the invocant at all.
struct Test {
foo: Option<Box<Fn(&Test)>>,
}
impl Test {
fn new() -> Test {
Test {
foo: Option::Some(Box::new(Self::a)),
}
}
fn a(&self) { /* can use Test */ }
fn invoke(&self) {
if let Some(f) = self.foo.as_ref() {
f(self);
}
}
}
fn main() {
let t = Test::new();
t.invoke();
}
The callable being stored is now a function that takes the invocant explicitly, side-stepping the issues with cyclic references. We can use this to store Test::a directly, by referring to it as a free function. Also note that because Test is the implementation type, I can also refer to it as Self.
Aside: I've also corrected your Test::new function. Rust doesn't have constructors, just functions that return values like any other.
If you're confident you will never want to store a closure in foo, you can replace Box<Fn(&Test)> with fn(&Test) instead. This limits you to function pointers, but avoids the extra allocation.
If you haven't already, I strongly urge you to read the Rust Book.
There are few mistakes with your code. new function (by the convention) should not take self reference, since it is expected to create Self type.
But the real issue is, Test::foo expecting a function type fn(), but Test::a's type is fn(&Test) == fn a(&self) if you change the type of foo to fn(&Test) it will work. Also you need to use function name with the trait name instead of self. Instead of assigning to self.a you should assign Test::a.
Here is the working version:
extern crate chrono;
struct Test {
foo: Option<fn(&Test)>
}
impl Test {
fn new() -> Test {
Test {
foo: Some(Test::a)
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
test.foo.unwrap()(&test);
}
Also if you gonna assign a field in new() function, and the value must always set, then there is no need to use Option instead it can be like that:
extern crate chrono;
struct Test {
foo: fn(&Test)
}
impl Test {
fn new() -> Test {
Test {
foo: Test::a
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
(test.foo)(&test); // Make sure the paranthesis are there
}

Resources