Introduce completion Candidate trait
diff --git a/TODO.md b/TODO.md
index 74f28b3..6690366 100644
--- a/TODO.md
+++ b/TODO.md
@@ -17,7 +17,7 @@
 - [ ] Windows escape/unescape space in path
 - [ ] file completion & escape/unescape (#106)
 - [ ] file completion & tilde (#62)
-- [ ] display versus replacement
+- [X] display versus replacement
 - [ ] composite/alternate completer (if the current completer returns nothing, try the next one)
 
 Config
diff --git a/examples/example.rs b/examples/example.rs
index adbbbcd..e249f51 100644
--- a/examples/example.rs
+++ b/examples/example.rs
@@ -3,7 +3,7 @@
 
 use log::{Level, LevelFilter, Metadata, Record, SetLoggerError};
 
-use rustyline::completion::{Completer, FilenameCompleter};
+use rustyline::completion::{Completer, FilenameCompleter, Pair};
 use rustyline::error::ReadlineError;
 use rustyline::hint::Hinter;
 use rustyline::{Cmd, CompletionType, Config, EditMode, Editor, Helper, KeyPress};
@@ -20,7 +20,9 @@
 struct MyHelper(FilenameCompleter);
 
 impl Completer for MyHelper {
-    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>), ReadlineError> {
+    type Candidate = Pair;
+
+    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<Pair>), ReadlineError> {
         self.0.complete(line, pos)
     }
 }
diff --git a/src/completion.rs b/src/completion.rs
index 4eaeba9..1d05753 100644
--- a/src/completion.rs
+++ b/src/completion.rs
@@ -12,14 +12,47 @@
 // ("select t.na| from tbl as t")
 // TODO: make &self &mut self ???
 
+/// A completion candidate.
+pub trait Candidate {
+    /// Text to display when listing alternatives.
+    fn display(&self) -> &str;
+    /// Text to insert in line.
+    fn replacement(&self) -> &str;
+}
+
+impl Candidate for String {
+    fn display(&self) -> &str {
+        self.as_str()
+    }
+    fn replacement(&self) -> &str {
+        self.as_str()
+    }
+}
+
+pub struct Pair {
+    pub display: String,
+    pub replacement: String,
+}
+
+impl Candidate for Pair {
+    fn display(&self) -> &str {
+        self.display.as_str()
+    }
+    fn replacement(&self) -> &str {
+        self.replacement.as_str()
+    }
+}
+
 /// To be called for tab-completion.
 pub trait Completer {
+    type Candidate: Candidate;
+
     /// Takes the currently edited `line` with the cursor `pos`ition and
     /// returns the start position and the completion candidates for the
     /// partial word to be completed.
     ///
     /// ("ls /usr/loc", 11) => Ok((3, vec!["/usr/local/"]))
-    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)>;
+    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<Self::Candidate>)>;
     /// Updates the edited `line` with the `elected` candidate.
     fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
         let end = line.pos();
@@ -28,6 +61,8 @@
 }
 
 impl Completer for () {
+    type Candidate = String;
+
     fn complete(&self, _line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
         Ok((0, Vec::with_capacity(0)))
     }
@@ -37,7 +72,9 @@
 }
 
 impl<'c, C: ?Sized + Completer> Completer for &'c C {
-    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
+    type Candidate = C::Candidate;
+
+    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<Self::Candidate>)> {
         (**self).complete(line, pos)
     }
     fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
@@ -48,7 +85,9 @@
     ($($id: ident)*) => {
         $(
             impl<C: ?Sized + Completer> Completer for $id<C> {
-                fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
+                type Candidate = C::Candidate;
+
+                fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<Self::Candidate>)> {
                     (**self).complete(line, pos)
                 }
                 fn update(&self, line: &mut LineBuffer, start: usize, elected: &str) {
@@ -104,7 +143,9 @@
 }
 
 impl Completer for FilenameCompleter {
-    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<String>)> {
+    type Candidate = Pair;
+
+    fn complete(&self, line: &str, pos: usize) -> Result<(usize, Vec<Pair>)> {
         let (start, path, esc_char, break_chars) =
             if let Some((idx, double_quote)) = find_unclosed_quote(&line[..pos]) {
                 let start = idx + 1;
@@ -179,7 +220,7 @@
     path: &str,
     esc_char: Option<char>,
     break_chars: &BTreeSet<char>,
-) -> Result<Vec<String>> {
+) -> Result<Vec<Pair>> {
     use dirs::home_dir;
     use std::env::current_dir;
 
@@ -211,7 +252,7 @@
         dir_path.to_path_buf()
     };
 
-    let mut entries: Vec<String> = Vec::new();
+    let mut entries: Vec<Pair> = Vec::new();
     for entry in try!(dir.read_dir()) {
         let entry = try!(entry);
         if let Some(s) = entry.file_name().to_str() {
@@ -220,7 +261,10 @@
                 if try!(fs::metadata(entry.path())).is_dir() {
                     path.push(sep);
                 }
-                entries.push(escape(path, esc_char, break_chars));
+                entries.push(Pair {
+                    display: String::from(s),
+                    replacement: escape(path, esc_char, break_chars),
+                });
             }
         }
     }
@@ -266,17 +310,17 @@
     }
 }
 
-pub fn longest_common_prefix(candidates: &[String]) -> Option<&str> {
+pub fn longest_common_prefix<C: Candidate>(candidates: &[C]) -> Option<&str> {
     if candidates.is_empty() {
         return None;
     } else if candidates.len() == 1 {
-        return Some(&candidates[0]);
+        return Some(&candidates[0].replacement());
     }
     let mut longest_common_prefix = 0;
     'o: loop {
         for (i, c1) in candidates.iter().enumerate().take(candidates.len() - 1) {
-            let b1 = c1.as_bytes();
-            let b2 = candidates[i + 1].as_bytes();
+            let b1 = c1.replacement().as_bytes();
+            let b2 = candidates[i + 1].replacement().as_bytes();
             if b1.len() <= longest_common_prefix
                 || b2.len() <= longest_common_prefix
                 || b1[longest_common_prefix] != b2[longest_common_prefix]
@@ -286,13 +330,14 @@
         }
         longest_common_prefix += 1;
     }
-    while !candidates[0].is_char_boundary(longest_common_prefix) {
+    let candidate = candidates[0].replacement();
+    while !candidate.is_char_boundary(longest_common_prefix) {
         longest_common_prefix -= 1;
     }
     if longest_common_prefix == 0 {
         return None;
     }
-    Some(&candidates[0][0..longest_common_prefix])
+    Some(&candidate[0..longest_common_prefix])
 }
 
 #[derive(PartialEq)]
diff --git a/src/lib.rs b/src/lib.rs
index d93037d..9d2f876 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -53,7 +53,7 @@
 
 use tty::{RawMode, RawReader, Renderer, Term, Terminal};
 
-use completion::{longest_common_prefix, Completer};
+use completion::{longest_common_prefix, Candidate, Completer};
 pub use config::{CompletionType, Config, EditMode, HistoryDuplicates};
 pub use consts::KeyPress;
 use edit::State;
@@ -91,7 +91,7 @@
         loop {
             // Show completion or original buffer
             if i < candidates.len() {
-                completer.update(&mut s.line, start, &candidates[i]);
+                completer.update(&mut s.line, start, candidates[i].replacement());
                 try!(s.refresh_line());
             } else {
                 // Restore current edited line
@@ -178,11 +178,11 @@
     }
 }
 
-fn page_completions<R: RawReader>(
+fn page_completions<R: RawReader, C: Candidate>(
     rdr: &mut R,
     s: &mut State,
     input_state: &mut InputState,
-    candidates: &[String],
+    candidates: &[C],
 ) -> Result<Option<Cmd>> {
     use std::cmp;
 
@@ -192,7 +192,7 @@
         cols,
         candidates
             .into_iter()
-            .map(|s| s.as_str().width())
+            .map(|s| s.display().width())
             .max()
             .unwrap()
             + min_col_pad,
@@ -235,9 +235,9 @@
         for col in 0..num_cols {
             let i = (col * num_rows) + row;
             if i < candidates.len() {
-                let candidate = &candidates[i];
+                let candidate = &candidates[i].display();
                 ab.push_str(candidate);
-                let width = candidate.as_str().width();
+                let width = candidate.width();
                 if ((col + 1) * num_rows) + row < candidates.len() {
                     for _ in width..max_width {
                         ab.push(' ');
diff --git a/src/test/mod.rs b/src/test/mod.rs
index d3216de..f2d6ea2 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -24,6 +24,8 @@
 
 struct SimpleCompleter;
 impl Completer for SimpleCompleter {
+    type Candidate = String;
+
     fn complete(&self, line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
         Ok((0, vec![line.to_owned() + "t"]))
     }