Dynamically select a function to call without intermediate variables - rust

I'm trying to select a function to call depending on a condition. I want to store that function in a variable so that I can call it again later without carrying the condition around. Here's a working minimal example:
fn foo() {
println! ("Foo");
}
fn bar() {
println! ("Bar");
}
fn main() {
let selector = 0;
let foo: &Fn() = &foo;
let bar: &Fn() = &bar;
let test = match selector {
0 => foo,
_ => bar
};
test();
}
My question is: is it possible to get rid of the intermediate variables? I've tried simply removing them:
fn foo() {
println! ("Foo");
}
fn bar() {
println! ("Bar");
}
fn main() {
let selector = 0;
let test = match selector {
0 => &foo as &Fn(),
_ => &bar as &Fn()
};
test();
}
but then the borrow checker complains that the borrowed values are only valid until the end of the match (btw, why? the functions are 'static anyway so should be valid to the end of times). I've also tried making the 'static lifetime explicit by using &foo as &'static Fn() but that doesn't work either.

The following works, if you only need to work with static functions and not closures:
fn foo() {
println!("Foo");
}
fn bar() {
println!("Bar");
}
fn main() {
let selector = 0;
let test: fn() = match selector {
0 => foo,
_ => bar
};
test();
}
(try on playground)
Here I've used function type instead of function trait.
The reason that the borrowed trait object doesn't work is probably the following. Any trait object is a fat pointer which consists of a pointer to some value and a pointer to a virtual table. When the trait object is created out of a closure, everything is clear - the value would be represented by the closure itself (internally being an instance of a structure containing all captured variables) and the virtual table would contain a pointer to the implementation of the corresponding Fn*() trait generated by the compiler whose body would be the closure body.
With functions, however, things are not so clear. There are no value to create a trait object from because the function itself should correspond to the implementation of Fn() trait. Therefore, rustc probably generates an empty structure and implements Fn() for it, and this implementation calls the static function directly (not actual Rust, but something close):
struct SomeGeneratedStructFoo;
impl Fn<()> for SomeGeneratedStructFoo {
type Output = ();
fn call(&self, args: ()) -> () {
foo();
}
}
Therefore, when a trait object is created out of fn foo(), a reference is taken in fact to a temporary value of type SomeGeneratedStructFoo. However, this value is created inside the match, and only a reference to it is returned from the match, thus this value does not live long enough, and that's what the error is about.

fn() is a function pointer type. It's already a pointer type. You can check this with std::mem::size_of::<fn()>(). It is not a zero-sized type.
When you do &foo, you take a pointer to a stack allocated pointer. This inner pointer does not survive very long, causing the error.
You can cast these to the generic fn() type as suggested. I would be interested in knowing why you can't cast fn() to &Fn(), though.

Related

Why doesn't this closure outlive var?

Consider the following Rust code:
use std::thread;
fn main() {
bar();
loop {}
}
fn bar() {
let var = b"foo";
thread::spawn(|| {
write(var);
});
}
fn write(d: &[u8]) {
println!("{:?}", d)
}
To my understanding, var is on the stack of function bar, which no longer exists after it returns.
Still, the new thread accesses it afterwards and successfully writes the data.
Why does this work?
Why doesn't the Rust compiler complain?
To my understanding, var is on the stack of function bar, which no longer exists after it returns.
var is just a reference with type &'static [u8; 3]. This reference value is what is on the stack, not the string literal.
The owner of the byte string literal b"foo" is the program binary, which results in the string literal having a 'static lifetime because it exists for the entire lifetime of the running program.
The b"foo" value doesn't live on the stack actually. It is stored in the read-only memory of the compiled binary and has a 'static lifetime.
Consider this alternative example:
fn bar() {
let var = format!("foo");
thread::spawn(|| {
write(&var);
});
}
fn write(d: &str) {
println!("{:?}", d)
}
That won't work (unless you add move before the closure), because var (of type String) is allocated on the stack.

Rust Double Reference Value

Looking through the blurz bluetooth library for Rust.
There is a variable declared with a value equal to a reference of a temp value(?).
This value is then passed into another function by reference.
How is ownership handled for a variable set to the value of a reference in a single statement and what does it mean to then use that reference as a reference?
Example:
let bt_session = &Session::create_session(None)?;
let adapter: Adapter = Adapter::init(bt_session)?;
adapter.set_powered(true)?;
let session = DiscoverySession::create_session(
&bt_session,
adapter.get_id()
)?;
See variable bt_session.
Source code example link.
A commented example to illustrate some concepts:
use rand;
#[derive(Debug)]
struct Struct {
id: i32
}
impl Drop for Struct {
fn drop(&mut self) {
// print when struct is dropped
println!("dropped {:?}", self);
}
}
fn rand_struct() -> Struct {
Struct { id: rand::random() }
}
/*
this does not compile:
fn rand_struct_ref<'a>() -> &'a Struct {
&rand_struct()
// struct dropped here so we can't return it to caller's scope
}
*/
fn takes_struct_ref(s: &Struct) {}
fn main() {
// brings struct into scope and immediately creates ref to it
// this is okay because the struct is not dropped until end of this scope
let s = &rand_struct();
println!("holding ref to {:?}", s);
// all these work because of deref coercion
takes_struct_ref(s);
takes_struct_ref(&s);
takes_struct_ref(&&s);
// struct dropped here
}
playground
To answer your first question: You can't return a reference from a function if the underlying value is dropped at the end of the function but it's okay to immediately make a reference to a returned value since that value lives for the rest of the caller's scope. You can see this in action if you run the above example, as holding ref to Struct { id: <id> } will get printed before dropped Struct { id: <id> }.
To answer your second question: the reason you can pass an &Struct or an &&Struct or an &&&Struct and so on to function which only takes an &Struct is because of a Rust syntax sugar feature called deref coercion which automatically derefs variables when they are used as function arguments or as part of method calls. In your shared code examples it looks like a ref of a ref is being passed to the function but actually it's just passing a single ref after auto deref coercion takes place.
See also
Why is it legal to borrow a temporary?
What are Rust's exact auto-dereferencing rules?

Implicit conversion to static lifetime in anyhow::Error

I was reading up code for anyhow crate in Rust. There is a particular line I don't fully grasp:
{
let vtable = &ErrorVTable { ... };
construct(vtable, ...);
}
fn construct(vtable: &'static ErrorVTable, ...);
We seem to create an ErrorVTable struct, and return a reference to it which has lifetime `static. I'd expect compiler to create a struct on function stack, and return a reference to it, causing weird memory issues.
But it appears like compiler detects that this variable for all possible E inferred on compile time and somehow creates static variables for them? How does this actually work?
Consider this simplified code:
struct Foo {
x: i32
}
fn test(_: &'static Foo) {}
fn main() {
let f = Foo{ x: 42 };
test(&f);
}
As expected, it does not compile, with the message:
f does not live long enough
However this slightly variation does compile:
fn main() {
let f = &Foo{ x: 42 };
test(f);
}
The difference is that in the former, the Foo object is local, with a local lifetime, so no 'static reference can be built. But in the latter, the actual object is a static constant so it has static lifetime, and f is just a reference to it.
To help see the difference, consider this other equivalent code:
const F: Foo = Foo{ x: 42 };
fn main() {
test(&F);
}
Or if you use an actual constant literal:
fn test_2(_: &'static i32) {}
fn main() {
let i = &42;
test_2(&i);
}
Naturally, this only works if all the arguments of the Foo construction are constant. If any value is not constant, then the compiler will silently switch to a local temporary instead of a static constant and you will lose the 'static lifetime.
The precise rules for this constant promotion, as it is sometimes called, are a bit complicated and may be extended in newer compiler versions.

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 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