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

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.

Related

How do I filter a vector of an enum of different structs using a common field?

I found you can create a vector of different types of structs using an enum. When filtering the vector on a common field, such as id, the compiler doesn't know the type while iterating:
use chrono::{DateTime, Utc}; // 0.4.19
use serde::{Serialize, Deserialize}; // 1.0.126
#[derive(Deserialize, Debug, Serialize, Clone)]
pub enum TransactionsEnum {
TransactionOrderA(TransactionOrderA),
TransactionOrderB(TransactionOrderB),
}
#[derive(Deserialize, Debug, Serialize, Clone)]
pub struct TransactionOrderA {
pub id: i64,
pub accountID: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct TransactionOrderB {
pub id: i64,
pub time: DateTime<Utc>,
}
fn transactions_filter(
transactions_vector: Vec<TransactionsEnum>,
x: i64,
) -> Vec<TransactionsEnum> {
transactions_vector
.into_iter()
.filter(|e| e.id >= x)
.collect()
}
error[E0609]: no field `id` on type `&TransactionsEnum`
--> src/lib.rs:27:23
|
27 | .filter(|e| e.id >= x)
| ^^
Sharing a common value in all enum values and Is there a way to directly access a field value in an enum struct without pattern matching? indirectly answer my question, but the answer provided here helped me understand why a match statement is necessary.
Those are not a common field, they are completely unrelated fields. The fact that they share a name is, as far as the Rust compiler is concerned, an insignificant coincidence. You need to use pattern matching to get the field from either case
impl TransactionsEnum {
pub fn id(&self) -> i64 {
match self {
TransactionsEnum::TransactionOrderA(value) => value.id,
TransactionsEnum::TransactionOrderB(value) => value.id,
}
}
}
transactions_vector
.into_iter()
.filter(|e| e.id() >= x) // Note the parentheses since we're calling a function now
.collect()

Is it possible to unpack a tuple into method arguments?

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?

How do I convert a mutable reference to self into an immutable reference to be used as an argument for a method?

I have following code that can't be compiled:
struct A {
x: i32,
}
impl A {
fn add_assign(&mut self, other: &Self) {
self.x += other.x;
}
fn double(&mut self) {
self.add_assign(self);
}
}
The error is:
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/lib.rs:11:9
|
11 | self.add_assign(self);
| ^^^^^----------^----^
| | | |
| | | immutable borrow occurs here
| | immutable borrow later used by call
| mutable borrow occurs here
How to pass self as the argument of add_assign? I have tried &self, *self, &*self without success.
For the current version of the question
fn add_assign(&mut self, other: &Self)
Your request is impossible.
You cannot have a mutable reference and an immutable reference to the same value at the same time. This is a fundamental aspect of Rust.
Please re-read the rules of references.
See also:
Cannot borrow as mutable because it is also borrowed as immutable
For the first version of the question
fn add_assign(&mut self, other: Self)
Your request is impossible.
You need one instance of struct A to call the method on and another instance of A to pass as the argument. Your type does not implement Copy or Clone or provide any equivalent methods so there is no way to get a second instance.
Beyond that, there's no universal way to take a mutable reference to a value and get an owned value out of it.
See also:
Cannot move out of borrowed content / cannot move out of behind a shared reference
Workarounds
If you implement Copy or Clone, then you can get a second value from the original and then call either of your versions.
If you implemented Copy:
(other: Self)
self.add_assign(*self);
(other: &Self)
let other = *self;
self.add_assign(&other);
If only Clone:
(other: Self)
self.add_assign(self.clone());
(other: &Self)
self.add_assign(&self.clone());
You probably want to implement the AddAssign trait to provide syntax sugar. Assuming you've implemented Copy:
impl A {
fn double(&mut self) {
*self += *self;
}
}
impl std::ops::AddAssign<Self> for A {
fn add_assign(&mut self, other: Self) {
self.x += other.x;
}
}
Stargateur's comment may also be applicable, as i32 implements Copy:
impl A {
fn double(&mut self) {
*self += self.x;
}
}
impl std::ops::AddAssign<i32> for A {
fn add_assign(&mut self, other: i32) {
self.x += other;
}
}

Can traits be used on enum types?

I read through the trait documentation and found a neat definition for using traits on structs. Is it possible to use traits on enum types? I have seen answers that say no, but they are 3 years old and don't quite do what I'm trying to do.
I tried to do this:
#[derive(Debug, Copy, Clone)]
pub enum SceneType {
Cutscene,
Game,
Menu,
Pause,
Credits,
Exit,
}
//We want to guarantee every SceneType can be played statically
trait Playable {
fn play();
}
impl Playable for SceneType::Cutscene {
fn play() {}
}
error[E0573]: expected type, found variant `SceneType::Cutscene`
--> src/main.rs:16:19
|
16 | impl Playable for SceneType::Cutscene {
| ^^^^^^^^^^^^^^^^^^^
| |
| not a type
| help: you can try using the variant's enum: `SceneType`
I don't understand this error because the enum it references is in the same file. If I really can't use traits on enum variants, is there any way I can guarantee any enum trait must implement certain methods?
Can traits be used on enum types?
Yes. In fact, you already have multiple traits defined for your enum; the traits Debug, Copy and Clone:
#[derive(Debug, Copy, Clone)]
pub enum SceneType
The problem is that you aren't attempting to implement Playable for your enum, you are trying to implement it for one of the enum's variants. Enum variants are not types.
As the error message tells you:
help: you can try using the variant's enum: `SceneType`
impl Playable for SceneType {
fn play() {}
}
See also:
Can struct-like enums be used as types?
Is there a way to use existing structs as enum variants?
If you want to implement a trait for Playable (i.e. for all enum variants) then the answer is quite simply: Yes you can. And Shepmaster's answer details how to do that.
However, if you really only want one enum variant to be Playable and not the others, then Rust doesn't directly support that, but there's an idiom I've seen used to emulate it. Instead of
enum MyEnum {
A(i32, i32),
B(String),
}
you explicitly implement each enum variant as a separate struct, so
enum MyEnum {
A(A),
B(B),
}
struct A {
x: i32,
y: i32,
}
struct B {
name: String,
}
And then you can impl Playable for A without impl Playable for B. Whenever you want to call it, pattern match the MyEnum and, if you get an A, you can call play in your example on the result of the pattern match.
I don't recommend using this pattern for every enum you write, as it does make the code a decent bit more verbose and requires some boilerplate constructor methods to make it palatable. But for complicated enums with a lot of options, this sort of pattern can make the code easier to reason about, especially if you have a lot of traits or functions that only really apply to a couple of the enum possibilities.
Edit: Truly apologize; this answer isn't about
every SceneType can be played statically
Old answer
Try generics:
#[derive(Debug, Copy, Clone)]
pub enum SceneType <Cutscene>
where
Cutscene: Playable
{
Cutscene(Cutscene),
Game,
Menu,
Pause,
Credits,
Exit,
}
//We want to guarantee every SceneType can be played statically
// Notice: add `pub` as enum
pub trait Playable {
fn play();
}
// create struct for inner of SceneType::Cutscene
struct Cutscene {
// ...
}
// impl to specific Cutscene
impl Playable for Cutscene {
fn play() {}
}
Test it:
fn main () {
let cutscene = Cutscene{};
let scenetype = SceneType::Cutscene(cutscene);
}
A downside I realized is that the generics are static. When there are more than one generics for an enum, all generics must be specified.
enum E <A, B>
where
A: SomeTrait1,
B: SomeTrait2,
{
Enum1(A),
Enum2(B),
}
trait SomeTrait1 {}
trait SomeTrait2 {}
struct S1 {}
impl SomeTrait1 for S1{}
struct S2 {}
impl SomeTrait2 for S2{}
struct X1 {}
impl SomeTrait1 for X1{}
fn main () {
// specify the generics
E::<S1, S2>::Enum1(S1{});
E::<X1, S2>::Enum1(X1{});
//error[E0282]: type annotations needed
// --> src/main.rs:26:5
// |
//33 | E::Enum1(S1{});
// | ^^^^^^^^ cannot infer type for type parameter `B` declared on the enum `E`
// E::Enum1(S1{});
// E::Enum1(X1{});
}

In a match, expected reference, found enum, error E0308 [duplicate]

It seems like every introductory document for Rust's enum types explains how to match on an enum object that you own, but what if you do not own the enum object and you just have a reference to it that you want to match against? I don't know what the syntax would be.
Here is some code where I attempt to match on a reference to an enum:
use std::fmt;
use std::io::prelude::*;
pub enum Animal {
Cat(String),
Dog,
}
impl fmt::Display for Animal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Animal::Cat(c) => f.write_str("c"),
Animal::Dog => f.write_str("d"),
}
}
}
fn main() {
let p: Animal = Animal::Cat("whiskers".to_owned());
println!("{}", p);
}
The Rust Playground gives errors on the first two cases of the match when trying to compile it:
error[E0308]: mismatched types
--> src/main.rs:12:13
|
12 | Animal::Cat(c) => f.write_str("c"),
| ^^^^^^^^^^^^^^ expected &Animal, found enum `Animal`
|
= note: expected type `&Animal`
= note: found type `Animal`
error[E0308]: mismatched types
--> src/main.rs:13:13
|
13 | Animal::Dog => f.write_str("d"),
| ^^^^^^^^^^^ expected &Animal, found enum `Animal`
|
= note: expected type `&Animal`
= note: found type `Animal`
How can I change that code to get it to compile? I tried adding ampersands in lots of different places without any luck. Is it even possible to match on a reference to an enum?
As of Rust 1.26, the idiomatic way is the way that you originally wrote it because match ergonomics have been improved:
use std::fmt;
pub enum Animal {
Cat(String),
Dog,
}
impl fmt::Display for Animal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Animal::Cat(_) => f.write_str("c"),
Animal::Dog => f.write_str("d"),
}
}
}
fn main() {
let p: Animal = Animal::Cat("whiskers".to_owned());
println!("{}", p);
}
Edit: Please see Shepmaster's answer for the latest idiom
The idiomatic way would be
match *self {
Animal::Cat(ref c) => f.write_str("c"),
Animal::Dog => f.write_str("d"),
}
You can use _ instead of ref c to silence the "unused" warning.
I figured it out thanks to helpful compiler messages:
match self {
&Animal::Cat(ref c) => f.write_str("c"),
&Animal::Dog => f.write_str("d"),
}

Resources