Write a string to file doing O_EXCL


#1

Hi,

I want to write a string to a file and it should use create_new for making sure there’s no process race condition.

And I’m new to Rust, and I don’t understand whether write_all exists or not. Has anyone time to help me here?

This is how far I came:

use std::fs::OpenOptions;
use std::fs::File;

fn tofile(
	path: String,
	value: String)
{
	let file = OpenOptions::new().write(true).create_new(true).open(path);	
	file.write_all(value.as_bytes());
}

fn main()
{
	tofile( "test.txt".to_string(),
		"blubb".to_string());
}

#2

write_all is a member of the std::io::Write trait: https://doc.rust-lang.org/stable/std/io/trait.Write.html#method.write_all

That means, in order to call it, you must use std::io::Write. Alternatively, you could do what the example does and require the std::io prelude, use std::io::prelude::*;, which would bring Write and several other useful traits into scope.


#3

Thanks!

I’ve included all the use lines without success. My problem seems to be that OpenOptions::new().write(true).create_new(true).open(path); returns a type
that has the file handle and the error included. And I was - sorry for this - too sedentary to figure this out.

If you had by chance a link or a minimum example of an asignment with the right side being std::result::Result<std::fs::File, std::io::Error>?

But I clearly need to learn myself the syntax at first, reading the books.


#4

You can express your tofile function like so:

use std::io::prelude::*;

fn tofile(path: String, value: String)
{
OpenOptions::new()
            .write(true)
            .create_new(true)
            .open(path)
            .and_then(|mut f| f.write_all(value.as_bytes()))
            .expect("Failed")
}

You’d want to report a failure (i.e. error) in real code, most likely, rather than panic like the above (via the expect call).

In your original code, to get the File, you’d just chain an unwrap call, like so:

let mut file = OpenOptions::new().write(true).create_new(true).open(path).unwrap();

#5

Thank you so much. :relaxed:


#6

Though, I must say, the syntax is weird as can be. It’s surely just coincidence, but nothing of “what my eyes read” reflects what it probably should do (in particular the order of words) when putting a string into a file, plus some lambda looking thing, and the as_bytes (but that’s ok).

The “true” arguments are nameless things which without Intellisense are … well anonymous things to look up in the docs, for now.

I know it would be a awkward syntax would be a phenomenon in Rust, but I can’t tell what it does. I’m not afraid (anymore) to say I might be too dumb for this language. All I actually would like (if I had 3 free wishes) is the ownership features, not much beside that. Can’t help it, it’s… a very special syntax. I might be fighting with the compiler for months or years before I make progress.


#7

The fields are named by the method. It’s called the “builder pattern”. When you see .write(true) in the builder chain, that’s equivalent to (..., write=true, ...) in a language with keyword arguments. Nothing quite as bad as create_thingy(true, true, false, 0, false, NULL, 0ULL) in some interfaces I’ve seen (*cough* WinAPI *cough*).


#8

Thanks! This makes it much clearer of course.


#9

Well, yeah, there are a few ways to skin this cat (and more still) :slight_smile:. If you’re just learning Rust (in fact, I’m pretty new to it myself), I’d stick to the more “clear cut” approaches, which is just adding “unwrap” to your original code as mentioned (+ making the file mut and using the prelude) - it looks fairly benign then.

The lambda/closure “functional” approach can make the code more concise in some cases (and also can avoid some lexical borrow issues), but yes, it can be terse when you’re not familiar with the APIs (and Rust itself). I’d stick with it, Rust is known for its learning curve (the steepness of which, depending on your background, will vary).