I was looking to use &[impl Trait] rather than struct names for some more flexible code but I keep getting an error whenever I have an empty slice since it doesn't know a type.
error[E0282]: type annotations needed
--> src/main.rs:9:14
|
9 | let _x = foo(&[]);
| -- ^^^ cannot infer type for `impl Edible`
| |
| consider giving `_x` a type
error: aborting due to previous error
What I find bizarre though is if I switch it to be Option<&[impl Trait]> the None type raises this exact same error.
When using a slice as a signature an empty slice is just as valid as a slice containing items.
The fact that it wants to know the size of an empty slice should not be an issue. If occupying memory is the only issue I'd be happy to have the ability to tell it what's an a acceptable space to occupy if that were possible with the dynamic functionality that impl Trait provides. But impl Trait isn't allowed in the signature for let.
For my use case I have more than a dozen objects that impl Object and the only thing those structs contain internally is a Value struct which is a repr(C) object. So I know the size and internally all the objects are based on the same root code of one specific size. My size is known I just need (really want) Rust to accept it and allow my use case.
Your comments suggest you think impl Trait is dynamic. It is not. In argument position, it is just a way to avoid writing an explicit type parameter. In return position, it is a way to avoid writing an explicit return type. These types must still exist, and must be statically determinable.
The usual solution to this is to specify the type explicitly in the call so that the compiler has enough information to nail down the types involved.. But to do that you need an explicit type parameter.
So I'd say: don't use impl Trait, write an explicit type parameter.
I currently do. I have a CouldBeAnything kind of object which each object can be converted/cast into and then converted back with a kind of "try convert". All this type casting for the same underlying structure that the C side of the code doesn't care about seems like more work than necessary. It just seems like it could be so much better with a behavioural type cast (with many compatible) than a hard cast type (one compatible).
impl Trait does mean an existencial type only for return type. For func's arguments it describes an universal type (generic). And type annotation needed.
This isn't strictly true - this syyntax implies C is chosen by the caller, but it is not.
Still, your point stands. When you return a value from a function, that value must have a type. impl Object does not remove this requirement. It simply means you don't have to write that type in the code.
As written, infer_impl_object would return one of two different types depending on runtime code execution. The compiler does not evaluate if false as unreachable because such optimizations happen much later, and this is part of type resolution. Your input type, and NilClass, are not necessarily the same, and if they are different then this won't work. You must have a single type for every returned value. You can't have 0, and you can't have 2 or more types.
If you want to return "some value which implements Object, but not necessarily one we know at compile time", use dyn Object. Specifically, Box<dyn Object> allows you to allocate any number of different types and construct a single type of box which could refer to any of them.
pub fn infer_impl_object(slice: &[impl Object]) -> &[Box<dyn Object>] {
let vec = vec![Box::new(NilClass::new()) as Box<dyn Object>];
if false {
vec.as_slice()
} else {
slice.iter().map(|thing| Box::new(thing) as Box<dyn Object>)
}
}
Unfortunately your function has more problems than that - like the fact that you're returning a reference to data that is owned by the function, and thus dropped at the end of the function. Even if this type checked, returning vec.as_slice() would fail.
In general, though, if you need to have one of a number of types implementing a trait, and the decision of which is made at runtime, you either need an enum with one variant per possible type, or Box<dyn Trait>.