2014-12-05 3 views
0

Я пытаюсь отредактировать строку на месте, передав ее mutate(), см. Ниже.Редактировать строку на месте с помощью функции

Упрощенный пример:

fn mutate(string: &mut &str) -> &str { 
    string[0] = 'a'; // mutate string 
    string 
} 

fn do_something(string: &str) { 
    println!("{}", string); 
} 

fn main() { 
    let string = "Hello, world!"; 
    loop { 
     string = mutate(&mut string); 
     do_something(string); 
    } 
} 

Но я получаю следующее сообщение об ошибке компиляции:

main.rs:1:33: 1:37 error: missing lifetime specifier [E0106] 
main.rs:1 fn mutate(string: &mut &str) -> &str { 
              ^~~~ 
main.rs:1:33: 1:37 help: this function's return type contains a borrowed value, but the signature does not say which one of `string`'s 2 elided lifetimes it is borrowed from 
main.rs:1 fn mutate(string: &mut &str) -> &str { 
              ^~~~ 

Почему я получаю эту ошибку и как я могу добиться того, чего я хочу?

+0

Что заставляет вас думать, что '& str' или' String' реализуют 'IndexMut'? Unicode гораздо более сложнее, чем это, и, следовательно, чтобы избежать ошибок, они этого не делают. Я не уверен, как достичь того, что вы хотите сделать, хотя ... –

+0

Возможно, мой фон C, который заставляет меня хотеть индексировать строки. Я понимаю, что это невозможно в ржавчине? Нужно ли использовать цикл? Предположим, я просто хочу отредактировать 100-й символ в строке, которую мне нужно будет перебрать через первые 99, прежде чем я смогу отредактировать 100-й? – Semi

ответ

8

Вы не можете изменить срез строки вообще. &mut &str не является подходящим типом в любом случае, потому что он буквально является изменяемым указателем на неизменяемым фрагментом. И все строка ломтики неизменяемы.

В строках Rust действительны последовательности UTF-8, а UTF-8 - кодирование с переменной шириной. Следовательно, в общем случае изменение символа может изменить длину строки в байтах. Это невозможно сделать с помощью срезов (поскольку они всегда имеют фиксированную длину), и это может привести к перераспределению для принадлежащих строк. Более того, в 99% случаев изменение символа внутри строки не то, что вы действительно хотите.

Для того, чтобы сделать то, что вы хотите с Юникодом кодовых вам нужно сделать что-то вроде этого:

fn replace_char_at(s: &str, idx: uint, c: char) -> String { 
    let mut r = String::with_capacity(s.len()); 
    for (i, d) in s.char_indices() { 
     r.push(if i == idx { c } else { d }); 
    } 
    r 
} 

Однако это O(n) эффективности, поскольку он должен перебирать оригинальный кусочек, и он также выиграл «Правильно работать со сложными символами - это может заменить письмо, но оставить акцент или наоборот.

Более правильный способ для обработки текста является перебирать графема кластеры, он будет принимать диакритических и другие подобные вещи правильно (в основном):

fn replace_grapheme_at(s: &str, idx: uint, c: &str) -> String { 
    let mut r = String::with_capacity(s.len()); 
    for (i, g) in s.grapheme_indices(true) { 
     r.push_str(if i == idx { c } else { g }); 
    } 
    r 
} 

Существует также некоторая поддержка для чистых ASCII строк в std::ascii модуле, но, скорее всего, он скоро будет реформирован. Во всяком случае, это, как он может быть использован:

fn replace_ascii_char_at(s: String, idx: uint, c: char) -> String { 
    let mut ascii_s = s.into_ascii(); 
    ascii_s[idx] = c.to_ascii(); 
    String::from_utf8(ascii_s.into_bytes()).unwrap() 
} 

Это будет паниковать, если либо s содержит не-ASCII символы или c не является ASCII-символов.

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