Почему Клиппи предлагает использовать дугу в качестве ссылки?

Я проверяю результаты Clippy в своем коде и обнаружил, что педантичное правило needless_pass_by_value может быть ложным срабатыванием.

В нем говорится, что:

предупреждение: этот аргумент передается по значению, но не используется в теле функции

справка: рассмотрите возможность использования ссылки: &Arc<Mutex<MyStruct>>

Поскольку клонирование Arc - это всего лишь подсчет ссылок, перемещение Arc не должно быть плохой идеей. Действительно ли имеет значение отправка ссылки вместо значения для Arc с точки зрения качества и производительности?

#![warn(clippy::pedantic)]

use std::sync::{Arc, Mutex};

fn main() {
    let my_struct = MyStruct { value: 3 };
    let arc = Arc::new(Mutex::new(my_struct));

    arc_taker(arc.clone());
}

fn arc_taker(prm: Arc<Mutex<MyStruct>>) {
    prm.lock().unwrap().do_something();
}

struct MyStruct {
    value: i32,
}

impl MyStruct {
    fn do_something(&self) {
        println!("self.value: {}", self.value);
    }
}

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


person Akiner Alkan    schedule 08.04.2019    source источник


Ответы (1)


Вызов arc_taker(arc.clone()) увеличивает счетчик ссылок, а возврат из arc_taker снова уменьшает его. В данном случае это бесполезно, поскольку переменная arc в main уже поддерживает Arc в течение всего вызова. Ссылки на него уже было бы достаточно. Нет необходимости увеличивать и уменьшать количество ссылок.

В вашем конкретном примере arc_taker даже не заботится о том, что им управляет Arc. Все, что его волнует, это наличие от Mutex до lock, поэтому, чтобы сделать вашу функцию менее строгой, просто возьмите вместо этого &Mutex<MyStruct>.

Если вы хотите сделать с ним какие-то Arc специфические вещи, например получить weak_count или что-то в этом роде, взятие &Arc<..> имело бы смысл. Если ваша функция будет поддерживать клон Arc, только тогда имеет смысл взять Arc по значению, потому что тогда вызывающий может решить дать вам дополнительную ссылку на него, вызвав .clone() (таким образом увеличивая счетчик ссылок), или предоставить вам право собственности на его собственный Arc (и, таким образом, не увеличивая счетчик ссылок).

person Mara Bos    schedule 08.04.2019
comment
Отличный ответ. FWIW, вот моя версия решения &Mutex<MyStruct>. Конечно, это делает arc_taker плохой репутацией, поскольку не принимает Arc.) - person trentcl; 08.04.2019
comment
Возможно, ничего не стоит из-за общего базового принципа: функция должна делать столько предположений, сколько требуется для ее правильной работы, что должно отражаться в типе аргументов, которые она принимает. Это тот же принцип, который приводит к рекомендации брать &str против &String. - person Matthieu M.; 08.04.2019
comment
@MatthieuM. Да, это хорошее дополнение. В данном конкретном случае это также означает, что он не должен даже принимать &Mutex, поскольку функция всегда блокирует его. Он должен просто принять &MyStruct и оставить блокировку вызывающей стороне. Затем вызывающие абоненты, которые уже заблокировали мьютекс для чего-то еще, также могут вызвать функцию без необходимости сначала разблокировать мьютекс. (Но я предполагаю, что исходный код, из которого возник этот вопрос, делает больше, чем просто .lock().something().) - person Mara Bos; 08.04.2019
comment
@MatthieuM. Я понимаю главный принцип и почему мне следует отправлять рекомендацию, посмотрев на директора. Что меня действительно интересует, нарушение этого принципа не кажется мне вредным, кроме затрат на то, чтобы увеличивать и уменьшать счетчик ссылок? - person Akiner Alkan; 08.04.2019
comment
@ M-ou-se мой MCVE не очень хорошо отражает потребность в Arc. Предположим, мы также проверяем weak_count. Это не имеет большого значения, пока Arc не будет поглощен вызовом. Детская площадка - person Akiner Alkan; 08.04.2019
comment
@AkinerAlkan Думаю, я не знаю, почему вы считаете это ложным срабатыванием из-за обрезков ворса. Тело arc_taker не должно потреблять Arc, поэтому его можно переписать, чтобы принять &Arc. Это все, что говорит линт. Вы можете проигнорировать его по любой причине, но это не делает его ложным. - person trentcl; 08.04.2019
comment
@AkinerAlkan Нарушение этого принципа означает ограничение способов использования вашей функции. Если какая-то другая часть вашего кода имеет Rc<Mutex> или просто Box<Mutex>, будет жаль, если она не сможет использовать вашу функцию, потому что для этого без нужды требуется Arc<Mutex>. Если вам действительно нужен Arc, взятие его по значению, когда достаточно ссылки, означает не только снижение производительности (двойная запись в атомарный объект), но также делает интерфейс менее эргономичным: arc_taker(a) потребляет a, поэтому пользователям функции потребуется вызвать .clone() вместо того, чтобы просто писать &a. - person Mara Bos; 08.04.2019