I want to expose a public function with immutable self which calls a private function with mutable self.
struct Foo {
value: i32,
}
impl Foo {
fn f1(&self) {
self.f2(); // <--- is it possible to make self mutable?
}
fn f2(&mut self) {
self.value = 5;
}
}
fn main() {
let foo = Foo { value: 0 };
foo.f1();
}
When compiling this code I get an error
cannot borrow immutable borrowed content *self as mutable
Is it possible to make self mutable?
EDIT:
After missing that last sentence.. you could get away with wrapping the property in a Cell:
use std::cell::Cell;
struct Foo { value: Cell<i32> }
impl Foo {
fn f1(&self) {
self.f2();
}
fn f2(&self) {
self.value.set(5);
}
}
fn main() {
let foo = Foo { value: Cell::new(0) };
foo.f1();
println!("{:?}", foo.value.get()); // prints 5
}
What you want cannot be done: you cannot convert a non-mutable reference into a mutable one.
But you can get almost that with RefCell:
struct Foo { value: RefCell<i32> }
impl Foo {
fn f1(&self) {
let mutValue = self.value.borrow_mut();
*mutValue = 5;
}
}
I didn't even need the f2 function!
Related
I'm trying to learn rust making a game with SDL2. I have a struct GameEngine which has ownership of some variables to control the game state.
The method GameEngine::run() is responsible to manage the game loop. I want this method to do 2 things:
Check if some event is related to closing the game and in this case break the loop
For any other kind of event I want to call a method GameEngine::handle_event() to handle it
The problem is that the compiler is refusing to accept my code telling me I'm trying to borrow self as mutable more than once. The first borrow happen on this line:
let event_poll_iterator = self.event_pump.poll_iter();
and the second on this:
self.handle_event(event);
As I'm a newbie in Rust, I'm getting stuck in this error.
The complete code:
pub mod engine {
use std::time::Duration;
use sdl2::{EventPump, Sdl};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::render::WindowCanvas;
fn get_canvas(sdl_context: &Sdl) -> WindowCanvas {
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem
.window("SDL2 Snake Game", 800, 600)
.position_centered()
.opengl()
.build()
.map_err(|e| e.to_string()).unwrap();
let mut canvas = window.into_canvas().build().map_err(|e| e.to_string()).unwrap();
canvas.set_draw_color(Color::BLACK);
canvas
}
pub struct GameEngine {
context: Sdl,
event_pump: EventPump,
canvas: WindowCanvas,
}
impl GameEngine {
pub fn new() -> Self {
let context = sdl2::init().unwrap();
let canvas = get_canvas(&context);
let event_pump = context.event_pump().unwrap();
GameEngine { context, canvas, event_pump }
}
fn redraw(&mut self) {
self.canvas.clear();
self.canvas.present();
}
fn handle_event(&mut self, event: Event) {
todo!()
}
pub fn run(&mut self) {
'game_loop: loop {
let event_poll_iterator = self.event_pump.poll_iter();
for event in event_poll_iterator {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => break 'game_loop,
_ => {
self.handle_event(event);
}
}
}
self.redraw();
std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 30));
}
}
}
}
Edit
I could reproduce the same problem (I think) with a much smaller example:
struct List {
v: Vec<u32>
}
impl List {
fn increment(&mut self, x: &mut u32) {
*x += 1;
}
fn iter(&mut self) {
for x in &mut self.v {
self.increment(x);
}
}
}
fn main() {
let mut list = List { v: vec![1, 2, 3] };
list.iter();
assert!(list.v == vec![2, 3, 4]);
}
Error log:
λ cargo run
Compiling rustlings v4.7.1 (/home/luizalabs/repositories/rust/rustlings)
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/main.rs:12:13
|
11 | for x in &mut self.v {
| -----------
| |
| first mutable borrow occurs here
| first borrow later used here
12 | self.increment(x);
| ^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `rustlings` due to previous error
The problem is, self.handle_event could modify whatever you are iterating over (in this case event_poll_iterator). If you are modifying what the iterator is iterating over, you might want to consider cloning the iterator. If self.handle_event isn't modifying the iterator, you have to show the borrow checker that you are not modifying the iterator. One option is to inline self.handle_event. Another option is to pass whatever you are modifying as mutable to self.handle_event. Here is a simple example of what is going on:
#[derive(Debug)]
struct Point {
x: i32,
y: i32
}
struct Points {
points: Vec<Point>,
num_points: usize
}
impl Points {
fn bar(&mut self, point: &mut Point) {
println!("{:?}", point)
}
fn foo(&mut self) {
for p in &mut self.points {
self.bar(p);
}
}
}
Inlining would change foo as such:
fn foo(&mut self) {
for p in &mut self.points {
println!("{:?}", p); // modified line
}
}
Tried to simplify your example
fn main() {
let i: i32 = 4326;
let b = i.to_le_bytes();
for i in 0..4 {
println!("{}", b[i]);
}
}
pub struct GameEngine {
event_pump: EventPump,
}
pub struct EventPump {
}
impl EventPump {
pub fn poll_iter(&mut self) -> Vec<i32> {
vec![0, 1, 2]
}
}
impl GameEngine {
pub fn new() -> Self {
GameEngine {
event_pump: EventPump { },
}
}
fn redraw(&mut self) {
}
fn handle_event(&mut self, _event: i32) {
todo!()
}
pub fn run(&mut self) {
loop {
let ep = self.event_pump.poll_iter();
let event_poll_iterator = ep.iter();
for event in event_poll_iterator {
match event {
_ => {
self.handle_event(*event);
}
}
}
self.redraw();
}
}
}
Hope without losing sense. Seems it compiled ok.
It changes Iter to vector instance but I'm not sure if that matters.
struct Foo<'a>(&'a str);
impl<'a> Foo<'a> {
fn get(&self) -> &'static str {self.0}
}
fn main() {
let value: &str;
{
let foo = Foo("Test Value");
value = foo.get();
}
println!("{}", value);
}
In this test code, I need to get the value stored in foo but as str is not sized, I can't just copy it's value. I even tried cloning the str. In a real life scenario, I could just use String or make a wrapper or encapsulate it in a box and return it but is there no way I can return a static reference to a variable I create in a function? If so, how does the as_str function work in rust?
----Edit----
In this example, Neither putting 'static before str in Foo nor returning value with lifetime 'a works. What to do then?
struct Foo<'a>(&'a [i32]);
impl<'a> Foo<'a> {
fn get(&self) -> &'static [i32] {self.0}
}
fn main() {
let value: &[i32];
{
let test_value = [0, 1, 2, 3];
let foo = Foo(&test_value);
value = foo.get();
}
println!("{:?}", value);
}
You can do it like this:
struct Foo<'a>(&'a str);
impl<'a> Foo<'a> {
fn get(&self) -> &'a str {self.0}
}
fn main() {
let value: &str;
{
let foo = Foo("Test Value");
value = foo.get();
}
println!("{}", value);
}
Or like this:
struct Foo(&'static str);
impl Foo {
fn get(&self) -> &'static str {self.0}
}
fn main() {
let value: &str;
{
let foo = Foo("Test Value");
value = foo.get();
}
println!("{}", value);
}
The reason your code doesn't compile is that you're declaring you are returning a reference with a (potentially) longer lifetime. Compare the lifetime of static with that of a (self.0 has a lifetime of a).
I'm trying to recreate a simple callback pattern in Rust using 2 structs. One will pass a bit of logic to execute whenever the other one is ready. The issue here is that the logic will run only if a certain value from the struct is true.
I can understand why the reference to Foo needs to live for 'static in this case, but I'm not sure how to refactor so that it compiles.
Seems like a pretty regular use case, but maybe I'm missing something since I'm new to Rust.
struct Foo {
value: bool,
}
struct Bar {
closure: Box<dyn Fn() -> ()>,
}
impl Foo {
fn new() -> Self {
Foo {
value: false,
}
}
fn set_closure(&self, b: &mut Bar) {
b.closure = self.get_closure();
}
fn get_closure(&self) -> Box<dyn Fn() -> ()> {
return Box::new(|| {
if self.value {
println!("true");
} else {
println!("false");
}
});
}
}
impl Bar {
fn new() -> Self {
Bar {
closure: Box::new(|| {})
}
}
}
fn main() {
let foo = Foo::new();
let mut bar = Bar::new();
foo.set_closure(&mut bar);
let closure = bar.closure;
closure();
}
Playground link -> here
You need to move the value into the closure:
fn get_closure(&self) -> Box<dyn Fn() -> ()> {
let value = self.value;
Box::new(move || {
if value {
println!("true");
} else {
println!("false");
}
})
}
Notice that in your example value is a bool which is Copy. If not you can either capture a reference or clone it. (If capturing a reference you may need to adjust some lifetimes there).
Playground
This is my code
struct AA{
size:i8
}
impl AA{
pub fn create()->Self{
Self { size: 10 }
}
pub fn world(mut self)->Self{
self.size+=2;
self
}
pub fn say(self){
println!("{}",self.size);
}
}
//cannot move out of `*aa` which is behind a mutable reference
fn hello(aa: &mut AA){//this function will make a change
aa.world();// <-- need to call world() from here
}
fn main() {
let mut a=AA::create();
hello(&mut a);
a.say();// <-- need to call say() from here after hello() does the change
//AA::create().world().say(); <-- this works
}
How can I achieve the following in rust?
From main(),
create instance
pass instance to world() so that it can change the instance
after the change occurs, call say() of the instance from main
Would this alternative work for you?
struct AA {
size: i8,
}
impl AA {
pub fn create() -> Self {
Self { size: 10 }
}
pub fn world(mut self) -> Self {
self.size += 2;
self
}
pub fn say(self) {
println!("{}", self.size);
}
}
fn hello(aa: AA) -> AA {
aa.world()
}
fn main() {
let mut a = AA::create();
a = hello(a);
a.say();
}
Notice that world returns a Self type and hello also returns AA. This way instead of passing a mutable reference to a in hello() we consume it and return a new version of it.
Another alternative would be for both world and say to accept references to self instead of consuming them, in which case the snippet would be:
pub fn world(&self) -> Self {
AA {
size: self.size + 2,
}
}
pub fn say(&self) {
println!("{}", self.size);
}
I'm wrapping a C library, and it has a standard sort of context object:
library_context* context = library_create_context();
And then using that you can create more objects:
library_object* object = library_create_object(context);
And destroy them both:
library_destroy_object(object);
library_destroy_context(context);
So I've wrapped this up in Rust structs:
struct Context {
raw_context: *mut library_context,
}
impl Context {
fn new() -> Context {
Context {
raw_context: unsafe { library_create_context() },
}
}
fn create_object(&mut self) -> Object {
Object {
raw_object: unsafe { library_create_object(self.raw_context) },
}
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe {
library_context_destroy(self.raw_context);
}
}
}
struct Object {
raw_object: *mut library_object,
}
impl Drop for Object {
fn drop(&mut self) {
unsafe {
library_object_destroy(self.raw_object);
}
}
}
So now I can do this, and it seems to work:
fn main() {
let mut ctx = Context::new();
let ob = ctx.create_object();
}
However, I can also do this:
fn main() {
let mut ctx = Context::new();
let ob = ctx.create_object();
drop(ctx);
do_something_with(ob);
}
I.e. the library context is destroyed before the objects it creates are.
Can I somehow use Rust's lifetime system to prevent the above code from compiling?
Yes, just use normal lifetimes:
#[derive(Debug)]
struct Context(u8);
impl Context {
fn new() -> Context {
Context(0)
}
fn create_object(&mut self) -> Object {
Object {
context: self,
raw_object: 1,
}
}
}
#[derive(Debug)]
struct Object<'a> {
context: &'a Context,
raw_object: u8,
}
fn main() {
let mut ctx = Context::new();
let ob = ctx.create_object();
drop(ctx);
println!("{:?}", ob);
}
This will fail with
error[E0505]: cannot move out of `ctx` because it is borrowed
--> src/main.rs:26:10
|
25 | let ob = ctx.create_object();
| --- borrow of `ctx` occurs here
26 | drop(ctx);
| ^^^ move out of `ctx` occurs here
Sometimes people like to use PhantomData, but I'm not sure I see the benefit here:
fn create_object(&mut self) -> Object {
Object {
marker: PhantomData,
raw_object: 1,
}
}
#[derive(Debug)]
struct Object<'a> {
marker: PhantomData<&'a ()>,
raw_object: u8,
}