Рефакторинг `clone`, когда свойство Copy не реализовано? - PullRequest
1 голос
/ 04 февраля 2020

Есть ли способ избавиться от clone(), учитывая ограничения, которые я отметил в комментариях? Мне бы очень хотелось узнать, возможно ли использовать заимствование в этом случае, где изменение сигнатуры сторонней функции невозможно.

// We should keep the "data" hidden from the consumer
mod le_library {
    pub struct Foobar {
        data: Vec<i32>  // Something that doesn't implement Copy
    }

    impl Foobar {
        pub fn new() -> Foobar {
            Foobar {
                data: vec![1, 2, 3],
            }
        }

        pub fn foo(&self) -> String {
            let i = third_party(self.data.clone()); // Refactor out clone?

            format!("{}{}", "foo!", i)
        }
    }

    // Can't change the signature, suppose this comes from a crate
    pub fn third_party(data:Vec<i32>) -> i32 {
        data[0]
    }
}

use le_library::Foobar;

fn main() {
    let foobar = Foobar::new();
    let foo = foobar.foo(); 
    let foo2 = foobar.foo(); 
    println!("{}", foo);
    println!("{}", foo2);
}

детская площадка

Ответы [ 2 ]

2 голосов
/ 04 февраля 2020

Пока ваш метод foo() принимает &self, это невозможно, поскольку сигнатура

pub fn third_party(data: Vec<i32>) -> i32

однозначна: независимо от того, что делает эта функция third_party, ее состояния API что ему нужен собственный экземпляр Vec, по значению. Это исключает использование заимствования в любой форме, и поскольку foo() принимает self по ссылке, вы ничего не можете сделать, кроме клонирования.

Кроме того, предположительно, этот third_party написан без каких-либо странных небезопасных хаки, так что вполне безопасно предположить, что Vec, который передается в него, в конечном итоге отбрасывается и освобождается. Поэтому небезопасное создание копии оригинального Vec без ее клонирования (путем копирования внутренних указателей) не подлежит сомнению - если вы это сделаете, вы обязательно получите беспошлинное использование.

Пока ваш вопрос не гласит, тот факт, что вы хотите сохранить первоначальное значение data, является своего рода естественным предположением. Если это предположение можно ослабить, и вы на самом деле согласны выдать экземпляр data и, например, заменить его пустым вектором внутри, тогда есть несколько вещей, которые вы потенциально можете сделать:

  1. Переключите foo(&self) на foo(&mut self), тогда вы можете легко извлечь data и заменить его пустым вектором.
  2. Используйте Cell или RefCell для хранения данных. Таким образом, вы можете продолжать использовать foo(&self) за счет некоторых проверок во время выполнения, когда вы извлекаете значение из ячейки и заменяете его некоторым значением по умолчанию.

Однако оба эти подхода , вы потеряете оригинал Vec. С данным сторонним API нет никакого способа обойти это.

Если вы все еще можете каким-то образом влиять на этот внешний API, то лучшим решением было бы изменить его, чтобы он принимал &[i32], который может быть легко получен от Vec<i32> с заимствованием.

2 голосов
/ 04 февраля 2020

Нет, вы не можете избавиться от вызова clone здесь.

Проблема здесь со сторонней библиотекой. Поскольку функция third_party написана сейчас, это правда, что она может использовать &Vec<i32>; он не требует владения, поскольку он просто выводит значение, равное Copy. Однако, поскольку реализация находится вне вашего контроля, ничто не мешает человеку, поддерживающему функцию, изменить ее, чтобы воспользоваться преимуществом владения Vec. Возможно, что все, что он делает, будет проще или потребует меньше памяти, если ему будет позволено перезаписать предоставленную память, а средство записи функций оставляет дверь открытой для этого в будущем. Если это не так, возможно, стоит предложить изменить подпись сторонней функции и в то же время полагаться на clone.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...