InterruptedException — такого понятия в нативных потоках нет в принципе, поэтому если оно вам нужно, то придётся делать вручную.
OutOfMemoryException — обработка этой ошибки — это прерогатива аллокатора. Дефолтный аллокатор (тот, что в libstd) сейчас паникует (или завершает программу, не помню точно — в любом случае, обработать его нельзя). В 99% случаев в прикладных программах это удовлетворительное поведение. Если вы пишите что-то близкое к железу, то там вы можете реализовать свои способы аллокации памяти (отключив libstd) и самостоятельно обрабатывать такие ошибки.
Здесь connection.begin() возвращает объект, у которого в деструкторе транзакция отменяется. Если этот объект выходит из области видимости, то транзакция отменяется. Макрос try!() как раз обеспечит ранний выход из области видимости. Метод commit() «поглощает» объект (принимает по значению и перемещает в себя), выполняя коммит транзакции. Поскольку txn уходит «во внутрь» метода, он не будет уничтожен прямо здесь, и роллбэка не произойдёт.
А как в Rust с управлением памятью? Есть GC?
Собственно, безопасное управление памятью без GC — это одна из ключевых фичей Rust. Если кратко, то управление памятью осуществляется за счёт умных указателей, гарантии которых невозможно нарушить из-за концепций владения и заимствования (например, вы не сможете получить ссылку на внутренность какого-нибудь умного указателя и затем уничтожить сам указатель — компилятор вам не даст). В частности, в стандартной библиотеке есть Rc, который является умным указателем с подсчётом ссылок (и его аналог, Arc, который использует атомарные операции — он медленее, но зато потокобезопасный).
Умного указателя с полноценным GC нету, фактически, только потому, что никто пока что не потрудился его сделать. Как выяснилось на практике, почти всегда без сборки мусора можно обойтись и совершенно не потерять в удобстве.
Большинство движков регулярных выражений, которые я знаю, не меняют исходную строку при замене — они создают новую на основе предыдущей. Не говоря уже о том, что в большинстве современных языков строки неизменяемые. Поэтому проблемы перестроения таблицы не существует.
Я не понимаю, к чему вы ведёте. Напомню, с чего началась дискуссия. Gorthauer87 написал:
ведь если есть символ b, а нужно его заменить на Б, то начнётся веселая чехарда: придется или в лучшем случае все символы за b сдвигать на разницу в длине или же вообще заново выделять память
Я ответил, что в общем случае замена code point'ов один к одному не работает, даже простейшие операции вроде toupper() приводят к изменению общего количества code point'ов.
Особенно это важно в случаях, когда идёт работа с пользователем (например, в текстовом редакторе), потому что в этом случае вам придётся работать с графемными кластерами в качестве символов, а длина кластеров может быть любой — поэтому в любом случае корректный код должен уметь заменять подстроки на подстроки произвольной длины, и ценность «быстрой» замены отдельных «символов» стремится к нулю.
если мы работаем с текстом как с массивом символов
Именно про это я и написал в комментарии, на который вы ответили — такая работа с текстом очень редка, в частности, потому, что в случае юникода «массив символов» в смысле «массив code point'ов» бесполезен практически для любого практического применения.
По поводу регулярок.
В общем случае нужно определиться, с чем работают регулярные выражения. Как правило, они работают над code point'ами (корректность такого подхода в общем случае можно оспорить, ну да ладно). В таком случае есть два варианта — либо переводить строки в UTF-32 внутри движка и работать с фиксированной длиной encoded character'а, либо нужно просто построить таблицу соответствия «номер encoded character'а -> смещение от начала строки в code unit'ах» и работать с ним, что тоже даст O(1) доступ по номеру code point'а в UTF-8.
Кстати, второй вариант отлично подходит для регулярок на графемных кластерах — просто будет номер кластера а не encoded character'а.
Вы понимаете, что ß — это один code point, а SS (его toupper-версия) — это два? Здесь в принципе невозможно получить соответствие 1:1 для code point'ов, вне зависимости от используемых типов.
Я время от времени думаю, почему в Rust не хотят сделать аннотаций «не выделяет память», чтобы как раз помечать функции
На самом деле, кода в Rust ещё использовалась концепция typestate, почти так оно и было. Потом от typestate отказались, потому что она приводит к проблемам с композицей (вот здесь есть ещё информация). Та же самая штука была с pure-функциями (они тоже были, и от них тоже отказались).
Уже много раз было сказано — «посимвольная» индексация строк, что бы в данном контексте «символ» не обозначал, — это совершенно бессмысленное действие. В 99% случаев строки в программах никогда не индексируются. Там же, где они индексируются, в 99% случаев это должна быть «семантическая» индексация, на уровне графемных кластеров, а в этом случае вы в принципе не сможете обеспечить индексацию за постоянное время, потому что графемные кластеры могут быть произвольной длины, безотносительно используемых кодировок.
Для всех оставшихся случаев (когда вам зачем-то нужна быстрая индексация по code point'ам) — есть UTF-32.
Я не знаю, почему вы решили, что меня, что-то путает. Именно это я и имел в виду — абстрактный символ как понятие отделено от кодировки, а из-за того, что в общем случае преобразования символов не имеют вида «один к одному» (как, например, toupper(ß)=SS), завязываться на то, что в программе каждый символ закодирован фиксированным количеством байт — глупо и неправильно.
По Rust есть целая большая официальная книга, на главу которой про макросы выше привели ссылку. И Rust, кстати, всё-таки по синтаксису гораздо ближе к C, чем Nim или Nemerle.
Ну в Maven Central без подписи загрузить библиотеку невозможно. Да, сами сборочные инструменты подписей не проверяют, но я более чем уверен что в Gradle или SBT использовать соответствующий плагин возможно (или написать самостоятельно, если его ещё нет). Вот, например.
Этот вывод — ошибочный в плане работы с юникодом. В UTF-8 U+10904 представляется четырьмя байтами F0, 90, A4, 84, а EFBFBD — это UTF-8-представление символа U+FFFD (вот этого).
Собственно, это явно написано по той ссылке, которую вы привели:
The reference implementation just replaces any character in the range \U010000 — \U10ffff with \ufffd, but as soon as Tcl has support for characters outside the BMP this range is reserved for exactly that.
Таким образом, тикль официально не поддерживает не-BMP-code point'ы, по крайней мере, на данный момент, как раз из-за своего, скажем так, legacy-внутреннего представления строк. Кроме того, насколько я понимаю, поддержки суррогатных пар тоже нет, поэтому с не-BMP-символами работать вообще нельзя ни в каком виде.
Собственно, вот этот TIP внушает надежду на то, что это всё-таки когда-нибудь будет реализовано.
Да, вы правы, прошу прощения. Я сначала тоже написал «до 4», но потом посмотрел на таблицу в вики (с примерами для разных длин) и исправил на 6, видимо, зря.
Окей, насчёт ASCII я ошибся (меня смутила фраза «but only handles a restricted range of characters» в документации). Видимо, там имеется в виду именно произвольная последовательность байт.
Внутреннее же представление в utf-8 (которое есть default) никоим образом не ущербно. Обыкновенный utf-8 как оно есть везде.
Документация с вами не согласна:
modified UTF-8 (which uses 1 to 3 bytes per character)
тогда как в настоящем UTF-8 на один code point может уйти до 6 байт. Поэтому во внутреннем представлении в любом случае понадобятся суррогатные пары или их аналог. Я попытался посмотреть, как в тикле будет во внутреннем представлении выглядеть символ U+10904, однако у меня не получилось:
proc bin2hex {bin} {
binary scan $bin H* hex
return $hex
}
set txt [здесь должен быть явный символ U+10904, но хабр, похоже, не умеет в юникод :(]
puts "Внутреннее представление: ($txt), ([bin2hex $txt]), длина: [string length $txt]"
set bin [encoding convertto utf-8 $txt]
puts "В UTF-8: ($bin), ([bin2hex $txt]), длина: [string length $txt]"
И txt, и bin, судя по выводу bin2hex, содержат одно и то же, но puts выводит их по-разному (и оба раза криво), и string length для обеих равна 4 (что означает, что это не строки во внутреннем представлении — если бы это было так, то их длина была бы 1, потому что это 1 code point).
И прошу прощения, вероятно, я действительно не понял цель вашего примера. Однако, на мой взгляд, отсутствие строгой типизации (или, по крайней мере, строгих операций для работы с разными типами строк, как в Python 3), может сильно запутать логику происходящего, что, собственно, и произошло здесь в моём случае.
Кстати, а в каких языках будет много кодпойнтов на юнит?
Это некорректный вопрос, на code unit не может быть много code point'ов. Наоборот — может быть. Я больше имел в виду тексты с диакритикой.
Да, для подавляющего большинства операций хватит UTF-8 строк. А всякие адаптивные вещи — это настолько экзотика, что я даже не знаю, где это может пригодиться.
Можно, вероятно, будет придумать такую строку, которая будет всё портить.
Да, примерно это я и имел в виду. Любую строку можно разбить на графемные кластеры, взять длину максимального из них за длину code unit'а, и перекодировать строку так, чтобы каждый кластер занимал фиксированное (не больше максимального) число байт. Но, как мне кажется, это настолько специфический случай, что я даже не могу представить, где это может понадобиться.
Почти всё то, что вы описали — это не кодировка, это способ работы с символьными данными. Кодировка — это байтовое представление символьных данных, и какую-то из них всё равно придётся выбрать (либо даже несколько и преобразовывать их по каким-то критериям на лету, как это делает, по-видимому, Tcl, хотя нужность этого весьма сомнительна).
можно иметь априорное знание о графемных кластерах
Нельзя. Графемные кластеры могут быть произвольными. Ограничивать их длину малым количеством code point'ов нельзя — многие валидные тексты в неанглийских языках отсекутся. Большая граница сделает размер code unit'а очень большим (десятки байт на code unit?).
можно хранить указатели или индексы
Это не часть кодировки.
В-третьих, можно перестраивать представление «на лету»
Да, можно, но этим вы не добьётесь того, что любому символу (в смысле графемному кластеру) соответствует фиксированное количество байт.
Просто всё дело в том, что в юникоде нет взаимооднозначного соответствия между абстрактными символами и code point'ами. Более того, нет даже однозначного количества code point'ов, которые требуются для представления абстрактных символов. Любые попытки работать с юникодным текстом в любой кодировке так, как будто каждый символ занимает фиксированное число байт, обречены на провал.
OutOfMemoryException — обработка этой ошибки — это прерогатива аллокатора. Дефолтный аллокатор (тот, что в libstd) сейчас паникует (или завершает программу, не помню точно — в любом случае, обработать его нельзя). В 99% случаев в прикладных программах это удовлетворительное поведение. Если вы пишите что-то близкое к железу, то там вы можете реализовать свои способы аллокации памяти (отключив libstd) и самостоятельно обрабатывать такие ошибки.
Здесь
connection.begin()возвращает объект, у которого в деструкторе транзакция отменяется. Если этот объект выходит из области видимости, то транзакция отменяется. Макросtry!()как раз обеспечит ранний выход из области видимости. Методcommit()«поглощает» объект (принимает по значению и перемещает в себя), выполняя коммит транзакции. Поскольку txn уходит «во внутрь» метода, он не будет уничтожен прямо здесь, и роллбэка не произойдёт.Собственно, безопасное управление памятью без GC — это одна из ключевых фичей Rust. Если кратко, то управление памятью осуществляется за счёт умных указателей, гарантии которых невозможно нарушить из-за концепций владения и заимствования (например, вы не сможете получить ссылку на внутренность какого-нибудь умного указателя и затем уничтожить сам указатель — компилятор вам не даст). В частности, в стандартной библиотеке есть Rc, который является умным указателем с подсчётом ссылок (и его аналог, Arc, который использует атомарные операции — он медленее, но зато потокобезопасный).
Умного указателя с полноценным GC нету, фактически, только потому, что никто пока что не потрудился его сделать. Как выяснилось на практике, почти всегда без сборки мусора можно обойтись и совершенно не потерять в удобстве.
Я ответил, что в общем случае замена code point'ов один к одному не работает, даже простейшие операции вроде toupper() приводят к изменению общего количества code point'ов.
Особенно это важно в случаях, когда идёт работа с пользователем (например, в текстовом редакторе), потому что в этом случае вам придётся работать с графемными кластерами в качестве символов, а длина кластеров может быть любой — поэтому в любом случае корректный код должен уметь заменять подстроки на подстроки произвольной длины, и ценность «быстрой» замены отдельных «символов» стремится к нулю.
Именно про это я и написал в комментарии, на который вы ответили — такая работа с текстом очень редка, в частности, потому, что в случае юникода «массив символов» в смысле «массив code point'ов» бесполезен практически для любого практического применения.
По поводу регулярок.
В общем случае нужно определиться, с чем работают регулярные выражения. Как правило, они работают над code point'ами (корректность такого подхода в общем случае можно оспорить, ну да ладно). В таком случае есть два варианта — либо переводить строки в UTF-32 внутри движка и работать с фиксированной длиной encoded character'а, либо нужно просто построить таблицу соответствия «номер encoded character'а -> смещение от начала строки в code unit'ах» и работать с ним, что тоже даст O(1) доступ по номеру code point'а в UTF-8.
Кстати, второй вариант отлично подходит для регулярок на графемных кластерах — просто будет номер кластера а не encoded character'а.
На самом деле, кода в Rust ещё использовалась концепция typestate, почти так оно и было. Потом от typestate отказались, потому что она приводит к проблемам с композицей (вот здесь есть ещё информация). Та же самая штука была с pure-функциями (они тоже были, и от них тоже отказались).
Уже много раз было сказано — «посимвольная» индексация строк, что бы в данном контексте «символ» не обозначал, — это совершенно бессмысленное действие. В 99% случаев строки в программах никогда не индексируются. Там же, где они индексируются, в 99% случаев это должна быть «семантическая» индексация, на уровне графемных кластеров, а в этом случае вы в принципе не сможете обеспечить индексацию за постоянное время, потому что графемные кластеры могут быть произвольной длины, безотносительно используемых кодировок.
Для всех оставшихся случаев (когда вам зачем-то нужна быстрая индексация по code point'ам) — есть UTF-32.
Да, по-видимому, всё именно так :)
В общем, я удовлетворил своё любопытство по поводу тикля, спасибо большое за содержательную дискуссию!
Собственно, это явно написано по той ссылке, которую вы привели:
Таким образом, тикль официально не поддерживает не-BMP-code point'ы, по крайней мере, на данный момент, как раз из-за своего, скажем так, legacy-внутреннего представления строк. Кроме того, насколько я понимаю, поддержки суррогатных пар тоже нет, поэтому с не-BMP-символами работать вообще нельзя ни в каком виде.
Собственно, вот этот TIP внушает надежду на то, что это всё-таки когда-нибудь будет реализовано.
Документация с вами не согласна:
тогда как в настоящем UTF-8 на один code point может уйти до 6 байт. Поэтому во внутреннем представлении в любом случае понадобятся суррогатные пары или их аналог. Я попытался посмотреть, как в тикле будет во внутреннем представлении выглядеть символ U+10904, однако у меня не получилось:
И txt, и bin, судя по выводу bin2hex, содержат одно и то же, но puts выводит их по-разному (и оба раза криво), и string length для обеих равна 4 (что означает, что это не строки во внутреннем представлении — если бы это было так, то их длина была бы 1, потому что это 1 code point).
И прошу прощения, вероятно, я действительно не понял цель вашего примера. Однако, на мой взгляд, отсутствие строгой типизации (или, по крайней мере, строгих операций для работы с разными типами строк, как в Python 3), может сильно запутать логику происходящего, что, собственно, и произошло здесь в моём случае.
Это некорректный вопрос, на code unit не может быть много code point'ов. Наоборот — может быть. Я больше имел в виду тексты с диакритикой.
Да, для подавляющего большинства операций хватит UTF-8 строк. А всякие адаптивные вещи — это настолько экзотика, что я даже не знаю, где это может пригодиться.
Да, примерно это я и имел в виду. Любую строку можно разбить на графемные кластеры, взять длину максимального из них за длину code unit'а, и перекодировать строку так, чтобы каждый кластер занимал фиксированное (не больше максимального) число байт. Но, как мне кажется, это настолько специфический случай, что я даже не могу представить, где это может понадобиться.
В целом же у нас с вами, как я вижу, консенсус)
Нельзя. Графемные кластеры могут быть произвольными. Ограничивать их длину малым количеством code point'ов нельзя — многие валидные тексты в неанглийских языках отсекутся. Большая граница сделает размер code unit'а очень большим (десятки байт на code unit?).
Это не часть кодировки.
Да, можно, но этим вы не добьётесь того, что любому символу (в смысле графемному кластеру) соответствует фиксированное количество байт.
Просто всё дело в том, что в юникоде нет взаимооднозначного соответствия между абстрактными символами и code point'ами. Более того, нет даже однозначного количества code point'ов, которые требуются для представления абстрактных символов. Любые попытки работать с юникодным текстом в любой кодировке так, как будто каждый символ занимает фиксированное число байт, обречены на провал.