Reputation: 23329
I have a library that used to have a top-level public trait (call it FlatMap
) implemented for Option
. Now I have refactored my library and moved that trait into a submodule named option
, and I have ensured backward compatibility by publicly importing FlatMap
into the top-level of the crate. I would like to warn users of the top-level trait that it is deprecated. I have tried the following:
pub mod option {
pub trait FlatMap<T> {}
}
#[deprecated(note="Use option::FlatMap instead")]
pub use option::FlatMap;
However this does not work: I can now use mylib::FlatMap
and use mylib::option::FlatMap
and either works fine with no warning. I would like the first use to generate a deprecation warning.
Note that I can't use an alias trait like this:
pub mod option {
pub trait FlatMap<T> {}
}
#[deprecated(note="Use option::FlatMap instead")]
pub trait FlatMap<T>: option::FlatMap<T> {}
impl<T> FlatMap<T> for Option<T> {}
because I rely on implicit conversion from Option
to option::FlatMap
to add a method to Option
instances, and the implicit conversion does not work with the aliased trait.
Upvotes: 3
Views: 357
Reputation: 27945
Being able to call option::FlatMap
methods on an Option
isn't a conversion, implicit or otherwise -- it's just how methods are resolved. Trait methods may be resolved on an object if the trait has been use
d, but the top level FlatMap
trait has no methods, so it doesn't add anything to the object at all.
It seems the best way to achieve this in Rust right now is to copy the trait (with all its contents) into the top level, annotate it #[deprecated]
, and write a blanket impl
that defers to the "real" version.
pub mod option {
pub trait FlatMap<T> {
type Item;
type FlatMap;
fn flat_map<F>(self, f: F) -> Self::FlatMap
where
F: FnOnce(Self::Item) -> T;
}
// ...
}
#[deprecated(note = "Use option::FlatMap instead")]
pub trait FlatMap<T> {
type Item;
type FlatMap;
fn flat_map<F>(self, f: F) -> Self::FlatMap
where
F: FnOnce(Self::Item) -> T;
}
#[allow(deprecated)]
impl<T, U> FlatMap<T> for U
where
U: option::FlatMap<T>,
{
type Item = U::Item;
type FlatMap = U::FlatMap;
fn flat_map<F>(self, f: F) -> Self::FlatMap
where
F: FnOnce(Self::Item) -> T,
{
option::FlatMap::flat_map(self, f)
}
}
Any attempt to use the "outer" FlatMap
will generate the deprecation warning (playground link).
One limitation of this approach (besides the verbosity) is that the two traits won't be compatible as trait objects (e.g., you couldn't pass an &FlatMap
to a function expecting &option::FlatMap
.) However, this trait isn't object safe anyway, so in this case it doesn't matter.
As it happens, your definition of flat_map
-- while a generalization of and_then
-- is a different generalization than several other languages use, including Rust's own Iterator::flat_map
. So you might want to consider renaming it. (I think map_or_default
would fit in well, name-wise, with the other methods available on Option
.)
Upvotes: 2