Is there a better functional way to process a vector with error checking?

I think that's what partition_map() from itertools is for:

use itertools::{Either, Itertools};

fn data_vals<'a>(sv: &[&'a str]) -> Result<Vec<i32>, Vec<(&'a str, std::num::ParseIntError)>> {
    let (successes, failures): (Vec<_>, Vec<_>) =
        sv.iter().partition_map(|s| match s.parse::<i32>() {
            Ok(v) => Either::Left(v),
            Err(e) => Either::Right((*s, e)),
        });
    if failures.len() != 0 {
        Err(failures)
    } else {
        Ok(successes)
    }
}

fn main() {
    let numbers = vec!["42", "aaaezrgggtht", "..4rez41eza", "55"];
    println!("{:#?}", data_vals(&numbers));
}

In a purely functional style, you have to avoid side-effects. Printing errors is a side-effect. The preferred style would be to return an object of the style:

Result<Vec<i32>, Vec<String>>

and print the list after the data_vals function returns.

So, essentially, you want your processing to collect a list of integers, and a list of strings:

fn data_vals(sv: &Vec<(u32, String)>) -> Result<Vec<i32>, Vec<String>> {
    let (ok, err): (Vec<_>, Vec<_>) = sv
        .iter()
        .map(|(i, s)| {
            s.parse()
                .map_err(|_e| format!("ERROR: Invalid data value at line {}: '{}'", i, s))
        })
        .partition(|e| e.is_ok());

    if err.len() > 0 {
        Err(err.iter().filter_map(|e| e.clone().err()).collect())
    } else {
        Ok(ok.iter().filter_map(|e| e.clone().ok()).collect())
    }
}

fn main() {
    let input = vec![(1, "0".to_string())];
    let r = data_vals(&input);
    assert_eq!(r, Ok(vec![0]));

    let input = vec![(1, "zzz".to_string())];
    let r = data_vals(&input);
    assert_eq!(r, Err(vec!["ERROR: Invalid data value at line 1: 'zzz'".to_string()]));
}

Playground Link

This uses partition which does not depend on an external crate.