Why can't a boxed struct be borrowed as a trait? - rust

Given a struct S implementing a trait T, why doesn't Box<S> implement Borrow<dyn T>?
The following code, that I would have expected to compile, doesn't:
trait T{}
struct S{}
impl T for S{}
fn f1(s: &S) -> &dyn T {
s
}
fn f2(s: &Box<S>) -> &dyn T {
std::borrow::Borrow::borrow(s)
}
Why does f1 compile while f2 doesn't? (The conversion from &S to &dyn T is done in the first case and not in the second).

This is to do with the way that type inference and type coercion work. The Borrow<B> trait's parameter is the type of the borrowed value, and the type checker needs to know what it is.
If you just write:
std::borrow::Borrow::borrow(s)
Then the type B in Borrow<B> will be inferred from the surrounding code. In your case it is inferred to be dyn T because that's the return value. However, dyn T is a completely different type from S, so it doesn't type-check.
Once the type checker knows that the value being returned is of type &S then it can coerce it to a &dyn T, but you need to give it that information:
fn f2(s: &Box<S>) -> &dyn T {
let s: &S = std::borrow::Borrow::borrow(s);
s
}
Or, more concisely:
fn f2(s: &Box<S>) -> &dyn T {
std::borrow::Borrow::<S>::borrow(s)
}
The reason why Sébastien Renauld's answer works is because Deref uses an associated type instead of a type parameter. The type-checker can easily infer the <S as Deref>::Target because there can only be one implementation of Deref per type and the associated Target type is uniquely determined. Borrow is different because Box<S> could implement Borrow<()>, Borrow<i32>, Borrow<Box<Option<Vec<bool>>>>,... so you have to be more explicit about which implementation you intend.

&Box<S> is not directly equal to Box<&S>, and this is why it does not compile directly.
You can relatively easily fix this by dereferencing, like so:
use std::ops::Deref;
trait T{}
struct S{}
impl T for S{}
fn f1(s : &S) -> &(dyn T) {
s
}
fn f2(s : &Box<S>) -> &(dyn T) {
s.deref()
}
(The trait Deref is there for slightly easier readability)
The call to deref() operates over &self, so having &Box<S> is sufficient to call it. It simply returns &S, and since that implements T the types check out.

Related

`impl Trait` return type causes wrong lifetime elision

The impl Trait syntax for return types seems to cause the compiler to incorrectly assume that the lifetime of the input argument must match the output in some situations. Consider the function
fn take_by_trait<T: InTrait>(_: T) -> impl OutTrait {}
If the input type contains a lifetime the compiler complains when the output outlives the it even though they are completely independent. This will not happen if the input type is not generic, or if the output is a Box<dyn OutTrait>.
Full code:
trait InTrait {}
struct InStruct<'a> {
_x: &'a bool, // Just a field with some reference
}
impl<'a> InTrait for InStruct<'a> {}
trait OutTrait {}
impl OutTrait for () {}
fn take_by_type(_: InStruct) -> impl OutTrait {}
fn take_by_trait<T: InTrait>(_: T) -> impl OutTrait {}
fn take_by_trait_output_dyn<T: InTrait>(_: T) -> Box<dyn OutTrait> {
Box::new(())
}
fn main() {
let _ = {
let x = true;
take_by_trait(InStruct{ _x: &x }) // DOES NOT WORK
// take_by_type(InStruct { _x: &x }) // WORKS
// take_by_trait_output_dyn(InStruct { _x: &x }) // WORKS
};
}
Is there some elided lifetime here that I could qualify to make this work, or do I need to do heap allocation?
The impl Trait semantic means that the function returns some type that implements the Trait, but the caller cannot make any assumptions about which type that would be or what lifetimes it would use.
For all the compiler knows the take_by_trait function can be used in many different modules, or, probably, in other crates. Now, your implementation would work fine in all use cases. It can be rewritten like
fn take_by_trait<T: InTrait>(_: T) -> () {}
This is a perfectly fine function and will work just fine. But then at some point you might want to add another implementation for the OutTrait and change your take_by_trait function a little.
trait OutTrait { fn use_me(&self) {} }
impl<T: InTrait> OutTrait for T {}
fn take_by_trait<T: InTrait>(v: T) -> impl OutTrait {v}
If we expand the generic parameter and impl definition, we get this code:
fn take_by_trait<'a>(v: InStruct<'a>) -> InStruct<'a> {v}
fn main() {
let v = {
let x = true;
take_by_trait(InStruct{ _x: &x })
};
v.use_me();
}
This obviously cannot work because x is dropped before println! tries to access its value. So by adding a new implementation for the OutTrait you broke code that uses this function, potentially somewhere in a crate that depends on yours. That's why the compiler is reluctant to allow you defining such things.
So, again, the issue with impl OutTrait is just that the compiler cannot make any assumptions about the returned type and its lifetime, so it uses the maximum possible bound, which produces the borrow checker error you see.
EDIT: I've modified the code a little so that the signature of the function would not change and the code actually compiles and produces the same lifetime error: playground
impl Trait in return position implicitly captures the any lifetime appearing in generic parameters. This is not something you can express in normal Rust, only with impl Trait.
On stable as far as I know there is no way to avoid that. On nightly you can use type_alias_impl_trait:
#![feature(type_alias_impl_trait)]
type Ret = impl OutTrait;
fn take_by_trait<T: InTrait>(v: T) -> Ret {}
See issues Unclear compiler error when impl Trait return value captures non-'static argument (#82171), impl Trait capturing lifetime of type parameter (#79415), False-positive "temporary dropped while borrowed" involving return-position impl Trait (#98997), impl Trait + 'static is not static if returned from generic function (#76882).

How can i constrain a generic constraint using a different generic argument?

So i have a struct.
pub struct Foo<TFn, TArg, TReturn> where TFn: Fn(TArg) -> TReturn { func: TFn }
This makes sence in my head being used to C# Generics, but why doesn't it work in rust?
I want the field 'func' to be of type Fn where the argument is of type 'TArg' and the return value is of type 'TReturn'.
The compiler is complaining that the paramter 'TArg' and 'TReturn' are never used, but they are helping to define the signature of the TFn value.
I tried removing the 'never used' parameters and just writing in a type in the constraint explicitly. That works fine.
#Aleksander Krauze's answer is right, but doesn't take into account one niche concern: variance. Consider this:
fn x<'s>(&'s str) -> String { // ...
x is a function that can deal with any &str that lives for 's. In particular this means that x can deal with any &str that lives longer than 's, a &'static str for example. This is true because &'static str is a subtype of &'s str. In Rust, T being a subtype of U (written T: U) means wherever I can accept a U, I can also accept a T, because a subtype can do anything its supertype can do and more (live longer, for example).
Subtyping comes up most often for lifetimes in Rust, where lifetime 'a is a subtype of lifetime 'b if 'a outlives (i.e. is longer than) 'b. So if I can accept a &'b T, I can also accept a &'a T as long as 'a: 'b. In other words, &'a T is a subtype of &'b T if 'a is a subtype of 'b. This is called covariance and is an instance of what is called variance.
However, functions are interesting in this regard. If I have types fn(&'a T) and fn(&'b T), fn(&'a T) is a subtype of fn(&'b T) if 'b is a subtype of 'a not the other way around. I.e. if I need a function that can deal with long lifetimes, then a function that can deal with shorter lifetimes will also do, because any argument I can pass it will be a subtype of the argument it expects. This is called contravariance and is a property we very much want for our functions.
Your type Foo is more or less a function so we'd like it to behave like one and be contravariant over its argument. But it isn't. It's covariant. That's because a struct in Rust inherits its variance from its fields. The type PhantomData<(TArg, TReturn)> is covariant over TArg and TReturn (because the type (TArg, TReturn) is) and so Foo will be covariant over TArg. To get it to behave like a function should, we can just mark it with the appropriate type: PhantomData<fn(TArg) -> TReturn>. This will be contravariant over TArg and covariant over TReturn (functions are covariant over their return type; I hope that follows from the explanations above).
I've written a little example (albeit an artificial one) to demonstrate how incorrect variance can break code that should work:
use std::marker::PhantomData;
pub struct Foo<TFn, TArg, TReturn>
{
func: TFn,
// this makes `Foo` covariant over `TArg`
_marker: PhantomData<(TArg, TReturn)>
}
impl<TFn, TArg, TReturn> Bar<TFn, TArg, TReturn>
where
TFn: Fn(TArg) -> TReturn,
{
// presumably this is how one might use a `Foo`
fn call(&self, arg: TArg) -> TReturn {
(self.func)(arg)
}
}
// `foo_factory` will return a `Foo` that is covariant over the lifetime `'a`
// of its argument
fn foo_factory<'a>(_: &'a str) -> Foo<fn(&'a str) -> String, &'a str, String> {
// We only care about the type signatures here
panic!()
}
fn main() {
let long_lifetime: &'static str = "hello";
// we make a `Foo` that is covariant over the `'static` lifetime
let mut foo = foo_factory(long_lifetime);
foo.call("world");
{
let short_lifetime = String::from("world");
// and because it's covariant, it can't accept a shorter lifetime
// than `'static`
// even though this should be perfectly fine, it won't compile
foo = foo_factory(&short_lifetime);
}
foo.call("world");
}
But if we fix the variance:
pub struct Foo<TFn, TArg, TReturn> {
func: TFn,
// `Foo` is now _contravariant_ over `TArg` and covariant over `TReturn`
_marker: PhantomData<fn(TArg) -> TReturn>,
}
The main function from above will now compile just fine as one would expect.
For more on variance in Rust and how it relates to data structures and the drop check, I recommend checking out the 'nomicon chapter on it and the one on PhantomData.
In rust your struct must use all of the generic types that it is generic over. And use mean, that they must appear in at least one type of the field. You can solve your problem with special type PhantomData. It is a marker type that is used to provide additional information to the compiler and is removed at compile time. The docs give you even example of how to use it to solve "unused type parameters" error. TLDR is here:
use std::marker::PhantomData;
pub struct Foo<TFn, TArg, TReturn>
where
TFn: Fn(TArg) -> TReturn
{
func: TFn,
_marker: PhantomData<(TArg, TRetur)> // This line tells the compiler that
// this struct should act like it owned
// type (Targ, TReturn)
}
And when you want to create an instance of your struct just put PhantomData as a value:
let s = Foo { func: f, _marker: PhantomData };

How to Downcast a Supertrait to a SubTrait in Rust

I am currently trying to implement a trait from a library (in my case serde), which expects a trait to be returned (see example function next), but i only have a supertrait available, but i do not know how to downcast a supertrait to a subtrait.
Example Code:
/// Imagine this type as a trait from a library, which i cannot change
trait SubTrait {
fn get_bool() -> bool;
}
trait SuperTrait: SubTrait {
fn get_string() -> String;
}
/// Imagine this type as a trait from a library, which i cannot change
trait GenericTrait {
fn next<T>(&mut self) -> Result<Option<T>, std::io::Error>
where
T: SubTrait;
}
struct SomeOtherStruct<'d, V: SuperTrait> {
field1: V,
_marker: std::marker::PhantomData<&'d ()>,
}
impl<'d, V> SomeOtherStruct<'d, V>
where
V: SuperTrait,
{
pub fn new(field: V) -> Self {
return Self {
field1: field,
_marker: std::marker::PhantomData,
};
}
pub fn do_something(self) -> Result<Option<V>, std::io::Error> {
return Ok(Some(self.field1));
}
}
struct ImplementingStruct<'d, V: SuperTrait> {
field1: V,
_marker: std::marker::PhantomData<&'d ()>,
}
/// Trying to implement the librarie's trait, while using a supertrait
impl<'d, V> GenericTrait for ImplementingStruct<'d, V>
where
// i have also already tried using "SuperTrait + SubTrait", but the same error happens
V: SuperTrait,
{
fn next<T>(&mut self) -> Result<Option<T>, std::io::Error>
where
T: SubTrait,
{
// Error: Expected "Option<T>" found "Option<V>"
return SomeOtherStruct::new(self.field1).do_something();
// TL;DR: how to cast "Option<V>" to "Option<T>"
}
}
rust version: 1.58.1
Notes:
i know This Rust Issue exists, but i dont think it is related (it is about SubTrait -> SuperTrait, but this question is about SuperTrait -> SubTrait)
from what i can tell, Boxing is not a option here
You cannot implement GenericTrait this way.
trait GenericTrait {
fn next<T>(&mut self) -> Result<Option<T>, std::io::Error>
where
T: SubTrait;
}
This trait means: For any type T the caller asks for, if T: SubTrait, next will return a T (wrapped in Result<Option...>). So, if the caller asks for String, next returns a String; if the caller asks for i32, next returns an i32; etc.
impl<'d, V> GenericTrait for ImplementingStruct<'d, V>
where
V: SuperTrait,
{
fn next<T>(&mut self) -> Result<Option<T>, std::io::Error>
where
T: SubTrait,
{
// Error: Expected "Option<T>" found "Option<V>"
return SomeOtherStruct::new(self.field1).do_something();
// TL;DR: how to cast "Option<V>" to "Option<T>"
}
}
This function body says: For any type T the caller asks for, return V instead. If the caller asks for String, you would return V. If the caller asks for i32, you would still return V.
You have broken your promise to the compiler: you said in the function signature you'd return a T, but you're returning a V instead. This isn't possible and has nothing to do with supertraits or subtraits (which are, as pointed out in the question comments, reversed in your example). You can't write a function whose body and signature are in conflict.
In similar cases, sometimes it is appropriate to change the function signature. However, you can't change the function signature because that would make impl GenericTrait for ImplementingStruct untrue. You can't implement a trait by not implementing the trait.
It is not clear what a solution might be in your case, but it is almost certainly unrelated to downcasting or supertraits. The same error happens with only one trait.
Note also that GenericTrait is not a generic trait (that is, a trait with generic parameters). If it were, that is, if it were defined as trait GenericTrait<T>, then you could simply implement GenericTrait<V> for ImplementingStruct<V>. That the method is generic and not the trait is what makes it impossible to implement in this way.
Similar Questions
"Expected type parameter" error in the constructor of a generic struct
Rust generics: Expected <T> found <Foo>
Generics Error: expected type parameter, found struct
"expected type parameter, found struct" when implementing trait
"expected type parameter, found struct"
expected type parameter, found struct
Yet again: "expected type parameter, found struct"

`cannot infer type` on Borrow::borrow despite constraints

I want to write a piece of code that can take references or owned values of a copyable type, and return an owned version of that type. I've reduced the problems I'm having with the type inference to the following code, which errors:
use std::borrow::Borrow;
fn copy<R, E>(val: E) -> R
where
R: Default + Copy,
E: Borrow<R>,
{
*val.borrow()
}
fn main() {
assert_eq!(6, copy(&6));
assert_eq!(6, copy(6));
assert_eq!(6.0, copy(&6.0));
assert!((6.0f64 - copy(&6.0f64)).abs() < 1e-6);
}
The error comes from the last assert:
error[E0282]: type annotations needed
--> src/main.rs:15:13
|
15 | assert!((6.0f64 - copy(&6.0f64)).abs() < 1e-6);
| ^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
|
= note: type must be known at this point
My only hypothesis that the Sub trait on f64 allows f64 or &f64, and if the Default constraint weren't there, then a valid expression for the last copy would be copy::<&f64, &f64>(&6.0f64), however that isn't allowed because &f64 doesn't implement Default. If we pass in an f64 by value it works, presumably because then it restricts R to be f64 instead of either.
What I'm not clear on is why the compiler can't further restrict the return type of copy, or how to indicate to the compiler that the value returned won't be a reference.
Nothing in copy constrains R to a specific concrete type. In particular, &f64 could implement Borrow<R> for multiple values of R (not just f64). It doesn't in your current code, but the lack of alternatives is not considered grounds to pick a specific implementation.
I can even add an implementation that matches:
#[derive(Copy, Clone, Debug, Default)]
struct Foo;
impl Borrow<Foo> for &f64 {
fn borrow(&self) -> &Foo { &Foo }
}
(This trait implementation is permitted even though f64 is a standard library type because Foo is a type defined in the current crate.)
Now we can actually use the choice:
fn main() {
dbg!(copy::<f64, _>(&1.0));
dbg!(copy::<Foo, _>(&1.0));
}
[src/main.rs:19] copy::<f64, _>(&1.0) = 1.0
[src/main.rs:20] copy::<Foo, _>(&1.0) = Foo
A function like copy can only have its return type derived from its argument type when the return type actually depends on the argument type: for example, if it is an associated type of trait implemented by the argument. Both AsRef and Borrow have a type parameter rather than an associated type (and can therefore be implemented multiple times for the same implementing type); Deref has an associated Target type instead, but Deref doesn't offer going from f64 to f64. You could implement your own trait for this:
trait DerefCopy: Copy {
type Output;
fn deref_copy(self) -> Self::Output;
}
impl<T: Copy> DerefCopy for &T {
type Output = T;
fn deref_copy(self) -> T {
*self
}
}
impl DerefCopy for f64 {
type Output = Self;
fn deref_copy(self) -> Self {
self
}
}
fn main() {
assert_eq!(6, (&6).deref_copy());
assert_eq!(6, (6).deref_copy());
assert_eq!(6.0, (&6.0).deref_copy());
assert!((6.0f64 - (&6.0f64).deref_copy()).abs() < 1e-6);
}
However, this would require you to implement DerefCopy for every non-reference type you wish to use it with, because it's not possible to write a blanket implementation for all non-reference Ts; the reason Borrow can have a blanket implementation is that impl Borrow<T> for T doesn't conflict with impl Borrow<T> for &T because if we suppose T is itself a reference &U, we get impl Borrow<&U> for &&U which is still not the same as impl Borrow<T> for T.

The trait `A` is not implemented for the type `A`

I am trying to use a trait that has a function that takes a closure as argument, and then use it on a trait object.
trait A {
fn f<P>(&self, p: P) where P: Fn() -> ();
}
struct B {
a: Box<A>
}
impl B {
fn c(&self) {
self.a.f(|| {});
}
}
This snippet generates the following error:
the trait `A` is not implemented for the type `A` [E0277]
The version of rustc is rustc 1.0.0-beta.3 (5241bf9c3 2015-04-25) (built 2015-04-25).
The problem is that method f is not object-safe because it is generic, and hence it can't be called on a trait object. You will have to force its users to pass boxed closure:
trait A {
fn f(&self, p: Box<Fn() -> ()>);
}
I wonder why Rust allows Box<A> in the first place, I would expect an error there. And this particular error is really misleading. I would file a bug about this.
Alternatively, you can discard trait objects in favor of regular bounded generics, though it is not always possible.

Resources