Not sure what I am missing here, the lifetime is declared, therefore the struct should use the path to create the file and return a Struct with the mutable File reference for me to be able to call "write" wrapper later...
use std::path::Path;
use std::fs::File;
// use std::io::Write;
#[derive(Debug)]
pub struct Foo<'a> {
file: &'a mut File,
}
impl<'a> Foo<'a> {
pub fn new(path: &'a Path) -> Result<Self, std::io::Error> {
let mut f: &'a File = &File::create(path)?;
Ok(Self { file: &mut f })
}
//pub fn write(&self, b: [u8]) {
// self.file.write(b);
//}
}
Error:
| impl<'a> Foo<'a> {
| -- lifetime `'a` defined here
11 | pub fn new(path: &'a Path) -> Result<Self, std::io::Error> {
12 | let mut f: &'a File = &File::create(path)?;
| -------- ^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'a`
...
15 | }
| - temporary value is freed at the end of this statement
As #E_net4 mentioned, I don't want a mutable reference, yet I want to own the value. Rather than trying to play with lifetimes, I can basically just own the file and handle the whole struct as mutable when trying to write to the file!
use std::path::{ PathBuf };
use std::fs::File;
use std::io::Write;
use std::env;
#[derive(Debug)]
pub struct Foo {
file: File,
}
impl Foo {
pub fn new(path: PathBuf) -> Self {
Self {
file: File::create(path).unwrap(),
}
}
pub fn write(&mut self, b: &[u8]) -> Result<usize, std::io::Error> {
self.file.write(b)
}
}
fn main() {
let mut tmp_dir = env::temp_dir();
tmp_dir.push("foo23");
let mut f = Foo::new(tmp_dir);
f.write(b"test2").unwrap();
}
Related
I am trying to make a simple game in Rust. Playing sound with rodio.
But when I run detach, I get an error:
error[E0507]: cannot move out of `self.sinks[_]` which is behind a mutable reference
--> src\sound.rs:48:3
|
48 | self.sinks[trk].detach();
| ^^^^^^^^^^^^^^^ -------- `self.sinks[_]` moved due to this method call
| |
| move occurs because `self.sinks[_]` has type `rodio::Sink`, which does not implement the `Copy` trait
|
note: this function takes ownership of the receiver `self`, which moves `self.sinks[_]`
--> C:\Users\dsinc\.cargo\registry\src\github.com-1ecc6299db9ec823\rodio-0.16.0\src\sink.rs:163:23
|
163 | pub fn detach(mut self) {
| ^^^^
For more information about this error.
I will put a sample of my code now:
use std::{io::BufReader, path::Path};
use rodio::Source;
pub struct Audio {
pub stream: rodio::OutputStream,
pub handle: rodio::OutputStreamHandle,
pub sinks: [rodio::Sink; 4],
}
impl Audio {
pub fn new() -> Audio {
let (stream, handle) = rodio::OutputStream::try_default().unwrap();
let sinks = [
rodio::Sink::try_new(&handle).unwrap(),
rodio::Sink::try_new(&handle).unwrap(),
rodio::Sink::try_new(&handle).unwrap(),
rodio::Sink::try_new(&handle).unwrap(),
];
Audio {
stream,
handle,
sinks,
}
}
pub fn load(
&mut self,
filename: &Path,
) -> rodio::source::Buffered<rodio::Decoder<BufReader<std::fs::File>>> {
let file = std::fs::File::open(filename).unwrap();
rodio::Decoder::new(BufReader::new(file))
.unwrap()
.buffered()
}
pub fn load_looped(
&mut self,
filename: &Path,
) -> rodio::source::Buffered<rodio::source::Repeat<rodio::Decoder<BufReader<std::fs::File>>>>
{
let file = std::fs::File::open(filename).unwrap();
rodio::Decoder::new(BufReader::new(file))
.unwrap()
.repeat_infinite()
.buffered()
}
pub fn play(
&mut self,
src: rodio::source::Buffered<rodio::Decoder<BufReader<std::fs::File>>>,
trk: usize,
) {
self.sinks[trk].append(src);
}
pub fn play_looped(
&mut self,
src: rodio::source::Buffered<
rodio::source::Repeat<rodio::Decoder<BufReader<std::fs::File>>>,
>,
trk: usize,
) {
self.sinks[trk].append(src);
}
pub fn stop(&mut self, trk: usize) -> &mut Self {
self.sinks[trk].detach(); // this gives me an error!
self
}
}
This file is a module, not main.
I'm having a hard time figuring out the lifetime syntax that I need for the following code, which does not compile. The basic idea is that I'm creating an Abstract Syntax Tree, and each node must have a different type. Some node types must hold a reference to an external value. It's not possible to make this external value an owned type; it's actually a reference to disk file that must be shared by a number of nodes. Here's the code, as simple as I can make it:
trait Node {
fn init(&mut self, my_str: &str);
}
struct NodeParent {
pub children: Vec<Box<dyn Node>>,
}
impl Node for NodeParent {
fn init(&mut self, my_str: &str) {
for child in self.children.iter_mut() {
child.init(my_str);
}
}
}
struct NodeLeaf<'a> {
pub my_str: Option<&'a str>,
}
impl Node for NodeLeaf<'_> {
fn init(&mut self, my_str: &str) {
self.my_str = Some(my_str);
}
}
impl NodeLeaf<'_> {
pub fn new() -> Box<dyn Node> {
Box::new(NodeLeaf { my_str: None })
}
}
pub fn make_ast() {
let mut parent = NodeParent { children: vec![] };
let leaf = NodeLeaf::new();
parent.children.push(leaf);
let some_string = String::from("foo");
let my_str = some_string.as_str();
parent.init(my_str);
}
The error is:
error: lifetime may not live long enough
--> src/query/lifetime_test.rs:23:9
|
22 | fn init(&mut self, my_str: &str) {
| --------- - let's call the lifetime of this reference `'1`
| |
| has type `&mut NodeLeaf<'2>`
23 | self.my_str = Some(my_str);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
I know I need some kind of lifetime on &my_str everywhere it appears, but once I start taking the compiler's suggestion to start adding lifetimes here and there they proliferate, and I never get to code that compiles. I've also tried to Rc<>, but the code to make that work eludes me as well.
How do I specify that &my_str outlives the whole Node tree?
Link to the code on Rust Playground
Add a lifetime to Node:
trait Node<'a> {
fn init(&mut self, my_str: &'a str);
}
struct NodeParent<'a> {
pub children: Vec<Box<dyn Node<'a>>>,
}
impl<'a> Node<'a> for NodeParent<'a> {
fn init(&mut self, my_str: &'a str) {
for child in self.children.iter_mut() {
child.init(my_str);
}
}
}
struct NodeLeaf<'a> {
pub my_str: Option<&'a str>,
}
impl<'a> Node<'a> for NodeLeaf<'a> {
fn init(&mut self, my_str: &'a str) {
self.my_str = Some(my_str);
}
}
impl<'a> NodeLeaf<'a> {
pub fn new() -> Box<dyn Node<'a>> {
Box::new(NodeLeaf { my_str: None })
}
}
But this is not enough, because Box<dyn Node<'a>> is actually Box<dyn Node<'a> + 'static>, that is, it cannot contain any non-'static lifetime. You need to specify that it can contain lifetime 'a:
struct NodeParent<'a> {
pub children: Vec<Box<dyn Node<'a> + 'a>>,
}
impl<'a> NodeLeaf<'a> {
pub fn new() -> Box<dyn Node<'a> + 'a> {
Box::new(NodeLeaf { my_str: None })
}
}
Now you will get a different error:
error[E0597]: `some_string` does not live long enough
--> src/lib.rs:39:18
|
39 | let my_str = some_string.as_str();
| ^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
40 | parent.init(my_str);
41 | }
| -
| |
| `some_string` dropped here while still borrowed
| borrow might be used here, when `parent` is dropped and runs the destructor for type `NodeParent<'_>`
|
= note: values in a scope are dropped in the opposite order they are defined
Because Box<dyn Node> may have an arbitrary destructor, and it may access the stored string, we need to make sure it is still alive when the value is dropped. Do that by moving the string creation before the nodes:
pub fn make_ast() {
let some_string = String::from("foo");
let mut parent = NodeParent { children: vec![] };
let leaf = NodeLeaf::new();
parent.children.push(leaf);
let my_str = some_string.as_str();
parent.init(my_str);
}
Playground.
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'm trying to implement an abstraction that allows me to read from either a directory or a zip file. I start by implementing something of this sort:
pub trait FileOpener<'a> {
type ReaderType: Read;
fn open(&'a self, file_name: &str) -> Result<Self::ReaderType, Box<dyn Error>>;
}
pub struct DirectoryFileOpener<'a> {
root: &'a Path
}
impl<'a> DirectoryFileOpener<'a> {
pub fn new(root: &'a Path) -> Self {
DirectoryFileOpener { root }
}
}
impl<'a> FileOpener<'a> for DirectoryFileOpener<'a> {
type ReaderType = File;
fn open(&'a self, file_name: &str) -> Result<File, Box<dyn Error>> {
Ok(File::open(self.root.join(file_name))?)
}
}
But then I realize that the zip-rs package's zip::ZipFile is constructed from a mutable reference to the zip::ZipArchive which it is located in, so I end up with the following code:
use std::path::Path;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use zip::{ZipArchive, read::ZipFile};
use std::marker::PhantomData;
pub trait FileOpener<'a> {
type ReaderType: Read;
fn open(&'a mut self, file_name: &str) -> Result<Self::ReaderType, Box<dyn Error>>;
}
pub struct DirectoryFileOpener<'a> {
root: &'a Path
}
impl<'a> DirectoryFileOpener<'a> {
pub fn new(root: &'a Path) -> Self {
DirectoryFileOpener { root }
}
}
impl<'a> FileOpener<'a> for DirectoryFileOpener<'a> {
type ReaderType = File;
fn open(&'a mut self, file_name: &str) -> Result<File, Box<dyn Error>> {
Ok(File::open(self.root.join(file_name))?)
}
}
pub struct ZipFileOpener<'a, R: Read + Seek> {
zip: ZipArchive<R>,
phantom: PhantomData<&'a Self>
}
impl<'a, R: Read + Seek> ZipFileOpener<'a, R> {
pub fn new(zip: ZipArchive<R>) -> Self {
ZipFileOpener { zip, phantom: PhantomData }
}
}
impl<'a, R: Read + Seek> FileOpener<'a> for ZipFileOpener<'a, R> {
type ReaderType = ZipFile<'a>;
fn open(&'a mut self, file_name: &str) -> Result<ZipFile<'a>, Box<dyn Error>> {
Ok(self.zip.by_name(file_name)?)
}
}
I'm not sure if that's the most optimal way to write that, but at least it compiles. Then I try to use it as such:
fn load(root: &Path) -> Result<...> {
let mut opener = io::DirectoryFileOpener::new(root);
let a = Self::parse_a(opener.open("a.txt")?)?;
let b = Self::parse_b(opener.open("b.txt")?, a)?;
}
and I get cannot borrow 'opener' as mutable more than once at a time. This does not surprise me much, as I indeed use open(), which borrows opener as mutable, twice - although a is only a u64, and from my point of view it is unrelated to the lifetime of opener.open(), from the compiler's point of view it has to be in the same lifetime of the line below it, and thus we attempt to borrow opener as mutable twice.
However, I then look at the following code, which compiles and works well and which I started this whole thing by trying to improve:
fn load_zip(root: &Path) -> Result<...> {
let file = File::open(root)?;
let mut zip = ZipArchive::new(file)?;
let a = Self::parse_a(zip.by_name("a.txt")?)?;
let b = Self::parse_b(zip.by_name("b.txt")?, a)?;
}
This throws me off completely, because the function by_name() also borrows zip as mutable, and is also called twice! Why is it allowed to borrow zip as mutable twice here but not in the previous case?
After researching the issue and Rust's semantics deeper, and building on top of the notes by trentcl, I came to realize that the problem essentially boils down to defining the FileOpener trait where the lifetime argument is bound to the associated type and not to the trait itself, e.g.
pub trait FileOpener {
type ReaderType: Read;
fn open(&'a mut self, file_name: &str) -> Result<Self::ReaderType, Box<dyn Error>>;
}
impl<'a, R: Read + Seek> FileOpener for ZipFileOpener<R> {
type ReaderType = ZipFile<'a>;
...
}
However, this is known as generic associated types (GAT), and is not yet supported in Rust. The GAT RFC does however mention that in some cases the problem can be circumvented by binding the lifetime to the trait itself and using higher-rank trait bounds (HRTB) in the receiving function, which yields the following working solution to this question:
pub trait FileOpener<'a> {
type ReaderType: Read;
fn open(&'a self, file_name: &str) -> Result<Self::ReaderType, Box<dyn Error>>;
}
...
fn load<T: for<'a> FileOpener<'a>>(opener: T) -> ... {
let a = parse_a(opener.open("a.txt")?)?;
let b = parse_b(opener.open("b.txt")?, a)?;
}
This is because the HRTB allows us to bind T to a FileOpener without binding a specific lifetime to it, which enables the late binding of different lifetimes for each call to opener.open()
I'm trying to rewrite my parser to allow for strings to be passed into the parse method, instead of being bound to the struct.
Previously, my code looked like this:
use std::collections::HashMap;
use std::str;
#[derive(Debug)]
pub enum ParserError {
Generic
}
pub struct Resource(
pub HashMap<String, String>
);
pub struct Parser<'a> {
source: str::Chars<'a>
}
impl<'a> Parser<'a> {
pub fn new(source: &str) -> Parser {
Parser { source: source.chars() }
}
pub fn parse(&mut self) -> Result<Resource, ParserError> {
let entries = HashMap::new();
Ok(Resource(entries))
}
}
fn main() {
let parser = Parser::new("key1 = Value 1");
let res = parser.parse();
}
and in my new code I'm trying something like this:
use std::collections::HashMap;
use std::str;
#[derive(Debug)]
pub enum ParserError {
Generic
}
pub struct Resource(
pub HashMap<String, String>
);
pub struct Parser<'a> {
source: Option<str::Chars<'a>>
}
impl<'a> Parser<'a> {
pub fn new() -> Parser<'a> {
Parser { source: None }
}
pub fn parse(&mut self, source: &str) -> Result<Resource, ParserError> {
self.source = Some(source.chars());
let entries = HashMap::new();
Ok(Resource(entries))
}
}
fn main() {
let parser = Parser::new();
parser.parse("key1 = Value 1");
parser.parse("key2 = Value 2");
}
but it seems like I'm messing with lifetimes in a way that I'm not fully comfortable with. The error I get is:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> test.rs:22:35
|
22 | self.source = Some(source.chars());
|
What's the canonical way of handling this? How can I take a String and clone it into the lifetime of the Parser struct?
The full error message is:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:22:35
|
22 | self.source = Some(source.chars());
| ^^^^^
|
help: consider using an explicit lifetime parameter as shown: fn parse(&mut self, source: &'a str) -> Result<Resource, ParserError>
--> src/main.rs:21:5
|
21 | pub fn parse(&mut self, source: &str) -> Result<Resource, ParserError> {
| ^
Doing as it suggests:
pub fn parse(&mut self, source: &'a str) -> Result<Resource, ParserError>
Allows the code to compile and run (after fixing the unrelated mismatched mutability in main).
To understand the difference, you must first understand lifetime elision.
Your original code was:
fn new(source: &str) -> Parser // with elision
fn new<'b>(source: &'b str) -> Parser<'b> // without elision
In words, the generic lifetime parameter 'a of the struct was tied to the lifetime of the incoming string.
Your new code was more complicated:
fn new() -> Parser<'b>
// with elision
fn parse(&mut self, source: &str) -> Result<Resource, ParserError>
// without elision
fn parse<'c, 'd>(&'c mut self, source: &'d str) -> Result<Resource, ParserError>
In words, the generic lifetime parameter 'a of the struct is still defined by the caller of new, but now it's not tied to anything from the constructor. When calling parse, you were attempting to pass in a string of an unrelated lifetime and store a reference to it (through the Chars iterator). Since the two lifetimes were unrelated, you cannot be sure it will last long enough.