89003


**目標**

  * Rustで配列内の文字列を新しい配列を用意せずに、全て逆順にする
  * 配列、vectorのどちらでも良い
  * 例） `["Hello","World"]` → `["olleH","dlroW"]`

**試したこと**

  * for inとiter_mut

[code]

    // data : 反転させたい文字列が格納されている配列
    for d in data.iter_mut() {
            *d = d.chars().rev().collect::<String>();
        }
    
    
[/code]

エラー  
・consider borrowing here: `&d.chars().rev().collect::<String>()`  
・mismatched types  
expected `&str`, found struct `std::string::String`

エラー文の通りに`&d.chars()...`にしたら`let`を使ってと言われる

  * map

[code]

    data = data
            .iter()
            .map(|&d| d.chars().rev().collect::<String>())
            .collect::<Vec<_>>();
    
[/code]

エラー  
・mismatched types  
expected struct `std::vec::Vec<&str>`  
found struct `std::vec::Vec<std::string::String>`

  * そのまま入れるやつ(一応)

[code]

    for (i, d) in data.iter().enumerate() {
            data[i] = &d.chars().rev().collect::<String>();
        }
    
[/code]

エラー  
・cannot assign to `data[_]` because it is borrowed  
assignment to borrowed `data[_]` occurs here


CC BY-SA 4.0
2022-05-22T13:06:45.550
88982
2022-06-01T05:38:51.157
2022-05-22T13:53:36.790
52746
52746
question
rust
Rustで新しい配列を用意せずに、配列内の文字列を全て逆順にできるのか知りたい


おそらく全体のコードはこのようになっていると思います

[code]

    fn main() {
        let mut data = ["Hello", "World"];
        for d in data.iter_mut() {
            *d = d.chars().rev().collect::<String>();
        }
        dbg!(&data);    
    }
    
[/code]

この時、`"Hello"`, `data[0]` の型は `&str` であり、`String` とは別のものです。  
`&str` は immutable な参照であって、 **参照先に** 再代入することは出来ません。  
よって、元の配列 (or `Vec`) が `&str` の配列である限り、その配列に逆順の文字列を入れることは、（通常の方法では）不可能です

`data` が `String` の配列であれば再代入することが可能です。

[code]

    fn main() {
        let mut data = ["Hello".to_string(), "World".to_string()];
        for d in data.iter_mut() {        
            *d = d.chars().rev().collect::<String>();
        }
        dbg!(&data);    
    }
    
[/code]


CC BY-SA 4.0
2022-05-22T13:44:48.650
88984
2022-05-22T13:44:48.650
20885
88982
answer


[v..snow さんの回答](https://ja.stackoverflow.com/a/88984/3054)にある通り、`String`
を使うのが実用的な書き方だと思います。

以下では、`&str`(文字列リテラル)のまま行なう場合を考えます。  
この条件だと、「新しい配列を用意せず」の意味合いによって可不可が変わります。

### 別のメモリ領域を確保したくない、という場合

以下の理由で、これは不可能です。

  * `&str` は不変の参照なのでこれを通してデータの変更はできない
  * `&str` により参照されている間は、元のデータも変更できない
  * 配列の生存期間を終わらせる以外に、参照を「返す」ような操作は無い

特に、文字列リテラルで作られたデータは、読み出し専用メモリ領域に置かれるようなので、unsafe な機能を使っても変更できないと思います。

### 変換結果の要素を別の配列に入れるのではなく、最初の配列に入れたい、という場合

これは可能です。  
必要なのは、

  * 変換結果をメモリ領域に保持する
  * そのメモリ領域の生存期間を目的の配列以上にする
  * 目的の配列の各要素を、確保したメモリ領域への参照とする(借用する)

の3点です。  
質問者さんのコードが「`let`を使ってと言われる」のは、上記の生存期間を確保できていないからです。コンパイラの提案に従って `let`
を使うと下記のようになり、エラーなく実行可能です。

[code]

    let mut data = ["Hello", "World"];
                                                          
    let buf0 = data[0].chars().rev().collect::<String>();
    let buf1 = data[1].chars().rev().collect::<String>();
    data[0] = &buf0;
    data[1] = &buf1;
                                                          
    dbg!(&data);
    assert!(data == ["olleH", "dlroW"]);
    
[/code]

要素の数だけ `let` を書くのは大変なので、一つの変数にまとめる場合は、下のようなコードが考えられます。

`Vec` を使う例:

[code]

    let mut data = ["Hello", "World"];
    let mut buf: Vec<String> = Vec::new();
                                                       
    for s in data.iter() {
        buf.push(s.chars().rev().collect::<String>());
    }
                                                       
    for (i, s) in buf.iter().enumerate() {
        data[i] = &s;
    }
                                                       
    dbg!(&data);
    dbg!(&buf);
    assert!(data == ["olleH", "dlroW"]);
    
[/code]

`String` を使う例:

[code]

    let mut data = ["Hello", "World"];
    let mut buf = String::new();
                                         
    for s in data.iter() {
        for c in s.chars().rev() {
            buf.push(c);
        }
    }
                                         
    let mut head = 0;
    for i in 0..data.len() {
        let tail = head + data[i].len();
        data[i] = &buf[head..tail];
        head = tail;
    }
                                         
    dbg!(&data);
    dbg!(&buf);
    assert!(data == ["olleH", "dlroW"]);
    
[/code]

以上のように、コンパイラを満足させるためループが2つに分かれており、いささか非効率かも知れません。もっと良い方法があれば知りたいです。


CC BY-SA 4.0
2022-05-23T13:19:10.050
89003
2022-06-01T05:38:51.157
2022-06-01T05:38:51.157
3054
3054
88982
answer