Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rust struct can borrow "&'a mut self" twice, so why can't a trait?

Tags:

The following Rust code compiles successfully:

struct StructNothing;  impl<'a> StructNothing {     fn nothing(&'a mut self) -> () {}      fn twice_nothing(&'a mut self) -> () {         self.nothing();         self.nothing();     } } 

However, if we try to package it in a trait, it fails:

pub trait TraitNothing<'a> {     fn nothing(&'a mut self) -> () {}      fn twice_nothing(&'a mut self) -> () {         self.nothing();         self.nothing();     } } 

This gives us:

error[E0499]: cannot borrow `*self` as mutable more than once at a time  --> src/lib.rs:6:9   | 1 | pub trait TraitNothing<'a> {   |                        -- lifetime `'a` defined here ... 5 |         self.nothing();   |         --------------   |         |   |         first mutable borrow occurs here   |         argument requires that `*self` is borrowed for `'a` 6 |         self.nothing();   |         ^^^^ second mutable borrow occurs here 
  • Why is the first version allowed, but the second version forbidden?
  • Is there any way to convince the compiler that the second version is OK?

Background and motivation

Libraries like rust-csv would like to support streaming, zero-copy parsing because it's 25 to 50 times faster than allocating memory (according to benchmarks). But Rust's built-in Iterator trait can't be used for this, because there's no way to implement collect(). The goal is to define a StreamingIterator trait which can be shared by rust-csv and several similar libraries, but every attempt to implement it so far has run into the problem above.

like image 784
emk Avatar asked Oct 04 '14 11:10

emk


People also ask

Can Rust be borrowed?

To accomplish this, Rust uses a borrowing mechanism. Instead of passing objects by value ( T ), objects can be passed by reference ( &T ). The compiler statically guarantees (via its borrow checker) that references always point to valid objects.

How does the borrow checker work Rust?

The borrow check is Rust's "secret sauce" – it is tasked with enforcing a number of properties: That all variables are initialized before they are used. That you can't move the same value twice. That you can't move a value while it is borrowed.

What is borrowing in Rust and what does it accomplish?

It is very inconvenient to pass the ownership of a variable to another function and then return the ownership. Rust supports a concept, borrowing, where the ownership of a value is transferred temporarily to an entity and then returned to the original owner entity.

What is a struct Rust?

A struct is a user-defined data type that contains fields which define its particular instance. Structs help programmers implement abstract ideas in a more understandable fashion. For example, creating a Student struct that consists of an id , name , marks , etc. makes the code more readable.


1 Answers

The following is an extension of Francis's answer using implicit lifetimes but it allows for the return value to be lifetime bound:

pub trait TraitNothing<'a> {     fn change_it(&mut self);      fn nothing(&mut self) -> &Self {         self.change_it();         self     }      fn bounded_nothing(&'a mut self) -> &'a Self {         self.nothing()     }      fn twice_nothing(&'a mut self) -> &'a Self {         // uncomment to show old fail         // self.bounded_nothing();         // self.bounded_nothing()         self.nothing();         self.nothing()     } } 

It's less than perfect, but you can call the methods with implicit lifetimes change_it and nothing multiple times within other methods. I don't know if this will solve your real problem because ultimately self has the generic type &mut Self in the trait methods whereas in the struct it has type &mut StructNothing and the compiler can't guarantee that Self doesn't contain a reference. This workaround does solve the code example.

like image 68
housleyjk Avatar answered Oct 13 '22 14:10

housleyjk