What is the difference between type casting by set

2020-05-06 13:24发布

What is the difference between these two cases? Why does the commented line compile but the last line in the main is incorrect?

How to cut string (substr in C++ with non-const arguments) without an extra variable?

use std::fs::File;
use std::io;
use std::io::Read;

fn read_string(filename: &str) -> Result<String, io::Error> {
    let mut s = String::new();
    File::open(filename)?.read_to_string(&mut s)?;
    Ok(s)
}

fn main() {
    let s = read_string("tt.txt").expect("Wow");

    // let s2: String = s.chars().skip(0).take(s.len() -2).collect();

    println!(
        "{}",
        s.chars().skip(0).take(s.len() - 2).collect() as String
    );
}

标签: rust
2条回答
We Are One
2楼-- · 2020-05-06 14:05

The issue lies in the .collect() method. From it's documentation:

Because collect() is so general, it can cause problems with type inference. As such, collect() is one of the few times you'll see the syntax affectionately known as the 'turbofish': ::<>. This helps the inference algorithm understand specifically which collection you're trying to collect into.

The compiler cannot infer the collection type you want to use, hence the need to specify it explicitly.

Some examples:

fn main() {
    let s = String::from("magic");

    //different ways to specify the target type
    //1. -> explicitly declaring the variable type
    //2. -> turbofish syntax (great article about it: https://matematikaadit.github.io/posts/rust-turbofish.html)

    let a1: String = s.chars().skip(0).take(s.len() - 2).collect();
    let a2 = s.chars().skip(0).take(s.len() - 2).collect::<String>();

    let b1: Vec<char> = s.chars().skip(0).take(s.len() - 2).collect();
    let b2 = s.chars().skip(0).take(s.len() - 2).collect::<Vec<char>>();

    let c1: HashSet<char> = s.chars().skip(0).take(s.len() - 2).collect();
    let c2 = s.chars().skip(0).take(s.len() - 2).collect::<HashSet<char>>();
}

As the examples show, many target types are valid and there is no way for the compiler to decide what your intention was.

查看更多
淡お忘
3楼-- · 2020-05-06 14:11

Explicitly typing a variable is not a type cast.

As thoroughly explained elsewhere, Iterator::collect requires knowing the concrete type to collect into.

A type cast, such as that performed by as, requires converting from one type to another. You've specified the second type (String), but there's still no way for the compiler to deduce what the first type should be.

Turbofish

The syntax you want in today's Rust is the turbofish:

use std::fs;

fn main() {
    let s = fs::read_to_string("tt.txt").expect("Wow");

    println!(
        "{}",
        s.chars().skip(0).take(s.len() - 2).collect::<String>()
    );
}

Type Ascription

As a nightly feature, you could also use the experimental type ascription:

#![feature(type_ascription)]

use std::fs;

fn main() {
    let s = fs::read_to_string("tt.txt").expect("Wow");

    println!(
        "{}",
        s.chars().skip(0).take(s.len() - 2).collect(): String
    );
}

Other

You don't need to write read_string.

查看更多
登录 后发表回答