I created two structs, Car and CarModifier. Both Car and CarModifier are instantiated in main.rs. CarModifier modifies one of the fields of Car using data from curl request. Here is the code:
main.rs
use sample::{car::Car, car_modifier::CarModifier};
fn main() {
println!("Hello, world!");
let mut car = Car::new();
let mut car_modifier = CarModifier::new();
car_modifier.update_car(&mut car);
println!("{:?}", car.driver);
}
Car.rs
#[derive(Debug)]
pub struct Car<'a> {
pub driver: &'a [u8]
}
impl<'a> Car<'a> {
pub fn new() -> Self {
Self {
driver: &[26]
}
}
pub fn update_driver(&mut self, new_driver: &'a [u8]) {
self.driver = new_driver;
}
}
CarModifier.rs
use curl::easy::Easy;
use crate::{car::Car};
pub struct CarModifier;
impl CarModifier {
pub fn new() -> Self {
Self {
}
}
pub fn update_car(&mut self, car: &mut Car) {
let mut easy = Easy::new();
easy.url("https://www.rust-lang.org/").unwrap();
let mut transfer = easy.transfer();
transfer.write_function(|data: &[u8]| {
car.update_driver(data);
Ok(data.len())
}).unwrap();
transfer.perform().unwrap();
}
}
This is the error which I get when trying to run
error[E0521]: borrowed data escapes outside of closure
--> src/car_modifier.rs:19:13
|
13 | pub fn update_car(&mut self, car: &mut Car) {
| --- `car` declared here, outside of the closure body
...
18 | transfer.write_function(|data: &[u8]| {
| ---- `data` is a reference that is only valid in the closure body
19 | car.update_driver(data);
| ^^^^^^^^^^^^^^^^^^^^^^^ `data` escapes the closure body here
For more information about this error, try `rustc --explain E0521`.
error: could not compile `sample` due to previous error
I seem to understand from the error that data lives only in the closure body and the code tries to refer that in car.driver which will outlive the closure body, hence the error. Is this the right understanding?
I am also aware that instead of using &[u8], I could use Vec<u8> to resolve the error. But how can I keep using &[u8]?
Basically data belongs to the curl library. It may be a slice of some library internal buffer pool, and may be re-used for some other curl api calls later on, or freed after this call. You need to copy data into the Car structure instead of just having a pointer to the underlying buffer. Rust compiler helps you to catch that problem. In C++ the compiler won't even tell you this error and the C++ code will have runtime problems.
Related
I'm using this struct:
pub struct Store {
pub player: HashMap<i64, Player>,
pub team: HashMap<i64, Team>,
}
impl Store {
pub fn new() -> Arc<Self> {
Arc::new(Self {
player: HashMap::new(),
team: HashMap::new(),
})
}
}
sharing it in my resolvers with:
async fn player_by_id(&self, store: Arc<Store>, id: i64) -> Result<()> {
let team = get_team();
store.team.insert(id, team.into()); // here I get the error
}
I get the error:
error[E0596]: cannot borrow data in an `Arc` as mutable
|
84 | store.team.insert(id, team.into());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<Store>`
Why?
As explained in Rc's documentation,
Shared references in Rust disallow mutation by default, and Rc is no exception: you cannot generally obtain a mutable reference to something inside an Rc.
Arc is the same as Rc in this regard. You need to use a Mutex or RwLock
(or atomics) to change its contents.
I want to simulate some natural process, so I have a Simulator, and a reactor like a NuclearReactor. The simulator will modify the reactor, and the reactor can reversely influance the simulator by modifying it. One important thing is that the NuclearReactor is wrapped from somewhere else, the solid_function must has a inmutable &self.
So after reading rust book of RefCell, I wrote something like these:
use std::borrow::BorrowMut;
use std::cell::RefCell;
use std::rc::{Rc, Weak};
pub struct Simulator {
nr: NuclearReactor,
data: Vec<f64>,
}
impl Simulator {
pub fn on_nuclear_data(&mut self, x: i64) {
// modify self
}
pub fn run_simulation(&mut self) {}
}
pub struct NuclearReactor {
simulator: Option<Weak<RefCell<Simulator>>>,
}
impl NuclearReactor {
pub fn solid_function(&self, x: i64) {
/*
this function `&self` is solid, so I have to use a RefCell to wrap Simulator
*/
}
pub fn write_simulator(&self) {
/*
none of the two following snippets will work
*/
/* snippet1: compiler says:
error[E0507]: cannot move out of an `Rc`
--> src/main.rs:87:17
|
87 | let t = *self.simulator.unwrap().upgrade().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| move occurs because value has type `RefCell<Simulator>`, which does not implement the `Copy` trait
| help: consider borrowing here: `&*self.simulator.unwrap().upgrade().unwrap()`
*/
let t = *self.simulator.unwrap().upgrade().unwrap();
t.borrow_mut().on_nuclear_data(0);
/*
snippet2: compiler says:
error[E0599]: no method named `on_nuclear_data` found for mutable reference `&mut Rc<RefCell<Simulator>>` in the current scope
--> src/main.rs:101:65
|
101 | self.simulator.unwrap().upgrade().unwrap().borrow_mut().on_nuclear_data(0);
| ^^^^^^^^^^^^^^^ method not found in `&mut Rc<RefCell<Simulator>>`
*/
// self.simulator.unwrap().upgrade().unwrap().borrow_mut().on_nuclear_data(0);
}
}
pub fn main() {
let nr_ = NuclearReactor {
simulator: None
};
let mut sm_ = Rc::new(RefCell::new(Simulator {
nr: nr_,
data: vec![],
}));
(*sm_).borrow_mut().nr.simulator = Some(Rc::downgrade(&sm_));
}
But it won't compile. How should I solve it.
Oh... the compile hints me solved it. But it seems complicated. Can anyone explain that, or give a better pattern? I think the pattern is common.
let t = &*self.simulator.as_ref().unwrap().upgrade().unwrap();
t.borrow_mut().on_nuclear_data(0);
Actually it should be:
(*self.simulator.as_ref().unwrap().upgrade().unwrap()).borrow_mut().on_nuclear_data(0);
This is because &Option<T> cannot be unwrapped.
self.simulator, where self is a &Self, gets a &Option<Weak<RefCell<Simulator>>>.
Option<T>::unwrap consumes the self and return the inner value by 'move'.
Option<T>::as_ref converts &self into an Option<&T> safely so that you can unwrap into a &T.
Say I have a struct whose implementation writes somewhere, i.e. to something that implements the std::io::Write trait. However, I don't want the struct to own this. The following code works:
fn main() {
let mut out = std::io::stdout();
let mut foo = Foo::new(&mut out);
foo.print_number(2);
}
struct Foo<'a> {
out: &'a mut dyn std::io::Write
}
impl<'a> Foo<'a> {
pub fn new(out: &'a mut dyn std::io::Write) -> Self {
Self {
out
}
}
pub fn print_number(&mut self, i: isize) {
writeln!(self.out, "The number is {}", i).unwrap()
}
}
But, now this writing functionality should be made optional. I thought this sounds easy enough, but now the following doesn't compile:
fn main() {
let mut out = std::io::stdout();
let mut foo = Foo::new(Some(&mut out));
foo.print_number(2);
}
struct Foo<'a> {
out: Option<&'a mut dyn std::io::Write>
}
impl<'a> Foo<'a> {
pub fn new(out: Option<&'a mut dyn std::io::Write>) -> Self {
Self {
out
}
}
pub fn print_number(&mut self, i: isize) {
if self.out.is_some() {
writeln!(self.out.unwrap(), "The number is {}", i).unwrap()
}
}
}
because of:
error[E0507]: cannot move out of `self.out` which is behind a mutable reference
--> src/main.rs:20:26
|
20 | writeln!(self.out.unwrap(), "The number is {}", i).unwrap()
| ^^^^^^^^
| |
| move occurs because `self.out` has type `Option<&mut dyn std::io::Write>`, which does not implement the `Copy` trait
| help: consider borrowing the `Option`'s content: `self.out.as_ref()`
which I'm not sure how to interpret.
I tried following the suggestion by changing the line in question to:
writeln!(self.out.as_ref().unwrap(), "The number is {}", i).unwrap()
but then I get
error[E0596]: cannot borrow data in a `&` reference as mutable
--> src/main.rs:20:26
|
20 | writeln!(self.out.as_ref().unwrap(), "The number is {}", i).unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
I'm really not sure how to interpret these error messages and surprisingly I'm not really getting anywhere by just sprinkling &s and muts in random places without really understanding!
(As an aside, I'm not sure if this is a "good" way of going about this anyway? I'm open to completely different approaches of solving this problem, which is basically to optionally pass something to write to into a struct, but without the struct owning it. I read about the Box type which might also be relevant?)
As you already know, based on you already using &mut for out. The issue with using as_ref() is that it returns an immutable reference. Instead you need to use as_mut().
pub fn print_number(&mut self, i: isize) {
if self.out.is_some() {
writeln!(self.out.as_mut().unwrap(), "The number is {}", i).unwrap()
}
}
Alternatively, you can also simplify this and express it more idiomatically using if let:
pub fn print_number(&mut self, i: isize) {
if let Some(out) = &mut self.out {
writeln!(out, "The number is {}", i).unwrap()
}
}
I would also suggest that instead of unwrapping, that you return the io::Result and let the caller handle any potential error.
pub fn print_number(&mut self, i: isize) -> std::io::Result<()> {
if let Some(out) = &mut self.out {
writeln!(out, "The number is {}", i)?;
}
Ok(())
}
You can also simplify your paths, e.g. std::io::Write and std::io::Result<()>, by importing them with a use declaration, e.g. use std::io::{self, Write}; and then changing them to Write and io::Result<()>.
I am learning Rust. For my first program, I wrote this code to maintain data about a partial ordering:
use std::collections::{HashMap, HashSet};
struct Node {
num_before: usize,
successors: HashSet<String>,
}
impl Node {
fn new() -> Node {
Node {
num_before: 0,
successors: HashSet::new(),
}
}
}
pub struct PartialOrdering {
node: HashMap<String, Node>,
}
impl PartialOrdering {
pub fn new() -> PartialOrdering {
PartialOrdering {
node: HashMap::new(),
}
}
pub fn get_node(&mut self, name: &String) -> &mut Node {
self.node.entry(name.clone()).or_insert_with(Node::new)
}
pub fn add_order(&mut self, before: &String, after: &String) {
let mut before_node = self.get_node(before);
if after != before {
let mut after_node = self.get_node(after);
if before_node.successors.insert(after.clone()) {
after_node.num_before += 1;
}
}
}
}
Compiling this code produces this error:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> main.rs:35:25
|
33 | let before_node = self.get_node(before);
| ---- first mutable borrow occurs here
34 | if after != before {
35 | let mut after_node = self.get_node(after);
| ^^^^ second mutable borrow occurs here
36 | if before_node.successors.insert(after.clone()) {
| ---------------------- first borrow later used here
Admittedly I am new to the Rust borrowing rules, but this problem has me stumped. Please tell me what I am doing wrong, and how can I fix it?
The problem is that in Rust it is forbidden to take more than one mutable reference (&mut) to an object at a time (see here for details). Your get_node() takes &mut self and uses it to obtain an &mut Node contained in self (where self is a PartialOrdering). This causes the mutable borrow of self to exist for as long as the value returned by get_node() exists, preventing other calls to get_node(). This means that you can't have before_node: &mut Node and after_node: &mut Node in the same scope, unfortunately.
I'm implementing middleware with Actix-web and having an issue with lifetime that I couldn't figure out.
extern crate actix_web;
use actix_web::actix::{Actor, Addr, Context, System};
use actix_web::middleware::Middleware;
use actix_web::{http, server, App, HttpRequest, Responder};
use std::collections::HashMap;
pub struct CacheActor {
caches: HashMap<String, String>,
}
impl CacheActor {
pub fn new() -> Self {
CacheActor {
caches: HashMap::new(),
}
}
}
impl Actor for CacheActor {
type Context = Context<Self>;
}
fn create_resource(req: HttpRequest, addr: &Addr<CacheActor>) -> impl Responder {
unimplemented!();
format!("Unimplemented")
}
fn list_resources(req: HttpRequest, addr: &Addr<CacheActor>) -> impl Responder {
unimplemented!();
format!("Unimplemented")
}
pub trait TusMiddlewareTrait {
fn with_tus(self, addr: &Addr<CacheActor>) -> App;
}
impl TusMiddlewareTrait for App {
fn with_tus(self, addr: &Addr<CacheActor>) -> App {
self.route("/files", http::Method::GET, |req| list_resources(req, addr))
.route("/files", http::Method::POST, |req| {
create_resource(req, addr)
})
}
}
fn main() {
let system = System::new("Example");
let cache_addr = CacheActor::new().start();
server::new(|| App::new().with_tus(&cache_addr))
.bind("127.0.0.1:8080")
.unwrap()
.run();
system.run();
}
The error that I get is the following,
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/tus/middleware.rs:84:49
|
84 | .route("/files", http::Method::GET, |req| list_resources(req, addr))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 81:5...
--> src/tus/middleware.rs:81:5
|
81 | / fn with_tus(self, addr: &actix::Addr<cache::CacheActor>) -> App {
82 | | self.middleware(TusMiddleware)
83 | | .route("/files", http::Method::OPTIONS, tus_information)
84 | | .route("/files", http::Method::GET, |req| list_resources(req, addr))
... |
87 | | })
88 | | }
| |_____^
= note: ...so that the types are compatible:
expected &&actix::address::Addr<tus::cache::CacheActor>
found &&actix::address::Addr<tus::cache::CacheActor>
= note: but, the lifetime must be valid for the static lifetime...
As for what I understand, I am passing cache_addr as a reference to with_tus function. Inside each closure in route, addr is also a reference.
I don't understand why the compiler said the lifetime cannot outlive the anonymous lifetime #1. From what I can tell is that cache_addr's lifetime still outlives the closure. The lifetime should cover up until system.run() line. Can someone enlighten me?
Edit:
I updated the code above to MCVE (at least to a point that it is simple enough to copy the whole code and run cargo build while still preserving the same error message). I can't run it on rust-playground. It doesn't support actix crate at this point. I tried reducing it further but it's giving me a different error. Sorry, I am pretty new to Rust.
My questions are twofold, one I like to understand what's the error telling me. Second, I like to know how to properly do this with actix thus why the sample code is dependent on actix.
Look at the App::route signature:
pub fn route<T, F, R>(self, path: &str, method: Method, f: F) -> App<S>
where
F: WithFactory<T, S, R>,
R: Responder + 'static,
T: FromRequest<S> + 'static,
F generic depends on T and R that in turn have 'static lifetime requirement.
Your closure captures an &Addr<CacheActor> that it is not valid for 'static lifetime and this generates the error.
A possibility that I see is to use the App "State", directly from the docs:
Application state is shared with all routes and resources within the same application. When using an http actor, state can be accessed with the HttpRequest::state() as read-only, but interior mutability with RefCell can be used to achieve state mutability. State is also available for route matching predicates and middlewares.
In this case should be something like:
extern crate actix_web;
use actix_web::actix::{Actor, Addr, Context, System};
use actix_web::{http, server, App, HttpRequest, HttpResponse, Result};
use std::collections::HashMap;
use actix_web::dev::Handler;
#[derive(Clone)]
pub struct CacheActor {
caches: HashMap<String, String>,
}
impl CacheActor {
pub fn new() -> Self {
CacheActor {
caches: HashMap::new(),
}
}
}
impl Actor for CacheActor {
type Context = Context<Self>;
}
impl<S> Handler<S> for CacheActor {
type Result = String;
fn handle(&self, _req: &HttpRequest<S>) -> Self::Result {
unimplemented!();
}
}
fn list_resources(req: &HttpRequest<Addr<CacheActor>>) -> Result<HttpResponse> {
Ok(HttpResponse::Found()
.header(http::header::LOCATION, format!("hello {}", req.path()))
.finish())
}
fn main() {
let system = System::new("Example");
server::new(|| {
let cache_addr = CacheActor::new().start();
App::with_state(cache_addr)
.resource("/world", |r| r.method(http::Method::GET).f(list_resources))
})
.bind("127.0.0.1:8080")
.unwrap()
.run();
system.run();
}