Open bjackman opened 2 weeks ago
After writing this and then trying it out (see example usage here) I realised that
a) Unless I'm mistaken this is an incompatible change because it added a new lifetime parameter to Builder
.
b) Because the Builder
is not capable of owning its prefix
/suffix
/parent_dir
params, passing the builder objects around is kinda unwieldy anyway (you'll see that my lazy approach in the linked example was just to leak them so I can set the lifetime params to 'static
).
But, I'm pretty new to Rust, so I thought I'd send this PR despite these problems, in case it turns out to be useful anyway e.g. because I'm misunderstanding these issues.
Ah, I missed your comment.
a) Unless I'm mistaken this is an incompatible change because it added a new lifetime parameter to Builder.
Yes.
b) Because the Builder is not capable of owning its prefix/suffix/parent_dir params, passing the builder objects around is kinda unwieldy anyway (you'll see that my lazy approach in the linked example was just to leak them so I can set the lifetime params to 'static).
Also yes. It's possible to pass it around, but a bit awkward. It would be possible to erase the lifetime entirely by making the builder own the prefix etc.... but I'm trying to reduce allocations a bit. The builder is designed to be an ephemeral way to construct temporary files/directories.
Thanks for the input :) So I think this feature is probably not worth it as-is, I think anyone who would benefit from it is pretty likely gonna want to end up creating their own wrapper type anyway which is what I ended up going for in my case.
Maybe if it was a part of a broader evolution it might make sense though. It's unclear to me how feasible it is to create something like the builder but which is generic wrt whether it owns its params or just has a reference. My understanding is that Borrow
is the "I don't care if it's a reference" type but presumably it's not really intended that you store Borrows
(surely the borrow checker isn't able to reason about "this field might or might not be a reference with a lifetime I need to check"?).
Do you think I'm on the right lines there? In that case I think I should abandon this effort. (BTW also I'm mostly just shooting the breeze at this point, if you're too busy to help random strangers understand Rust API design principles please feel free to ignore me!)
Learned something interesting, I ran out of time to fully explore it so I'll just drop some notes...
It's unclear to me how feasible it is to create something like the builder but which is generic wrt whether it owns its params or just has a reference.
I think Cow
might be one way to do this? I found an example of this in a random web search. But, I'm not sure if it's possible to take advantage of it here. I naïvely thought we'd be able to have something like:
pub struct Builder<'a, 'b> {
// ...
prefix: Cow<'a, OsStr>,
suffix: Cow<'b OsStr>,
// ...
}
impl<'a, 'b> Builder<'a, 'b> {
pub fn new() -> Self {
Self::default()
}
pub fn owned_prefix(&mut self, prefix: OsString) -> &mut Builder<'static, 'b> {
self.prefix = Cow::Owned(prefix);
self
}
}
But this turns out to fall afoul of some pretty hardcore type system business:
1 error: lifetime may not live long enough
--> src/lib.rs:290:9
|
197 | impl<'a, 'b> Builder<'a, 'b> {
| -- lifetime `'a` defined here
...
290 | self
| ^^^^ returning this value requires that `'a` must outlive `'static`
|
= note: requirement occurs because of a mutable reference to `Builder<'_, '_>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: could not compile `tempfile` (lib test) due to 1 previous error
But I think the hardcore stuff about variance is probably a red herring and this is just me expecting the borrow checker to do impossible magic, if you have prefix_owned(mut self, prefix: OsString) -> Builder<'static, 'b>
you still get a similar error:
1 error: lifetime may not live long enough
--> src/lib.rs:290:9
|
197 | impl<'a, 'b> Builder<'a, 'b> {
| -- lifetime `'a` defined here
...
290 | self
| ^^^^ returning this value requires that `'a` must outlive `'static`
error: could not compile `tempfile` (lib test) due to 1 previous error
I think I need to meditate at the top of a mountain and understand that error then I'll be able to understand if there's actually a solution here or not.
I played with that and got it working on my local "eventual v4 branch" but... it's not worth it, IMO. What I did was:
to_static
method returning a Builder
with a static lifetime.Unfortunately, that static builder has all kind of thorns because, e.g., calling .prefix(...)
on it would require a prefix with a static lifetime.
IMO, the right approach is to pass around a closure or a custom temporary file creator.
(although I may consider in an eventual v4)
I have a usecase where it's useful to be able to pass
Builder
s around and have them remember the parent directory to create files in, just like they remember the filename prefix and suffix.So, add a new
parent_dir
API to do just that.Note that we still call env::temp_dir even when the result won't be used. Using
self.dir.unwrap_or_else(|| temp_dir())
doesn't work becausetempdir_in
doesn't accept an ownedPathBuf
. If anyone prefers, we can easily get rid of this pointless call by just having an explicitmatch
instead ofunwrap_or
, but it's a bit more verbose. No strong feelings either way from my side.