How do I imply the type of the value when there ar

2020-01-30 01:10发布

I am trying to convert my struct into a HashMap, but I am unable to do so while being in the impl block. Due to a crate constraint, I can only use &self as a parameter for the resolve function.

use std::collections::HashMap;

pub enum Value {
    Int(i64),
    Object(HashMap<String, Value>),
}

pub struct WeatherSettings {
    forecast_days: i64,
}

impl WeatherSettings {
    fn resolve(&self) -> Value {
        let json_object: HashMap<String, Value> = *self.into();
        Value::Object(json_object)
    }
}

impl From<WeatherSettings> for HashMap<String, Value> {
    fn from(weather: WeatherSettings) -> HashMap<String, Value> {
        let mut json_object = HashMap::new();
        json_object.insert("forecast_days".to_owned(),
                           Value::Int(weather.forecast_days));
        return json_object;
    }
}

fn main() {}

More directly, I get the error:

error: the type of this value must be known in this context
  --> src/main.rs:14:51
   |
14 |         let json_object: HashMap<String, Value> = *self.into();
   |                                                   ^^^^^^^^^^^^

标签: rust
1条回答
可以哭但决不认输i
2楼-- · 2020-01-30 01:52

How do I imply the type of the value when there is no type parameters or ascriptions?

In the vast majority of cases, the Rust compiler can infer the generic types of a type or function based on how values of the generic type are used.

In some cases, there isn't enough information to infer exactly one type for a generic type, but there is always a way to pass type parameters when they exist.

The two ways of doing so are to use the turbofish or fully qualified syntax at the call site.

Turbofish

The turbofish is the symbols ::<Type1, Type2, ...> appended to a function or type. See how it looks like a fish?

Function example

mem::size_of is defined as:

pub const fn size_of<T>() -> usize.

You can call it as:

std::mem::size_of::<i8>()
//               ^^^^^^ turbofish

Type example

Vec::new is defined as:

impl<T> Vec<T> {
    pub fn new() -> Vec<T>
}

You can call it as:

Vec::<u8>::new()
// ^^^^^^ turbofish

Multiple types

If your function has multiple types, you need to specify something for each type in the same order as the definition:

fn example<A, B>() {}

fn main() {
    example::<i32, bool>();
    //         ^A  ^B
}

Fully qualified syntax

If you need to disambiguate a method call to a specific trait with a type parameter, you can use the fully qualified syntax.

From::from is defined as:

trait From<T> {
    fn from(T) -> Self;
}

You can call it as:

    <String as From<&str>>::from("a")
//  ^^^^^^^^^^^^^^^^^^^^^^ fully qualified syntax

Partially inferring types

If there are multiple types that can be provided but some of them can be inferred, you can still use _ to allow the compiler to infer that specific type.


Here, I use the turbofish on the Into type for your code:

let json_object = *Into::<HashMap<String, Value>>::into(self);

That's not your problem though.

For this line to be valid:

let json_object: HashMap<String, Value> = *self.into();

The result of the call to self.into() must be something that could be dereferenced to produce the type HashMap<String, Value>. How is the compiler expected to know what that is? It's also not what you want.

All you have is &self, so that's what you have to convert from. Implement the trait for a reference to your struct:

impl<'a> From<&'a WeatherSettings> for HashMap<String, Value> {
    fn from(weather: &'a WeatherSettings) -> HashMap<String, Value> {
        let mut json_object = HashMap::new();
        json_object.insert("unit".to_owned(), Value::String(weather.unit.clone()));
        json_object.insert("forecast_days".to_owned(), Value::Int(weather.forecast_days));
        json_object.insert("data".to_owned(), Value::String(weather.data.clone()));
        json_object
    }
}

This means you cannot move the strings over, but instead have to copy them. That's the restriction placed by the &self.

查看更多
登录 后发表回答