Комментарии 16
Видимо по началу идею статью не понял, заметил, что хотите сами. Но какой в этом смысл? Не проще сделалить ли действительно конечный продукт. Разве только разобраться как оно устроено в ядре.
Советую еще почитать этот цикл статей habrahabr.ru/post/248153, тоже очень интересно и сам автор может многое почерпнуть.
В том числе, как сделать метод отрисовки линии менее страшным :)
В том числе, как сделать метод отрисовки линии менее страшным :)
Хорошая статья, вы молодцы. «Огреть тяжёлым» не хочется :)
Не совсем так. Для подключения внешней библиотеки достаточно написать
Конкретно в данном случае вам вообще не нужны lifetime-параметры — см. ниже.
У rust-sdl2 действительно проблема с документацией. Та, что доступна по ссылке на rust-ci, очень устаревшая. В этом случае самый правильный способ получить актуальную документацию — склонировать репозиторий и запустить там
И уже из документации будет понятно, зачем вообще нужны все эти лайфтайм-параметры.
Таким образом, вы можете описать вашу структуру как
и убрать все параметры из ваших функций.
любое объявление extern crate package_name в библиотеке должно быть также продублировано в main.rs приложения
Не совсем так. Для подключения внешней библиотеки достаточно написать
extern crate whatver;
только в корне crate'а, т.е. в вашем случае в main.rs. В остальных модулях внешнюю библиотеку можно просто use
'ать, как любой другой модуль.Особого упоминания заслуживает такая штука, как время жизни (lifetime) в Rust. С ней я долго боролся. Вообще говоря в Rust у каждой переменной и ссылки есть свой lifetime. Просто он выводится компилятором автоматически на основе определенных правил. Однако иногда его требуется указывать явно. Чтение статьи про время жизни из книги раста ничего для меня не прояснило. (хотя я ее 3 раза перечитал) Я так и не понял, почему же в моем случае Rust попросил указать lifetime. По сути я просто добавил эти странные <'_> везде, где компилятор указывал на ошибку с неуказанным lifetime, так и не разобравшись, зачем же ему это от меня было надо. Если есть знающие люди, буду рад, если просветите в комментариях. Почему именно подчерк, а не какой-то другой знак после апострофа? Просто в сообщении об ошибке про несоответствие типов было sdl2::render::Renderer<'_>. Поначалу я пытался обозначать поле как просто renderer: Renderer, но компилятор меня ругал: error: wrong number of lifetime parameters: expected 1, found 0
'_
— это обозначение анонимного lifetime-параметра, используемое внутри компилятора. На самом деле то, что сейчас можно писать '_
и всё будет работать — это в некотором роде недосмотр, и есть RFC на эту тему, которое, если будет принято, изменит семантику '_
. Именно подчерк просто потому, что так компилятор обозначает анонимные lifetime'ы.Конкретно в данном случае вам вообще не нужны lifetime-параметры — см. ниже.
У rust-sdl2 действительно проблема с документацией. Та, что доступна по ссылке на rust-ci, очень устаревшая. В этом случае самый правильный способ получить актуальную документацию — склонировать репозиторий и запустить там
cargo doc
:% git clone https://github.com/AngryLawyer/rust-sdl2
% cd rust-sdl2
% cargo doc
% open target/doc/sdl2/index.html
И уже из документации будет понятно, зачем вообще нужны все эти лайфтайм-параметры.
sdl2::init().video().unwrap()
возвращает значение типа sdl2::Sdl
, которое позволяет, в частности, создавать новые окна. sdl_context.window(...).....unwrap()
возвращает значение типа sdl2::video::Window
. Из окна можно создать renderer для этого окна, и это делает window.renderer().build().unwrap()
— этот код возвращает значение типа sdl2::video::Renderer<'static>
. Внимание на lifetime-параметр — он равен 'static
. Это специальный lifetime, обозначающий нечто, что может жить до конца всей программы. В данном случае 'static используется из-за того, что полученный renderer самодостаточен — он получен из поглощённого значения Window (есть другой случай, когда renderer создаётся из поверхности (surface), и тогда он связан с этой поверхностью с помощью этого lifetime-параметра) и не зависит от жизни никакого другого значения.Таким образом, вы можете описать вашу структуру как
pub struct Canvas {
renderer: Renderer<'static>,
sdl_context: Sdl,
xsize: u32,
ysize: u32,
}
и убрать все параметры из ваших функций.
Спасибо, что разъяснили. По мотивам вашего комментария внес правки в статью, чтобы не учить людей плохому.
Попытался использовать ваши советы, чтобы улучшить стиль кода. Со <'static > все сработало отлично. Спасибо.
А вот удаление
А вот удаление
extern crate sdl2;
из canvas.rs приводит к тому, что проект отказывается компилироваться с ошибкой error: failed to resolve. Use of undeclared type or module `sdl2`
. Получается, что extern'ы должны быть как в main.rs, так и в файлах модулей.Тут дело уже в ином. В качестве быстрофикса вам нужно заменить
let sdl_context = sdl2::init().video().unwrap();
наlet sdl_context = ::sdl2::init().video().unwrap();
(обратите внимание на ::
перед sdl2
).В Rust отдельные файлы — это отдельные модули. Когда вы в
Для того, чтобы не писать полные типы различных структур, вы знакомым образом используете выражение
1. Когда вы пишете путь к типу внутри выражения
2. Когда вы пишете путь к типу вне выражения
Что делать?
Я предложил добавить два двоеточия (
Второй вариант — написать
main.rs
написали mod canvas;
, вы определили новый модуль, который Rust ищет в canvas.rs
или canvas/mod.rs
.Для того, чтобы не писать полные типы различных структур, вы знакомым образом используете выражение
use foo::bar::something
. Тем самым вы добавляете something
в текущую область видимости. И здесь кроется одна маленькая, но важная деталь.1. Когда вы пишете путь к типу внутри выражения
use
, используется абсолютный путь. Например:use sdl2::render::Renderer;
означает, что мы поднимаемся до самого корня (mais.rs
), там мы находим extern crate sdl2;
, далее находим внутри контейнера модуль render
и внутри него — нечто с именем Renderer
.2. Когда вы пишете путь к типу вне выражения
use
, используется относительный путь. Например:let sdl_context = sdl2::init().video().unwrap();
означает, что Rust будет искать, что такое sdl2
в текущем модуле (canvas
, потому что мы в canvas.rs
). Потому и error: failed to resolve.
Что делать?
Я предложил добавить два двоеточия (
::
). Тем самым, мы сконвертировали относительный путь в абсолютный. Как и в первом случае, теперь, чтобы найти sdl2
, Rust будет подниматься до корня контейнера (main.rs
) и начинать искать sdl2
оттуда.Второй вариант — написать
use sdl2::init;
тогда можно будет использовать функцию init
из sdl2
.К сожалению, официальная книжка упоминает об этом вскользь, в двух последних абзацах (начиная с What about the
self
?). Мне в своё время очень помогло чьё-то неофициальное объяснение модулей: Rust's Modules are Weird :) А потом можно почитать соответствующее обсуждение на reddit.Нет, просто удалить extern crate недостаточно.
extern crate «погружает» указанный crate в глобальное пространство имён как модуль. Чтобы обращаться к нему, вам по-прежнему нужно его импортировать с помощью
extern crate «погружает» указанный crate в глобальное пространство имён как модуль. Чтобы обращаться к нему, вам по-прежнему нужно его импортировать с помощью
use
:use sdl2;
use sdl2::Sdl;
Да, кстати.
Ну так это и неудивительно, Rust не является объектно-ориентированным, поэтому ни классов, ни объектов в смысле ООП здесь нет :) есть trait objects, но это про другое.
В книге раста не было статей с названием Classes/Objects/Constructors и т. п.
Ну так это и неудивительно, Rust не является объектно-ориентированным, поэтому ни классов, ни объектов в смысле ООП здесь нет :) есть trait objects, но это про другое.
let p = yi*(x1-x2) — x1*y2 + x2*y1;
if xi*(y1-y2) < p + (y1-y2)/2 {
xi = xi+1;
}
self.set(xi, yi, color);
Это ад какой-то, если честно.
К примеру метод серединной точки
a = x_size/2;
for all x {
__setpixel(x,y);
__a -= y_size;
__if (a<0) {
___a += x_size;
___y++;
__}
__x++;
}
в стародавние времена на Z80 делали так:
L00:
SЕТ 7,(НL) ;pixel x=0, y=0
SUВ Е ;a -= size_y
JR C,L11A ;if (a < 0) jump L11A
L01:
SЕТ 6,(НL) ;pixel x=1, y=0
SUВ Е
JR C,L12A
L02:…
L10A:
ADD A,D ;a += size_x
INC Н ;y++;
JР L10 ;jump to pixel 0, line 1
L11A:
ADD A,D
INC Н
JР L11
…
L10:
SЕТ 7,(НL) ;pixel x=0, y=1
SUВ Е
JR C,L21A
развёрнутый цикл, выполняющий 3/6 инструкций на пиксель
(не хватает кармы на тег «code», уж извиняйте)
if xi*(y1-y2) < p + (y1-y2)/2 {
xi = xi+1;
}
self.set(xi, yi, color);
Это ад какой-то, если честно.
К примеру метод серединной точки
a = x_size/2;
for all x {
__setpixel(x,y);
__a -= y_size;
__if (a<0) {
___a += x_size;
___y++;
__}
__x++;
}
в стародавние времена на Z80 делали так:
L00:
SЕТ 7,(НL) ;pixel x=0, y=0
SUВ Е ;a -= size_y
JR C,L11A ;if (a < 0) jump L11A
L01:
SЕТ 6,(НL) ;pixel x=1, y=0
SUВ Е
JR C,L12A
L02:…
L10A:
ADD A,D ;a += size_x
INC Н ;y++;
JР L10 ;jump to pixel 0, line 1
L11A:
ADD A,D
INC Н
JР L11
…
L10:
SЕТ 7,(НL) ;pixel x=0, y=1
SUВ Е
JR C,L21A
развёрнутый цикл, выполняющий 3/6 инструкций на пиксель
(не хватает кармы на тег «code», уж извиняйте)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Пишем свой упрощенный OpenGL на Rust — часть 1 (рисуем линию)