Как мне преобразовать из & mut T в тип & mut U? - PullRequest
1 голос
/ 15 марта 2019

Я пытаюсь понять, что заимствовать чекер. У меня есть функция с подписью

fn SerializeChar(&mut self, value: &mut u8)

Я хотел бы получить данные от u8 и i8, потому что мне не важен знак:

let mut test: i8 = 0; 
thing.SerializeChar(&mut test); //Error: &mut u8 expected

Это хорошо, но как именно я это разыгрываю?

&mut (test as u8) это не одно и то же. Есть ли безопасный или небезопасный способ передать тест в качестве аргумента SerializeChar, похожий на простой приведение в C ++? Кроме того, я не хотел бы уничтожать test, когда я делаю это, поскольку мне все еще нужно test, и все еще хочу это как i8, а не u8.

Я не пытаюсь клонировать значение. Я хочу, чтобы адрес был test, потому что данные в SerializeChar являются изменяемыми и должны изменять то, что вставлено. В терминах C ++ я не хочу char, я хочу *char, потому что мне нужно изменить 8 бит в памяти. Вполне возможно, что я могу сделать две отдельные версии SerializeChar, но до этого я хотел знать, возможно ли на самом деле сделать что-то вроде *u8(test), с которым проверяет заемщик в порядке.

Ответы [ 3 ]

3 голосов
/ 15 марта 2019

Перво-наперво, это не имеет никакого отношения к контролеру заимствований.То, что один тип не совпадает с другим, относится к проверке type .

Существует ли какой-либо безопасный или небезопасный способ передачи test в качестве аргумента SerializeChar, аналогичный простому приведению в C ++?

Через необработанные указатели

Преобразуйте его в следующую последовательность:

  1. &mut i8
  2. *mut i8
  3. *mut u8
  4. &mut u8
fn serialize_char(value: &mut u8) {
    *value = std::u8::MAX
}

fn main() {
    let mut test: i8 = 0;
    serialize_char(unsafe { &mut *(&mut test as *mut i8 as *mut u8) });
    println!("{}", test); // -1
}

См. Также:

transmute

Использование as всегда должно быть вашей первой попыткой, но есть также Большой Молот transmute.Это позволяет вам совершать всевозможные плохие поступки по сравнению с более простой последовательностью приведений as и не рекомендуется при наличии альтернативы:

use std::mem;

fn serialize_char(value: &mut u8) {
    *value = std::u8::MAX
}

fn main() {
    let mut test: i8 = 0;
    serialize_char(unsafe { mem::transmute(&mut test) });
    println!("{}", test); // -1
}

См. Также:

Безопасность

Все преобразования через as безопасны, хотя они могут выдавать поддельные или неожиданные данные.

Преобразование из *mut u8 в &mut u8 или использование transmute небезопасно, поскольку программист должен убедиться, что:

  • соблюдены правила ссылок
  • все значения действительнызначения для их типов

Мы знаем, что ссылка действительна: есть только одна изменяемая ссылка, и она указывает на действующие данные.

A u8 и i8оба действительны для любого 8-битного набора битов, даже если семантические значения могут изменяться, как показывает то, как u8::MAX становится -1.


Все это, как говорится, не означаетчто нет лучших способов достичь своей цели.Как Лейни упоминает , вы можете создать черту для интересных типов и скрыть небезопасный код внутри реализаций.Это позволяет вашим пользователям избегать небезопасной работы.

Вы также можете использовать макрос для создания нескольких похожих функций, фактически не делая их одинаковыми.

Вероятно, существует и более высокий уровень решения вашей реальной проблемы.Почему вы должны изменить данные здесь?Это кажется очень необычным для сериализации.

3 голосов
/ 15 марта 2019

Вы не можете сделать это безопасно. Если у вас есть &mut ссылка на u8, код, использующий его, может обрабатывать его только как u8, и вам понадобится небезопасный код для выполнения преобразований.

as работает с примитивными числовыми значениями, поскольку копирует данные в память и может позаботиться о любых преобразованиях в этой точке. Это не будет работать со ссылкой, потому что не может изменить исходное значение.

Если вы абсолютно не можете изменить сигнатуру thing.serialize_char, и , вы не можете изменить тип переменной test, обходной путь - использовать другую переменную, а затем обновить оригинал потом:

let mut test: i8 = 0; 

let mut test_u8 = test as u8;
thing.serialize_char(&mut test_u8);
test = test_u8 as i8;

Это не будет работать, если serialize_char требуется ссылка дольше, чем вызов функции - но если это так, то средство проверки заимствований скоро сообщит вам!

1 голос
/ 15 марта 2019

Черты в Rust могут быть общими, поэтому вы можете определить черту с сигнатурой следующим образом:

trait SerializeChar<T> {
    fn serialize_char(&mut self, value: &mut T);
}

, а затем реализовать ее для своей структуры с помощью T = u8 и T = i8:

struct Thing {}

impl SerializeChar<u8> for Thing {
    fn serialize_char(&mut self, value: &mut u8) { *value = 55; }
}

impl SerializeChar<i8> for Thing {
    fn serialize_char(&mut self, value: &mut i8) { *value = 56; }
}

тест:

fn main() {
    let mut a = 0u8;
    let mut b = 0i8;
    let mut thing = Thing{};
    thing.serialize_char(&mut a);
    thing.serialize_char(&mut b);
    dbg!(a);
    dbg!(b);
}
...