2017-01-10 4 views
1

У меня есть функция:Как сбрасывать с возвращаемым значением результата?

fn test<T: FromStr>(text: &str) -> Result<T, SaleError> 

SaleError моя ошибка пользовательской структура в этом случае.

У меня есть 2 массивов, которые я хочу умножать то же значение координат и просуммировать результат:

fn calculate_result() -> Result<f64, SaleError> { 
    let q_coms = ["1", "2", "3"]; 
    let v_un_coms = ["1", "2", "3"]; 
    Ok(try!(q_coms.iter().zip(v_un_coms.iter()).fold(0.0, mult_and_sum))) 
} 

Функция пытается застегнуть оба массивы и чем пытается вызвать fold на mult_and_sum функции:

fn mult_and_sum(sum: f64, (q_com, v_un_com): (&str, &str)) -> Result<f64, SaleError> { 
    Ok(sum + try!(test::<f64>(q_com)) * try!(test::<f64>(v_un_com))) 
} 

проблема, насколько я могу понять, что ожидает fold функции с подписью

fn mult_and_sum(sum: f64, (q_com, v_un_com): (&str, &str)) -> f64 

Поскольку функция test может потерпеть неудачу, как я могу использовать fold со значением Result возврата, так что если он терпит неудачу, calculate_result вернется Err(SaleError)?

+2

Занятно, у меня есть [PR, чтобы добавить, что поддержка непосредственно в 'Result'] (https://github.com/rust-lang/rust/ тянуть/38580). – Shepmaster

ответ

5

Не нужно угадывать, сообщение об ошибке сообщает вам. Я реорганизовал его, чтобы сделать его более очевидным:

actual: for<'r, 'r> std::ops::FnMut<(f64, (&'r str, &'r str))> 
required:    std::ops::FnMut<({float}, (&&str, &&str))> 

Он также говорит (expected &str, found str). Это потому, что у вас есть кусочек струнных срезов (&[&str]). Итератор среза возвращает ссылки на каждый элемент, поэтому значение имеет тип &&str. Функция принимает &str, так что это несоответствие.

С другой стороны, documentation for fold показывает, что он ожидает:

fn fold<B, F>(self, init: B, f: F) -> B 
    where F: FnMut(B, Self::Item) -> B 

В словах: fold принимает итератор по значению (self), начальное значение некоторого типа (init: B) и функцию (f: F). Функция принимает этот тип, значение итератора и возвращает тип (FnMut(B, Self::Item) -> B).

Работая в обратном направлении, вы хотите вернуть Result, поэтому к этому нужно приложить B. Это означает, что ваш аккумулятор должен быть Result, предположительно вы хотите Ok, иначе мы сработаем с самого начала. Давайте прямую реализацию, что:

fn calculate_result() -> Result<f64, SaleError> { 
    let q_coms = ["1", "2", "3"]; 
    let v_un_coms = ["1", "2", "3"]; 
    let pairs = q_coms.iter().zip(v_un_coms.iter()); 
    pairs.fold(Ok(0.0), |acc, (a, b)| { 
     match acc { 
      Ok(old) => mult_and_sum(old, (a, b)), 
      other => other, 
     } 
    }) 
} 

Что может быть упрощена с and_then

pairs.fold(Ok(0.0), |acc, (a, b)| 
    acc.and_then(|old| mult_and_sum(old, (a, b))) 
) 

А потом может быть немного меньше, как:

let pairs = q_coms.iter().cloned().zip(v_un_coms.iter().cloned()); 
pairs.fold(Ok(0.0), |acc, i| acc.and_then(|old| mult_and_sum(old, i))) 

Мы используем cloned для преобразования &&str - &str, но вы также можете изменить функцию, чтобы принять a &&str.

2

Поскольку функция test может потерпеть неудачу, как можно использовать раз со значением Result возврата, так что если он терпит неудачу, [если один элемент является Err]?

Ваш аккумулятор (вещь, которая «изменена» на каждой итерации) просто должна быть Result! Посмотрите на этот код:

let q_coms = ["1", "2", "3"]; 
let v_un_coms = ["1", "2", "3"]; 

q_coms.iter() 
    // We don't need to call `iter()` on `v_un_coms`, because `zip()` 
    // takes an argument which implements `IntoIterator` 
    .zip(&v_un_coms) 

    // As you can see: the starting value is `Ok(0.0)` to say: so far, 
    // there was no error. 
    .fold(Ok(0.0), |acc, (a, b)| { 
     // The `and_then()` method calls the given closure when the `acc` 
     // (the outer one) is `Ok`. The inner `acc` represents the `Ok` 
     // value. The closure will then return another `Result`. 
     acc.and_then(|acc| { 
      // More fun with `and_then()` and `map()`. Read docs for more 
      // information. 
      test::<f64>(a) 
       .and_then(|a| test::<f64>(b).map(|b| a * b)) 
       .map(|new_product| acc + new_product) 
     }) 
    }) 

(Try it on playground)

Смежные вопросы