AsRef<Path> vs &Path - rust

I have the following struct:
struct Config<'a> {
path1: &'a dyn AsRef<Path>,
path2: HashMap<SomeEnum, &'a dyn AsRef<Path>>,
}
Then when I try to create a new instance of this config:
Config {
path1: &Path::new("path/to/file1"),
path2: HashMap::from([(SomeEnum::Value, &Path::new("path/to/file2"))
}
I get no errors for the variable of path1, but for path2 I get:
error[E0308]: mismatched types
expected trait object `dyn AsRef`, found `&Path`
Note: expected struct `HashMap<_, &dyn AsRef<Path>>`
found struct `HashMap<_, &&Path>`
I don't get why this works for path1, but not for path2.
Thanks in advance for any help,
Jomy

Type inference isn't quite able to figure out what's supposed to happen here. It isn't able to see that the &&Path reference should be converted to &dyn AsRef<Path>.
You need to help it figure out that the second tuple slot should be &dyn AsRef<Path>. You could do this with a cast:
Config {
path1: &Path::new("path/to/file1"),
path2: HashMap::from([(SomeEnum::Value, &Path::new("path/to/file2") as &dyn AsRef<Path>)]),
}
You could also create the tuple first with a type annotation:
let v: (SomeEnum, &dyn AsRef<Path>) = (SomeEnum::Value, &Path::new("path/to/file2"));
Config {
path1: &Path::new("path/to/file1"),
path2: HashMap::from([v]),
}

It seems like in this case, the compiler won't automatically cast the reference into a trait object reference. But you can do it manually with as:
Config {
path1: &Path::new("path/to/file1"),
path2: HashMap::from([(SomeEnum::Value, &Path::new("path/to/file2") as &dyn AsRef<Path>)]),
}
playground

Related

How do I debug a downcast_ref failure?

Since Rust doesn't support upcasting, I'm trying to do the as_any trick mentioned here, but with a parameterized type. However, when I try to call downcast_ref on the returned Any, I get None. Since I cannot print Any to find out what it actually is:
`dyn std::any::Any` doesn't implement `std::fmt::Display`
`dyn std::any::Any` cannot be formatted with the default formatter
How can I debug what it actually is? Here is the failing code (playground):
use std::any::Any;
use std::rc::{Rc, Weak};
pub trait Wrapper: Any {
fn as_any(&self) -> &dyn Any;
}
pub struct WeakWrapper<T: Any> {
item: Weak<T>,
}
impl<'a, T: Any + 'static> Wrapper for WeakWrapper<T> {
fn as_any(&self) -> &dyn Any {
self
}
}
fn main() {
let rc = Rc::new(Box::new(5));
let weak_wrapper = WeakWrapper {
item: Rc::downgrade(&rc),
};
let item = weak_wrapper
.as_any()
.downcast_ref::<WeakWrapper<i32>>()
.map(|w| w.item.upgrade().map(|n| *n))
.flatten();
println!("Item is {}", item.unwrap());
}
That's because you're downcasting to the wrong type: you're trying to downcast to WeakWrapper<i32> but weak_wrapper is a WeakWrapper<Box<i32>>: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f49a3c2236beabae48a41d024a04d08f
Since I cannot print Any to find out what it actually is
You can debug-print the TypeId, though it's not very helpful (you get a pretty opaque number) it can be compared to TypeId::of::<type>(), so you can do e.g.
any_value.type_id() == TypeId::of::<WeakWrapper<i32>>()
and find out that it's false, but
any_value.type_id() == TypeId::of::<WeakWrapper<Box<i32>>>()
is true.

How to convert trait to concrete type?

I have a trait object, and I want to know the concrete object that it points to, but I cannot work out how to get the concrete object.
What I want is something like the following:
trait MyClonable {
/** copy from another MyClonable */
fn my_clone_from(&mut self, other: &Self)->Result<(), MyError>;
}
impl MyClonable for SomeType {
fn my_clone_from(&mut self, other: &MyClonable)->Result<(), MyError> {...}
}
So that I can say something like:
let mut new_thing = SomeType::new();
new_thing.my_clone_from(&old_thing)?;
Then the new_thing will contain a sort-of copy of the old_thing, unless old_thing is of an unexpected type, in which case it should return an error.
But Rust will not let me get something like an Option<&SomeType> from a MyClonable.
You cannot. The trait object only gives you access to the trait methods. You would need to manually specify which concrete type to downcast to as described in this QA: How to get a reference to a concrete type from a trait object?.
Then, you could try downcasting to each of your known types until one of them succeeds but that is dirty.
Instead, using generics would be a better fit:
trait MyClonable<T> {
fn my_clone_from(&mut self, other: &T);
}
And now you can implement this trait for all your supported types:
impl MyClonable<u32> for i32 {
fn my_clone_from(&mut self, _other: &u32) { }
}
impl MyClonable<Tomato> for i32 {
fn my_clone_from(&mut self, _other: &Tomato) { }
}

Reference to generic trait with asscociated type as struct field

I am struggling to convince Rust to accept a reference to an instance implementing a generic trait with associated type as a value of struct field. Could you please give some clues what is wrong in the code below.
trait TraitA{}
trait TraitB{
type As: TraitA;
}
struct StructA;
impl TraitA for StructA{}
struct StructB;
impl TraitB for StructB{
type As = StructA;
}
struct StructC<'a> {
r: &'a (dyn TraitB<As = (dyn TraitA)> + 'a),
}
fn main(){
let x = StructB;
let z = StructC {
r: &x,
};
}
Playground
About posted compiler error
The problem caused be the fact that TCPtransport does not implement
Transport<.., Configuration=(dyn TransportConfiguration<SyncReq>>,
instead it implements
Transport<.., Configuration=TcpTranportCfg>, and later one cannot be casted to former:
playground
Possible solution: Add new trait (Basically to strip associated type info):
trait SyncReqTransport{
...
}
impl<T> SyncReqTransport<SyncReq> for T
where
T: Transport<SyncReq>, // I Removed other parameters for simplicity
<T as Transport<SyncReq>>::Config: TransportConfiguration<SyncReq>
{
...
}
And change DAG to
pub struct DAG<'a, T> {
request_transport: &'a (dyn SyncReqTransport + 'a),
}
Another problem
sr_transport being created on stack, and later reference to it being returned from
the function it is created, which is illegal.

E0277 "Sized is not implemented for the type [u8]", but my type does not have a [u8]

I'm making a Node tree. Here is the code:
use std::option::Option;
use std::path;
#[derive(Debug)]
enum NodeType {
Binding((String, String)),
Header,
Include(path::Path),
Raw(String),
}
#[derive(Debug)]
pub struct Node {
node_type: NodeType,
}
impl Node {
fn new() -> Node {
Node { node_type: NodeType::Header }
}
}
When I compile this, I get the following error:
error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied in `std::path::Path`
--> src/main.rs:8:13
|
8 | Include(path::Path),
| ^^^^^^^^^^^ within `std::path::Path`, the trait `std::marker::Sized` is not implemented for `[u8]`
|
= note: `[u8]` does not have a constant size known at compile-time
= note: required because it appears within the type `std::path::Path`
= note: only the last field of a struct may have a dynamically sized type
I searched for this error, but it seems to refer to an type where Sized is not implemented. Oddly, the error output says that [u8] does not implement Sized, but there's not even one u8 in my code. What could it be?
The problem is that your NodeType enum contains a std::path::Path in its Include variant, but Path is an unsized type (because it contains a [u8] indirectly, and [u8] is unsized, hence the error you get).
To fix this, change the Include variant to contain either a &Path (if the node should borrow the path) or a PathBuf (if the node should own the path), or else change Node::new() to return Box<Node>.
Changing Include to contain a &Path requires adding a lifetime parameter to Node and to NodeType. The concrete lifetime may be static when the enum is not an Include.
The code below demonstrates how this would work. Note how there are two impl blocks for Node: the first one (impl Node<'static>) should contain all methods that don't use the lifetime parameter, while the second one (impl<'a> Node<'a>) should contain all methods that do use the lifetime parameter (that includes all methods with a self parameter).
use std::path;
#[derive(Debug)]
enum NodeType<'a> {
Binding((String, String)),
Header,
Include(&'a path::Path),
Raw(String),
}
#[derive(Debug)]
pub struct Node<'a> {
node_type: NodeType<'a>,
}
impl Node<'static> {
fn new() -> Node<'static> {
Node { node_type: NodeType::Header }
}
}
impl<'a> Node<'a> {
fn include(path: &'a path::Path) -> Node<'a> {
Node { node_type: NodeType::Include(path) }
}
}

Why is this trait/implementation incompatible - bound lifetime vs concrete lifetime

I'm struggling with this error rustc gives me:
error: method `create_shader_explicit` has an incompatible type for trait: expected bound lifetime parameter 'a, found concrete lifetime
My trait declaration is pretty much this:
pub trait GraphicsContext<R: Resources> {
/// Creates a shader object
fn create_shader<'a>(&'a self, shader::Stage, source: &str) ->
Result<handle::Shader<R>, shader::CreateError>;
}
Here's my implementation,
pub struct OpenGLResources<'a> {
phantom: PhantomData<&'a u32>
}
impl<'a> Resources for OpenGLResources<'a> {
type Shader = Shader<'a>;
}
impl<'z> GraphicsContext<OpenGLResources<'z>> for OpenGLGraphicsContext {
/// Creates a shader object
fn create_shader<'a>(&'a self, stage: shader::Stage, source: &str) ->
Result<handle::Shader<OpenGLResources>, shader::CreateError> {
let shader = Shader::new(self, stage);
try!(shader.compile_from_source(source));
Ok(shader)
}
}
In other questions on StackOverflow, they are missing things like <'a> between create_shader and (), however when I compare the fn definitions in mine they look identical.
EDIT:
Changing the definition inside impl to the following fixes that issue
fn create_shader<'a>(&'a self, stage: shader::Stage, source: &str) ->
Result<handle::Shader<OpenGLResources**<'z>**>, shader::CreateError>
But then the issue is that 'a and 'z need to be the same lifetime. If I change it to this:
fn create_shader(**&'z** self, stage: shader::Stage, source: &str) ->
Result<handle::Shader<OpenGLResources<'z>>, shader::CreateError>
The impl block works, but then I need a way of specifying the 'z lifetime in the trait definition. I tried the following:
pub trait<'z> GraphicsContext<R: Resources<'z>>
But it didn't work.
When comparing things like this, you need to remember to expand all the generics so that you can actually compare it all. In this case, you haven’t expanded R. If you do, the answer becomes obvious: R is OpenGLResources<'z>, linking the OpenGLResources to the impl block, whereas your method definition has elided the lifetime on OpenGLResources, causing it to be inferred as self’s lifetime, which is 'a.
Thanks the hints of #Chris Morgan I managed to implement this functionality and its now working fine.
If we start with the base trait with the 'a lifetime included:
trait Resources<'a> {
type Shader: Shader;
type ShaderProgram: ShaderProgram;
}
Then implement it for OpenGL. (note the PhantomData struct)
struct OpenGLResources<'a> {
phantom: PhantomData<&'a u32> // 'a is the lifetime of the context reference
}
impl<'a> ResourcesTrait<'a> for Resources<'a> {
type Shader = Shader<'a>;
type ShaderProgram = ShaderProgram<'a>;
type CommandBuffer = CommandBuffer;
type CommandBufferBuilder = CommandBufferBuilder;
}
Its a bit verbose, but the GraphicsContext trait works fine too now. The 'a lifetime goes in the type parameters part.
trait GraphicsContext<'a, R: Resources<'a>> {
fn create_shader(&'a self, ty: Type, source: &str) -> Result<R::Shader, ShaderCreationError>
}
Finally this is the required code in the graphics context implementation.
It is extremely verbose with the 'a lifetimes sprinkled everywhere but at least it works!
impl<'a> GraphicsContext<'a, Resources<'a>> for OpenGLGraphicsContext

Resources