2016-07-13 8 views
0

私は、各文字列の最初の文字を大文字にし、残りを大文字にする関数を持つ文字列の新しい特性を実装しようとしています。私は錆の標準ライブラリのto_uppercase()to_lowercase()に関数のインターフェイスを基にしています。複数のイテレータ・タイプからどのように収集しますか?

use std::io; 

trait ToCapitalized { 
    fn to_capitalized(&self) -> String; 
} 

impl ToCapitalized for String { 
    fn to_capitalized(&self) -> String { 
     self.chars().enumerate().map(|(i, c)| { 
      match i { 
       0 => c.to_uppercase(), 
       _ => c.to_lowercase(), 
      } 
     }).collect() 
    } 
} 

fn main() { 
    let mut buffer = String::new(); 
    io::stdin().read_line(&mut buffer).ok().expect("Unable to read from stdin."); 

    println!("{}", buffer.to_capitalized()); 
} 

このコードは、here所定の提案に基づいて、しかしコードが古くなって、複数のコンパイルエラーが発生しています。

src/main.rs:10:13: 13:14 error: match arms have incompatible types [E0308] 
src/main.rs:10    match i { 
         ^
src/main.rs:10:13: 13:14 help: run `rustc --explain E0308` to see a detailed explanation 
src/main.rs:10:13: 13:14 note: expected type `std::char::ToUppercase` 
src/main.rs:10:13: 13:14 note: found type `std::char::ToLowercase` 
src/main.rs:12:22: 12:38 note: match arm with an incompatible type 
src/main.rs:12     _ => c.to_lowercase(), 

だから要するに、fn to_uppercase(&self) -> ToUppercasefn to_lowercase(&self) -> ToLowercaseの戻り値は、マップは現在、複数の戻り値の型を持っているので、一緒に収集することができません:私は今、私の実装を持っています唯一の問題は、次のエラーです。

BytesCharsのような共通のイテレータタイプにキャストしようとしましたが、これらのイテレータタイプを収集して文字列を形成することはできません。助言がありますか?ここで

答えて

2

pub fn to_uppercase(&self) -> Stringhereの実装を見て、私はDogbertとDKのソリューションと標準ライブラリで提供されている実装の間のハイブリッドのビットである解決策を考案しました。それはUnicodeでも動作します!

fn to_capitalized(&self) -> String { 
    match self.len() { 
     0 => String::new(), 
     _ => { 
      let mut s = String::with_capacity(self.len()); 
      s.extend(self.chars().next().unwrap().to_uppercase()); 
      s.extend(self.chars().skip(1).flat_map(|c| c.to_lowercase())); 
      return s; 
     } 
    } 
} 

Working Rust Playground Example

編集:大きい可視性のために、Shepmasterの簡略化および最適化されたソリューション:

fn to_capitalized(&self) -> String { 
    let mut s = String::with_capacity(self.len()); 
    let mut chars = self.chars(); 

    s.extend(chars.by_ref().take(1).flat_map(|c| c.to_uppercase())); 
    s.extend(chars.flat_map(|c| c.to_lowercase())); 

    s 
} 
+1

[簡略化してやや最適化できます](https://play.rust-lang.org/?gist=dfa85b1d5e07d8e5e29785c5e530ee76) – Shepmaster

1

は、私はそれを行うだろう方法は次のとおりです。

trait ToCapitalized { 
    fn to_capitalized(&self) -> String; 
} 

impl ToCapitalized for String { 
    fn to_capitalized(&self) -> String { 
     match self.chars().next() { 
      Some(c) => { 
       c.to_uppercase() 
        .chain(self.chars().skip(1).flat_map(|c| c.to_lowercase())) 
        .collect() 
      } 
      None => String::new(), 
     } 
    } 
} 

fn main() { 
    println!("{}", "fOoBaR".to_string().to_capitalized()); 
} 

それが二回最初の文字を解読し、それはIMOかなり読みやすいですので、これは、理想的なソリューションよりも少し遅くなります。

出力:

Foobar 
+0

は、それがメモリリークが発生しません@Jared(ボックスはcollect' 'とすぐに割り当て解除になるだろうそれらを使用して行われます)が、非常に非効率的です。編集された答えをご覧ください。 – Dogbert

2

鋳造がまれルーストにタイプの問題を解決するための良い方法です。ここでの正しい解決策は、異種のイテレータ・タイプを統一するタイプを記述する(または定義するクレートを見つける)ことです。しかし、それは努力を必要とするので、それはちょうど窓の外collectをスローするように簡単です:あなたは「ToUppercaseのいずれかを表すために、いくつかの種類があった場合

trait ToCapitalized { 
    fn to_capitalized(&self) -> String; 
} 

impl ToCapitalized for String { 
    fn to_capitalized(&self) -> String { 
     let mut r = String::with_capacity(self.len()); 
     for (i, c) in self.chars().enumerate() { 
      match i { 
       0 => r.extend(c.to_uppercase()), 
       _ => r.extend(c.to_lowercase()), 
      } 
     } 
     r 
    } 
} 

fn main() { 
    let buffer = String::from("canberra"); 
    println!("{}", buffer.to_capitalized()); 
} 

これは、多かれ少なかれ、何collectはとにかくをを行うだろうさまたはToLowercase "となります。 の広範なケースでは、の場合、これは1回の割り当てだけを実行します。

+0

おそらく、stdlibのどこかにIterator型がありますが、私たちの誰かがそれを見つけることはできないでしょう。正直言って、あまりにも多くあると思うが、私は逃げる。これは最良の解決策のように見えます。なぜなら、同様のforループの収集コールを単に控えるだけなので、私はこれが最も賢明なアプローチになると考えています。あなたのご意見ありがとうございます。 –

関連する問題