-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mocks closures respect no lifetime constraints #38
Comments
Probably should enforce on closure passed to |
I can't find solution for this problem. I've wasted a lot of time and failed miserably. It boils down to casting functions, which both base (mocked function) and mock ultimately are.
The order on this chart is not random: function can always be casted to function below. This is because functions are contravariant regarding their inputs and covariant regarding outputs. So mock can be casted to base, base to evil and evil to cast. Lifetime casting is the biggest enemy, every function implicitly casts to any fn type lower on chart making distinguishing mock, base and evil impossible. Base mustn't be casted at all in order to check if proposed mock can be casted to it or not. If base is not casted, mock can be casted to base and evil can't. But if base gets casted to cast, both mock and evil can be casted to cast as well and evil can't be rejected. Whatever invariant wrappers base gets covered in, the casting happens BEFORE wrapping making everything work. It does not matter if base comes in form of fn item, fn pointer or Fn reference, it always gets casted, it in fact IS itself and all functions below. It's not possible to retrieve original base argument types either, Rust does not provide type system tools for it. Implementing some trait for function (generic fn pointer or generic Fn reference, it's not possible for specific fn item, which is a pity) does not help either, because once again function gets casted before trait is used on it. |
So the problem can't be solved in elastic, per-function way, but can it be solved with some general, stiff rules? Yes and no. What we need is to force mock to have inputs living shorter than any possible input required by base and output living longer than any possible base output. The output is trivial: just force it to be We need to enforce arguments to have lifetime of only the mock call duration, that's the only lifetime that is guaranteed to not outlive any possible argument of base. Unfortunately Rust does not have necessary tooling. GAT may or may not be able to provide them, but it still wouldn't be enough. In perfectly working system every I don't think that this problem can be solved in any reasonable type system. The only option there is left is to allow mocks to be safe only when they don't have or ignore arguments. Otherwise there is no way to prevent implicit lifetime escalation. |
This code compiles causing conversion of
&str
to&'static str
:The text was updated successfully, but these errors were encountered: