how to save a function pointer with dyn parameters in a structure - rust

I'm getting
error[E0308]: mismatched types
--> src/main.rs:38:39
|
37 | fn set_source_outcome<S: Source, O: Outcome>(&mut self, selector: fn(s: &S) -> &O) {
| - this type parameter
38 | self.selector = Some(Box::new(selector));
| ^^^^^^^^ expected trait object `dyn Source`, found type parameter `S`
|
= note: expected fn pointer `for<'r> fn(&'r (dyn Source + 'r)) -> &'r (dyn Outcome + 'r)`
found fn pointer `for<'r> fn(&'r S) -> &'r O`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
when trying to save a function with dyn parameters in a structure, like this:
struct X {
selector: Option<Box<fn(s: &dyn Source) -> &dyn Outcome>>,
}
impl X {
fn set_source_outcome<S: Source, O: Outcome>(&mut self, selector: fn(s: &S) -> &O) {
self.selector = Some(Box::new(selector));
}
}
Is there any way to do so?
playground: here

ok, looks somewhat like but still works playground
a more straightforward solution is most welcome

Related

Expecting and getting different types

I am trying to learn rust following this https://github.com/dhole/rust-homework/tree/master/hw03, which follows this https://rust-unofficial.github.io/too-many-lists/second-option.html, and when I try to do this:
type Link<T> = Option<Box<Node<T>>>;
struct Node<T> {
elem: T,
left: Link<T>,
right: Link<T>,
}
pub struct BST<T: std::cmp::PartialOrd> {
root: Link<T>,
}
impl<T: std::cmp::PartialOrd> BST<T> {
pub fn new() -> Self {
BST { root: None }
}
pub fn insert(&mut self, elem: T) -> bool {
self.root.insert(elem)
}
}
trait InsertSearch<T: std::cmp::PartialOrd> {
fn insert(&mut self, elem: T) -> bool;
}
impl<T: std::cmp::PartialOrd> InsertSearch<T> for Link<T> {
fn insert(&mut self, elem: T) -> bool {
true
}
}
I get the following 2 erros:
error[E0308]: mismatched types
--> src\second.rs:35:34
|
23 | impl<T: std::cmp::PartialOrd> BST<T> {
| - this type parameter
...
35 | self.root.insert(elem)
| ^^^^ expected struct `Box`, found type parameter `T`
|
= note: expected struct `Box<second::Node<T>>`
found type parameter `T`
Why is it expecting a Box, when I am calling Option<Box<Node<T>>>::insert(T)?
error[E0308]: mismatched types
--> src\second.rs:35:17
|
28 | pub fn insert(&mut self, elem: T) -> bool {
| ---- expected `bool` because of return type
...
35 | self.root.insert(elem)
| ^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found mutable reference
|
= note: expected type `bool`
found mutable reference `&mut Box<second::Node<T>>`
And this one really confuses me. Why is it getting &mut Box<second::Node<T>>, when the return type of the insert function is bool? What am I calling then?
Both errors have the same reason: you're not calling the insert() function you defined. You're calling another function.
Remember Link is actually Option<_> (Option<Box<Node<T>>>, to be precise). And Option has an insert() method. And it takes T (not your T, the Option's T, which is Box<Node<T>> in this case) and produces &mut T (&mut Box<Node<T>>). And when you call some method, you always call an inherent method if one exists; only if there is no inherent method with that name you call a trait method, but in this case there is.
The solution can be to use Universal Function Call Syntax - <Link<T> as InsertSearch<T>>::insert(&mut self.root, elem), but I would recommend just making Link a newtype struct instead of a type alias. That is,
struct Link<T>(Box<Node<T>>);
This way, you can define an inherent insert() method, and you don't inherit Option's methods.

Why does `|_| 1` not meet lifetime requirements

I have the following trait and generic implementation for Fn:
trait Provider<'a> {
type Out;
fn get(&'a self, state: &State) -> Self::Out;
}
impl<'a, F, T> Provider<'a> for F
where
F: Fn(&State) -> T,
{
type Out = T;
fn get(&'a self, state: &State) -> T {
self(state)
}
}
Now, I have some code that wants a for<'a> Provider<'a, Out = usize>. However, the most simple closure, |_| 1, does not qualify and instead provides this error message which I don't understand:
fn assert_usize_provider<P>(_: P)
where
P: for<'a> Provider<'a, Out = usize>,
{
}
fn main() {
assert_usize_provider(|_| 1);
}
error[E0308]: mismatched types
--> src/main.rs:27:5
|
27 | assert_usize_provider(|_| 1);
| ^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
= note: expected type `FnOnce<(&State,)>`
found type `FnOnce<(&State,)>`
note: this closure does not fulfill the lifetime requirements
--> src/main.rs:27:27
|
27 | assert_usize_provider(|_| 1);
| ^^^^^
note: the lifetime requirement is introduced here
--> src/main.rs:22:29
|
22 | P: for<'a> Provider<'a, Out = usize>,
| ^^^^^^^^^^^
Playground link
Can someone explain what that error message means and how to get this code working?
I don't know why inference does not work in this case but you can add type annotation to get the code working.
assert_usize_provider(|_ : &State| 1);

Cast struct object to dynamic trait object

This is a simpler version of my code:
type Res = Result<(), Box<dyn std::error::Error + Send + Sync>>;
// defined before-hand
pub trait Attr {
fn method(&self) -> Res;
}
// a struct, a block of code provided by user
// which is unknown by far
pub struct S;
// the trait implemented for provided struct
// user does this manually
// which is unknown by far
impl Attr for S {
fn method(&self) -> Res {
Ok(())
}
}
// in order to use the struct and its implemented method
// a possible way may be cast struct type to dynamic trait object
type Item = dyn Fn(&dyn Attr) -> Res;
pub fn use_method(f: &Item) {}
fn main() {
// casting happens here
// and do this job in macro, here is simplified version
let m = &(<S as Attr>::method) as &Item;
use_method(m);
}
It is required to tackle a struct, a block of code and its implementation both of which are unknown and provided by user. A rough direction may be using a trait as dynamic trait bounds which converted from struct object, but the compiler complain:
error[E0631]: type mismatch in function arguments
--> src/main.rs:30:13
|
4 | fn method(&self) -> Res;
| ------------------------ found signature of `for<'r> fn(&'r S) -> _`
...
30 | let m = &(<S as Attr>::method) as &Item;
| ^^^^^^^^^^^^^^^^^^^^^^ expected signature of `for<'r> fn(&'r (dyn Attr + 'r)) -> _`
|
= note: required for the cast to the object type `dyn for<'r> Fn(&'r (dyn Attr + 'r)) -> std::result::Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>`
How can I get it done? Is there any other way?

Type of a function pointer to method with lifetime parameters

I have a struct with a lifetime and some methods:
struct Ctrl<'a> {
x: &'a i32,
}
impl<'a> Ctrl<'a> {
fn foo(&self) -> i32 {
*self.x + 1
}
fn bar(&self) -> i32 {
*self.x + 2
}
}
Now I want to store pointers to the methods in a slice, kind of like a look-up table:
const LIST: &[_] = &[Ctrl::foo, Ctrl::bar];
Rust demands to know the slice element type and suggests for<'r> fn(&'r Ctrl) -> i32, but this results in an error:
error[E0308]: mismatched types
--> main.rs:16:48
|
16 | const LIST: &[for<'r> fn(&'r Ctrl) -> i32] = &[Ctrl::foo, Ctrl::bar];
| ^^^^^^^^^ one type is more general than the other
|
= note: expected fn pointer `for<'s, 'r> fn(&'r Ctrl<'s>) -> _`
found fn pointer `for<'r> fn(&'r Ctrl<'_>) -> _`
Is there a way to specify the correct type?
The problem is that foo is a method of Ctrl<'x> for some specific lifetime 'x, and not a method generic over all Ctrl<'_>. If you try to make all the lifetimes explicit, you will find that some of them can't be expressed:
const LIST: &[for<'a, 'b> fn(&'b Ctrl<'a>) -> i32] = &[Ctrl::<'?>::foo, Ctrl::<'?>::bar)];
// What lifetimes? ^^^^ ^^^^
Since foo isn't generic, but is bound to a specific Ctrl<'_>, there's no way to express the concept you need. You might try something like <for<'a> Ctrl<'a>>::foo, but that's not legal in current Rust.
One way to fix this would be to wrap each method in a closure that can be coerced to a function pointer:
const LIST: &[fn(&Ctrl<'_>) -> i32] = &[|this| this.foo(), |this| this.bar()];
This might get a little verbose if you have a lot of methods or if the signatures are more complex. Another option is to wrap each method in a free function with the correct signature. You could write a declarative macro to make it easier:
/// Wrap `Ctrl::$method` in a free function and return it.
macro_rules! ctrl {
($method:ident) => {{
fn inner(this: &Ctrl<'_>) -> i32 {
this.$method()
}
inner
}};
}
const LIST: &[fn(&Ctrl<'_>) -> i32] = &[ctrl!(foo), ctrl!(bar)];

Storing a closure with lifetimes in a struct

I'm trying to store closures in a Vec that is part of a struct. The closure is a factory function which receives 2 references as arguments and produces a trait object which stores the references the closure receives as arguments.
Because of that, the produced trait object has a lifetime that must not exceed the lifetime of the references. Also component_registrations will be accessed from multiple threads and is therefore wrapped in an Arc<Mutex>.
I tried implementing it but the compiler says that the generic parameter F of the register_component function doesn't satisfy the trait bound used in component_registrations.
This is the relevant part of the code:
use std::sync::Mutex;
use std::sync::Arc;
pub mod gl {
pub struct Gl();
}
pub struct ComponentRegistry<'a> {
gl: &'a gl::Gl
}
pub trait Component<'a> {
}
pub struct Application {
component_registrations: Arc<Mutex<Vec<Box<for<'b> Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> Box<Component<'b>> + Send + 'static>>>>
}
impl Application {
pub fn new() -> Application {
Application {
component_registrations: Arc::new(Mutex::new(vec![]))
}
}
pub fn register_component<'a, F>(&mut self, register: F) where F: Fn(&'a ComponentRegistry<'a>, &'a gl::Gl) -> Box<Component<'a>> + Send + 'static {
self.component_registrations.lock().unwrap().push(Box::new(register));
}
}
error[E0277]: the trait bound `for<'b> F: std::ops::Fn<(&'b ComponentRegistry<'b>, &'b gl::Gl)>` is not satisfied
--> src/main.rs:27:59
|
27 | self.component_registrations.lock().unwrap().push(Box::new(register));
| ^^^^^^^^^^^^^^^^^^ the trait `for<'b> std::ops::Fn<(&'b ComponentRegistry<'b>, &'b gl::Gl)>` is not implemented for `F`
|
= help: consider adding a `where for<'b> F: std::ops::Fn<(&'b ComponentRegistry<'b>, &'b gl::Gl)>` bound
= note: required for the cast to the object type `for<'b> std::ops::Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> std::boxed::Box<Component<'b>> + std::marker::Send`
error[E0271]: type mismatch resolving `for<'b> <F as std::ops::FnOnce<(&'b ComponentRegistry<'b>, &'b gl::Gl)>>::Output == std::boxed::Box<Component<'b>>`
--> src/main.rs:27:59
|
27 | self.component_registrations.lock().unwrap().push(Box::new(register));
| ^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter 'b, found concrete lifetime
|
note: concrete lifetime that was found is the lifetime 'a as defined on the method body at 26:5
--> src/main.rs:26:5
|
26 | / pub fn register_component<'a, F>(&mut self, register: F) where F: Fn(&'a ComponentRegistry<'a>, &'a gl::Gl) -> Box<Component<'a>> + Send + 'static {
27 | | self.component_registrations.lock().unwrap().push(Box::new(register));
28 | | }
| |_____^
= note: required for the cast to the object type `for<'b> std::ops::Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> std::boxed::Box<Component<'b>> + std::marker::Send`
If you use a higher ranked lifetime when you define your component_registrations struct field, you should use a higher ranked lifetime for F as well.
Also, if you say Box<Component<'b>>, it really means Box<Component<'b> + 'static> (so the trait object can contain only owned data). What you really need is Box<Component<'b> + 'b>, which means it is a trait object that implements Component<'b> and it can also contain borrowed data which live at least as long as 'b.
The relevant part is
pub struct Application {
component_registrations: Vec<Box<for<'b> Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> Box<Component<'b> + 'b> + Send + 'static>>
}
impl Application {
pub fn register_component<F>(&mut self, register: F) where F: for<'b> Fn(&'b ComponentRegistry<'b>, &'b gl::Gl) -> Box<Component<'b> + 'b> + Send + 'static {
self.component_registrations.push(Box::new(register));
}
}
You can see the full example. Note, that I removed the Arc and Mutex types from your example since they were not relevant.

Resources