Is it possible to unpack a tuple into method arguments? - methods

Unpacking a tuple as arguments and calling a function with those values is covered by Is it possible to unpack a tuple into function arguments?, but is it possible to do the same trick on methods?
#![feature(fn_traits)]
struct Foo;
impl Foo {
fn method(&self, a: i32, b: i32) {
println!("{:?}, {:?}", a, b);
}
}
fn main() {
let foo = Foo;
let tuple = (10, 42);
// does not compile
//foo.method.call(tuple);
// nor this one
//std::ops::Fn::call(&foo.method, tuple);
}
For both I get the following error:
error[E0615]: attempted to take value of method `method` on type `Foo`
--> src/main.rs:20:9
|
20 | foo.method.call(tuple);
| ^^^^^^ help: use parentheses to call the method: `method(...)`
I do not control the method I call, so changing the signature to accept tuples is not an option.

Methods are functions that
Are associated with a type (called associated functions). Most people are familiar with "constructor" associated functions like new. These are referenced as Type::function_name.
Take some kind of Self as the first argument.
Thus you need to use Foo::method and provide a matching self:
#![feature(fn_traits)]
struct Foo;
impl Foo {
fn method(&self, a: i32, b: i32) {
println!("{:?}, {:?}", a, b);
}
}
fn main() {
let foo = Foo;
let tuple = (&foo, 10, 42);
std::ops::Fn::call(&Foo::method, tuple);
}
See also:
Fully-qualified syntax
What types are valid for the `self` parameter of a method?

Related

How can I pass a method as a function parameter? [duplicate]

For example,
struct Foo;
impl Foo {
fn bar(&self) {}
fn baz(&self) {}
}
fn main() {
let foo = Foo;
let callback = foo.bar;
}
error[E0615]: attempted to take value of method `bar` on type `Foo`
--> src/main.rs:10:24
|
10 | let callback = foo.bar;
| ^^^ help: use parentheses to call the method: `bar()`
With fully-qualified syntax, Foo::bar will work, yielding a fn(&Foo) -> () (similar to Python).
let callback = Foo::bar;
// called like
callback(&foo);
However, if you want it with the self variable already bound (as in, calling callback() will be the same as calling bar on the foo object), then you need to use an explicit closure:
let callback = || foo.bar();
// called like
callback();

Access the methods of primitive Rust types

How can I access the methods of primitive types in Rust?
Concretely, I want to pass either one of the two slice methods split_first_mut and split_last_mut to a function operating on slices. I know you can wrap them in closures as a workaround, but I’d like to know if direct access is possible.
You can access the methods on primitives just like regular types:
u8::to_le();
str::from_utf8();
<[_]>::split_first_mut();
You can create a function that accepts a slice ops function:
fn do_thing<T>(f: impl Fn(&mut [u8])) -> Option<(&mut T, &mut [T])>) {
// ...
}
And pass in both split_first_mut and split_last_mut:
fn main() {
do_thing(<[_]>::split_first_mut);
do_thing(<[_]>::split_last_mut);
}
You have to refer to the method using fully-qualified syntax. In a nutshell: <T>::{method_name} where T is the type and {method_name} is the name of the method. For example, if you're modifying a [i32] then you'd to prefix the method name with <[i32]>:: like this:
fn apply_fn<T, U>(t: T, t_fn: fn(T) -> U) -> U {
t_fn(t)
}
fn main() {
let mut items: Vec<i32> = vec![1, 2, 3];
let slice: &mut [i32] = items.as_mut_slice();
let first_split = apply_fn(slice, <[i32]>::split_first_mut);
let slice: &mut [i32] = items.as_mut_slice();
let last_split = apply_fn(slice, <[i32]>::split_last_mut);
}
playground

How to call a method through a function pointer? [duplicate]

This question already has an answer here:
Is there a way to create a function pointer to a method in Rust?
(1 answer)
Closed 3 years ago.
What is the correct syntax to call a method on an object using a function pointer?
struct Foo {
var: i32,
}
impl Foo {
fn method(&mut self, arg: i32) {
self.var = self.var + arg;
println!("var = {}", self.var);
}
}
fn main() {
let foo = Foo { var: 11 };
let func_ptr: Fn() = &foo.method;
(func_ptr).method(12);
}
I am getting this error:
error[E0615]: attempted to take value of method `method` on type `Foo`
--> src/main.rs:14:31
|
14 | let func_ptr: Fn() = &foo.method;
| ^^^^^^ help: use parentheses to call the method: `method(...)`
error[E0599]: no method named `method` found for type `dyn std::ops::Fn()` in the current scope
--> src/main.rs:15:16
|
15 | (func_ptr).method(12);
| ^^^^^^
|
= note: (func_ptr) is a function, perhaps you wish to call it
error[E0277]: the size for values of type `dyn std::ops::Fn()` cannot be known at compilation time
--> src/main.rs:14:9
|
14 | let func_ptr: Fn() = &foo.method;
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `dyn std::ops::Fn()`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
I guess I am not using correct type for func_ptr type; what is the correct type?
Methods do not bind the self argument, but you can get access to the methods via their fully qualified names and you can save them in a function pointer if you like. Later on, when you invoke the function pointer you have to provide the self argument yourself:
struct Foo {
var: i32,
}
impl Foo {
fn method(&mut self, value: i32) {
self.var += value;
println!("var = {}", self.var);
}
}
fn main() {
let mut foo = Foo { var: 11 };
let func_ptr = Foo::method;
func_ptr(&mut foo, 12);
}
If you'd like to manually define the type of this local variable then it would be the following:
let func_ptr: fn(&mut Foo, i32) = Foo::method;
If you'd like to use the trait notation, you have to use it behind a reference:
let func_ptr: &Fn(&mut Foo, i32) = &Foo::method;

"Expected type parameter, found reference to type parameter" when implementing a generic cache struct

In the Closures chapter of the second edition of The Rust Programming Language, the writer implements a Cache struct and leaves it with a few problems for the reader to fix up, such as:
Accepting generic parameters and return values on the closure function
Allowing more than one value to be cached
I've attempted to fix those problems but I am quite stuck and can't make it work.
use std::collections::HashMap;
use std::hash::Hash;
struct Cacher<T, X, Y>
where
T: Fn(&X) -> &Y,
X: Eq + Hash,
{
calculation: T,
results: HashMap<X, Y>,
}
impl<T, X, Y> Cacher<T, X, Y>
where
T: Fn(&X) -> &Y,
X: Eq + Hash,
{
fn new(calculation: T) -> Cacher<T, X, Y> {
Cacher {
calculation,
results: HashMap::new(),
}
}
fn value<'a>(&'a mut self, arg: &'a X) -> &'a Y {
match self.results.get(arg) {
Some(v) => v,
None => {
let res = (self.calculation)(arg);
self.results.insert(*arg, res);
res
}
}
}
}
Where T is the closure function type, X is the argument type and Y is the return value type.
The error I get:
error[E0308]: mismatched types
--> src/main.rs:30:43
|
30 | self.results.insert(*arg, res);
| ^^^ expected type parameter, found &Y
|
= note: expected type `Y`
found type `&Y`
I understand this, but I can't think of an elegant solution for the whole ordeal.
You've stated that your closure returns a reference:
T: Fn(&X) -> &Y,
but then you are trying to store something that isn't a reference:
results: HashMap<X, Y>,
This is fundamentally incompatible; you need to unify the types.
In many cases, there's no reason to have a reference to a generic type because a generic type can already be a reference. Additionally, forcing the closure to return a reference means that a closure like |_| 42 would not be valid. Because of that, I'd say you should return and store the value type.
Next you need to apply similar logic to value, as it needs to take the argument by value in order to store it. Additionally, remove all the lifetimes from it as elision does the right thing: fn value(&mut self, arg: X) -> &Y.
Once you've straightened that out, apply the knowledge from How to lookup from and insert into a HashMap efficiently?:
fn value(&mut self, arg: X) -> &Y {
match self.results.entry(arg) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
let res = (self.calculation)(e.key());
e.insert(res)
}
}
}
Round it off with some tests that assert it's only called once, and you are good to go. Note that we had to make decisions along the way, but they aren't the only ones we could have chosen. For example, we could have made it so that the cached value is cloned when returned.
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::Hash;
struct Cacher<F, I, O>
where
F: Fn(&I) -> O,
I: Eq + Hash,
{
calculation: F,
results: HashMap<I, O>,
}
impl<F, I, O> Cacher<F, I, O>
where
F: Fn(&I) -> O,
I: Eq + Hash,
{
fn new(calculation: F) -> Self {
Cacher {
calculation,
results: HashMap::new(),
}
}
fn value(&mut self, arg: I) -> &O {
match self.results.entry(arg) {
Entry::Occupied(e) => e.into_mut(),
Entry::Vacant(e) => {
let res = (self.calculation)(e.key());
e.insert(res)
}
}
}
}
#[test]
fn called_once() {
use std::sync::atomic::{AtomicUsize, Ordering};
let calls = AtomicUsize::new(0);
let mut c = Cacher::new(|&()| {
calls.fetch_add(1, Ordering::SeqCst);
()
});
c.value(());
c.value(());
c.value(());
assert_eq!(1, calls.load(Ordering::SeqCst));
}

What types are valid for the `self` parameter of a method?

I wanted to create a method that only works where the self parameter was an Rc. I saw that I could use Box, so I thought I might try to mimic how that works:
use std::rc::Rc;
use std::sync::Arc;
struct Bar;
impl Bar {
fn consuming(self) {}
fn reference(&self) {}
fn mutable_reference(&mut self) {}
fn boxed(self: Box<Bar>) {}
fn ref_count(self: Rc<Bar>) {}
fn atomic_ref_count(self: Arc<Bar>) {}
}
fn main() {}
Yields these errors:
error[E0308]: mismatched method receiver
--> a.rs:11:18
|
11 | fn ref_count(self: Rc<Bar>) {}
| ^^^^ expected struct `Bar`, found struct `std::rc::Rc`
|
= note: expected type `Bar`
= note: found type `std::rc::Rc<Bar>`
error[E0308]: mismatched method receiver
--> a.rs:12:25
|
12 | fn atomic_ref_count(self: Arc<Bar>) {}
| ^^^^ expected struct `Bar`, found struct `std::sync::Arc`
|
= note: expected type `Bar`
= note: found type `std::sync::Arc<Bar>`
This is with Rust 1.15.1.
Before Rust 1.33, there are only four valid method receivers:
struct Foo;
impl Foo {
fn by_val(self: Foo) {} // a.k.a. by_val(self)
fn by_ref(self: &Foo) {} // a.k.a. by_ref(&self)
fn by_mut_ref(self: &mut Foo) {} // a.k.a. by_mut_ref(&mut self)
fn by_box(self: Box<Foo>) {} // no short form
}
fn main() {}
Originally, Rust didn't have this explicit self form, only self, &self, &mut self and ~self (the old name for Box). This changed so that only by-value and by-references have the short-hand built-in syntax, since they are the common cases, and have very key language properties, while all smart pointers (including Box) require the explicit form.
As of Rust 1.33, some additional selected types are available for use as self:
Rc
Arc
Pin
This means that the original example now works:
use std::{rc::Rc, sync::Arc};
struct Bar;
impl Bar {
fn consuming(self) { println!("self") }
fn reference(&self) { println!("&self") }
fn mut_reference(&mut self) { println!("&mut self") }
fn boxed(self: Box<Bar>) { println!("Box") }
fn ref_count(self: Rc<Bar>) { println!("Rc") }
fn atomic_ref_count(self: Arc<Bar>) { println!("Arc") }
}
fn main() {
Bar.consuming();
Bar.reference();
Bar.mut_reference();
Box::new(Bar).boxed();
Rc::new(Bar).ref_count();
Arc::new(Bar).atomic_ref_count();
}
However, the impl handling hasn't yet been fully generalised to match the syntax, so user-created types still don't work. Progress on this is being made under the feature flag arbitrary_self_types and discussion is taking place in the tracking issue 44874.
(Something to look forward to!)
It's now possible to use arbitrary types for self, including Arc<Self>, but the feature is considered unstable and thus requires adding this crate attribute:
#![feature(arbitrary_self_types)]
Using feature crate attributes requires using nightly Rust.

Resources