While @H2CO3 already provided a short solution involving some API from the Option
type, it’s important to know that Option
is just an enum type with the two variants Some(value)
or None
. The way of handling enums that always gets the job done is: use match
.
The other thing to note here is that you’re taking <F: AsRef<Path>>
, probably with the goal of producing a &Path
eventually. Your previous approach of first converting Option<F>
into F
and then later probably &F
into &Path
cannot possibly work, because you can in fact not create a value of type F
in case the option is None
.
But the solution isn’t too hard. If your Option<F>
is Some(value)
then you have a value: F
inside and can use AsRef
to get the str
, otherwise you can use your default &Path
.
The first/easiest approach with match
that might work is to just split up the whole function
match file_name {
Some(value) => {
let file_name_path = value.as_ref();
// use file_name_path
}
None => {
let file_name_path = Path::new("default-env.json");
// use file_name_path
}
}
Factoring out the // use file_name_path
so that it’s not duplicated isn’t 100% trivial, e.g. a first approach might be
let file_name_path;
match file_name {
Some(value) => {
file_name_path = value.as_ref();
}
None => {
file_name_path = Path::new("default-env.json");
}
}
// use file_name_path
which doesn’t work, or
let file_name_path = match file_name {
Some(value) => {
value.as_ref()
}
None => {
Path::new("default-env.json")
}
};
// use file_name_path
which doesn’t work either. Both can be fixed by also moving value
out of the scope of the match, e.g. like this
let value_in_outer_scope;
let file_name_path = match file_name {
Some(value) => {
value_in_outer_scope = value;
value_in_outer_scope.as_ref()
}
None => {
Path::new("default-env.json")
}
};
// use file_name_path
but it isn’t very pretty…
Also, this match
does move the Option<F>
, and thinking about it, you actually only need to borrow it, which leads to the solution that @Hyeonu already suggested below:
let file_name_path = match &file_name {
Some(value) => {
// value: &F
value.as_ref();
}
None => {
Path::new("default-env.json");
}
};
// use file_name_path
This approach would’ve also worked in the other version:
let file_name_path;
match &file_name {
Some(value) => {
file_name_path = value.as_ref();
}
None => {
file_name_path = Path::new("default-env.json");
}
};
// use file_name_path
The combinators in the solution by @H2CO3 work as follows:
It also borrows the option, so it starts with an &Option<F>
, which can be turned into Option<&F>
with Option::as_ref
.(i.e. the file_name.as_ref()
call). Then, the by-value combinators of Option
can be used.
As you noticed, unwrap_or
isn’t applicable yet since we’re unable to provide a value of type F
(or rather &F
now…), but map
helps. You can do file_name.as_ref().map(F::as_ref)
to get an Option<&Path>
. (file_name.as_ref().map(F::as_ref)
is the same as file_name.as_ref().map(|value| value.as_ref())
.
Then finally on the Option<&Path>
, you can use unwrap_or
, so there’s the solution:
let file_name_path = file_name
.as_ref()
.map(F::as_ref)
.unwrap_or(Path::new("default-env.json"));
// use file_name_path
Finally, F::as_ref
can also be written AsRef::as_ref
(both are shorthands for <F as AsRef>::as_ref
, and type inference can fill in the missing parts), and .map_or(default, function)
is a short-hand for / combination of .map(function).unwrap_or(default)
.