Тайна о типах ржавчины, которую я не совсем понимаю.
Недавно я занимался рефакторингом кода для проекта и столкнулся с загадкой печатания, которая озадачила меня, и я надеюсь, что кто-то здесь сможет прояснить, что происходит
Я пишу программу для реализации алгоритма вырезания шва . «Вырезание шва» - это способ автоматического уменьшения размеров изображения путем нахождения для каждого пикселя в первой строке извилистого пути пикселей сверху вниз или слева направо, который будет наносить наименьший ущерб изображениюи затем удаляя этот «шов». Путь вычисляется из энергии каждого пикселя: вычисляется расстояние между цветами двух соседей этого пикселя, а затем определяется шов с наименьшей общей дельтой .
Итак, Изображение → Энергетическая карта → Шов → Уменьшенное изображение.
В памяти изображение представляет собой набор строк, соединенных вместе. Сканирование по вертикальному шву означает сканирование строк (левый и правый соседы образуют дельту), но сканирование по горизонтальному шву означает сканирование столбцов . Смежные пиксели в столбцах не смежны в памяти, и это затрудняет многопоточность с районным или поперечным пучком.
Хорошая новость заключается в том, что каждый источник читаетсятолько. Генерация энергетической карты требует доступа на запись только к энергетической карте. Для столбцов, если бы я мог вращать энергетическую карту, я мог бы выполнять многопоточное горизонтальное сканирование. Это оказывается легко, и все, что мне нужно, это прокси-объект для сопоставления (x, y)
с (y, x)
при чтении изображения. Давайте назовем этот прокси-объект Pivot
.
Позвольте мне показать некоторый код (AviShaOne - это первый алгоритм вырезания шва от Avidan & Shamir, «Seam Carving», 2007):
fn calculate_energy<I, P, S>(image: &I) -> TwoDimensionalMap<u32>
where
I: GenericImageView<Pixel = P>,
P: Pixel<Subpixel = S> + 'static,
S: Primitive + 'static,
{
...
}
impl<'a, I, P, S> AviShaOne<'a, I, P, S>
where
I: GenericImageView<Pixel = P>,
P: Pixel<Subpixel = S> + 'static,
S: Primitive + 'static,
{
fn find_vertical_seam(&self) -> Vec<u32> {
energy_to_seam(&calculate_energy(self.image))
}
fn find_horizontal_seam(&self) -> Vec<u32> {
energy_to_seam(&calculate_energy(&Pivot::new(self.image)))
}
}
Позвольте мне подчеркнуть: приведенный выше код работает . Pivot перераспределяет координаты, и теперь внутренние функции могут работать с объектами памяти с поддержкой потоков.
Если я хочу уменьшить изображение более чем на один шов, мне придется повторно выполнять этот расчет. Было бы неплохо, если бы я мог кэшировать карту энергии. (Поскольку шов меандрирует, и удаление этого шва может повредить швы, которые пересекают его, удаляемый столбец будет довольно большим, но все равно будет менее опасным, чем пересчет для каждого шва .) Я хочу добавитькарту энергии к struct
, и приведите calculate_energy
в impl
.
Вот где все становится странным:
impl<'a, I, P, S> AviShaOne<'a, I, P, S>
where
I: GenericImageView<Pixel = P>,
P: Pixel<Subpixel = S> + 'static,
S: Primitive + 'static,
{
fn calculate_energy(&self, image: &I) -> TwoDimensionalMap<u32>
{
...
}
fn find_horizontal_seam(&self) -> Vec<u32> {
energy_to_seam(&self.calculate_energy(&Pivot::new(self.image)))
}
}
Все, что я делал, это двигался calculate_energy
в реализацию. Никакой другой код не был изменен. Pivot
имеет такую же сигнатуру типа, что и AviShaOne
, так как они оба обрабатывают эти GenericImageView
объекты. И теперь он не скомпилируется:
176 | energy_to_seam(&self.calculate_energy(&Pivot::new(self.image)))
| ^^^^^^ expected type parameter, found struct `pivot::Pivot`
Это загадка. Функции буквально одинаковы. Типовые подписи, насколько я могу судить, конгруэнтны, буквально копируются и вставляются для согласованности. Я еще не добавил energy
как поле! Она работала нормально, когда функция была просто функцией, но теперь, когда она является членом реализации, компилятор хочет что-то еще, но я понятия не имею, что.
Чего хочет компилятор?