speice.io/2018/06/dateutil-parser-to-rust/index.html

142 lines
30 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html><html lang=en dir=ltr class="blog-wrapper blog-post-page plugin-blog plugin-id-default" data-has-hydrated=false><meta charset=UTF-8><meta name=generator content="Docusaurus v3.6.0"><title data-rh=true>What I learned porting dateutil to Rust | The Old Speice Guy</title><meta data-rh=true name=viewport content="width=device-width,initial-scale=1.0"><meta data-rh=true name=twitter:card content=summary_large_image><meta data-rh=true property=og:url content=https://speice.io/2018/06/dateutil-parser-to-rust><meta data-rh=true property=og:locale content=en><meta data-rh=true name=docusaurus_locale content=en><meta data-rh=true name=docusaurus_tag content=default><meta data-rh=true name=docsearch:language content=en><meta data-rh=true name=docsearch:docusaurus_tag content=default><meta data-rh=true property=og:title content="What I learned porting dateutil to Rust | The Old Speice Guy"><meta data-rh=true name=description content="I've mostly been a lurker in Rust for a while, making a couple small contributions here and there."><meta data-rh=true property=og:description content="I've mostly been a lurker in Rust for a while, making a couple small contributions here and there."><meta data-rh=true property=og:type content=article><meta data-rh=true property=article:published_time content=2018-06-25T12:00:00.000Z><link data-rh=true rel=icon href=/img/favicon.ico><link data-rh=true rel=canonical href=https://speice.io/2018/06/dateutil-parser-to-rust><link data-rh=true rel=alternate href=https://speice.io/2018/06/dateutil-parser-to-rust hreflang=en><link data-rh=true rel=alternate href=https://speice.io/2018/06/dateutil-parser-to-rust hreflang=x-default><script data-rh=true type=application/ld+json>{"@context":"https://schema.org","@id":"https://speice.io/2018/06/dateutil-parser-to-rust","@type":"BlogPosting","author":{"@type":"Person","name":"Bradlee Speice"},"dateModified":"2024-11-10T01:23:31.000Z","datePublished":"2018-06-25T12:00:00.000Z","description":"I've mostly been a lurker in Rust for a while, making a couple small contributions here and there.","headline":"What I learned porting dateutil to Rust","isPartOf":{"@id":"https://speice.io/","@type":"Blog","name":"Blog"},"keywords":[],"mainEntityOfPage":"https://speice.io/2018/06/dateutil-parser-to-rust","name":"What I learned porting dateutil to Rust","url":"https://speice.io/2018/06/dateutil-parser-to-rust"}</script><link rel=alternate type=application/rss+xml href=/rss.xml title="The Old Speice Guy RSS Feed"><link rel=alternate type=application/atom+xml href=/atom.xml title="The Old Speice Guy Atom Feed"><link rel=stylesheet href=https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css integrity=sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM crossorigin><link rel=stylesheet href=/assets/css/styles.ae6ff4a3.css><script src=/assets/js/runtime~main.751b419d.js defer></script><script src=/assets/js/main.62ce6156.js defer></script><body class=navigation-with-keyboard><script>!function(){var t,e=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();t=null!==e?e:"light",document.documentElement.setAttribute("data-theme",t)}(),function(){try{for(var[t,e]of new URLSearchParams(window.location.search).entries())if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id=__docusaurus><div role=region aria-label="Skip to main content"><a class=skipToContent_fXgn href=#__docusaurus_skipToContent_fallback>Skip to main content</a></div><nav aria-label=Main class="navbar navbar--fixed-top"><div class=navbar__inner><div class=navbar__items><button aria-label="Toggle navigation bar" aria-expanded=false class="navbar__toggle clean-btn" type=button><svg width=30 height=30 viewBox="0 0 30 30" aria-hidden=true><path stroke=currentColor stroke-linecap=round stroke-miterlimit=10 stroke-width=2 d="M4 7h22M4 15h22M4 23h22"/></svg></button><a class=navbar__brand href=/><div class=navbar__logo><img src=/img/logo.svg alt="Sierpinski Gasket" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src=/img/logo-dark.svg alt="Sierpinski Gasket" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><b class="navbar__title text--truncate">The Old Speice Guy</b></a></div><div class="navbar__items navbar__items--right"><a href=https://github.com/bspeice target=_blank rel="noopener noreferrer" class="navbar__item navbar__link header-github-link"></a><div class="toggle_vylO colorModeToggle_DEke"><button class="clean-btn toggleButton_gllP toggleButtonDisabled_aARS" type=button disabled title="Switch between dark and light mode (currently light mode)" aria-label="Switch between dark and light mode (currently light mode)" aria-live=polite aria-pressed=false><svg viewBox="0 0 24 24" width=24 height=24 class=lightToggleIcon_pyhR><path fill=currentColor d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg><svg viewBox="0 0 24 24" width=24 height=24 class=darkToggleIcon_wfgR><path fill=currentColor d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg></button></div><div class=navbarSearchContainer_Bca1><div class=navbar__search><span aria-label="expand searchbar" role=button class=search-icon tabindex=0></span><input id=search_input_react type=search placeholder=Loading... aria-label=Search class="navbar__search-input search-bar" disabled></div></div></div></div><div role=presentation class=navbar-sidebar__backdrop></div></nav><div id=__docusaurus_skipToContent_fallback class="main-wrapper mainWrapper_z2l0"><div class="container margin-vert--lg"><div class=row><aside class="col col--3"><nav class="sidebar_re4s thin-scrollbar" aria-label="Blog recent posts navigation"><div class="sidebarItemTitle_pO2u margin-bottom--md">All posts</div><div role=group><h3>2022</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2011/11/webpack-industrial-complex>The webpack industrial complex</a></ul></div><div role=group><h3>2019</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/12/release-the-gil>Release the GIL</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/09/binary-format-shootout>Binary format shootout</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/06/high-performance-systems>On building high performance systems</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/05/making-bread>Making bread</a></ul><div role=group><h4>Allocations in Rust</h4><ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/understanding-allocations-in-rust>Foreword</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/the-whole-world>Global memory</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/stacking-up>Fixed memory</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/a-heaping-helping>Dynamic memory</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/08/compiler-optimizations>Compiler optimizations</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2019/02/summary>Summary</a></ul></ul></div></div><div role=group><h3>2018</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/12/allocation-safety>QADAPT - debug_assert! for allocations</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/12/what-small-business-really-means>More "what companies really mean"</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/10/case-study-optimization>A case study in heaptrack</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/09/isomorphic-apps>Isomorphic desktop apps with Rust</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/09/primitives-in-rust-are-weird>Primitives in Rust are weird (and cool)</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a aria-current=page class="sidebarItemLink_mo7H sidebarItemLinkActive_I1ZP" href=/2018/06/dateutil-parser-to-rust>What I learned porting dateutil to Rust</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/05/hello>Hello!</a></ul><div role=group><h4>Captain's Cookbook</h4><ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/01/captains-cookbook-part-1>Project setup</a><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2018/01/captains-cookbook-part-2>Practical usage</a></ul></ul></div></div><div role=group><h3>2016</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/11/pca-audio-compression>PCA audio compression</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/10/rustic-repodcasting>A Rustic re-podcasting server</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/06/event-studies-and-earnings-releases>Event studies and earnings releases</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/05/the-unfair-casino>The unfair casino</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/04/tick-tock>Tick tock...</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/03/tweet-like-me>Tweet like me</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/03/predicting-santander-customer-happiness>Predicting Santander customer happiness</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/02/profitability-using-the-investment-formula>Profitability using the investment formula</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/02/guaranteed-money-maker>Guaranteed money maker</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/01/cloudy-in-seattle>Cloudy in Seattle</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2016/01/complaining-about-the-weather>Complaining about the weather</a></ul></div><div role=group><h3>2015</h3><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2015/12/testing-cramer>Testing Cramer</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2015/11/autocallable>Autocallable Bonds</a></ul><ul class="sidebarItemList_Yudw clean-list"><li class=sidebarItem__DBe><a class=sidebarItemLink_mo7H href=/2015/11/welcome>Welcome, and an algorithm</a></ul></div></nav></aside><main class="col col--7"><article><header><h1 class=title_f1Hy>What I learned porting dateutil to Rust</h1><div class="container_mt6G margin-vert--md"><time datetime=2018-06-25T12:00:00.000Z>June 25, 2018</time> · <!-- -->7 min read</div><div class="margin-top--md margin-bottom--sm row"><div class="col col--12 authorCol_Hf19"><div class="avatar margin-bottom--sm"><div class="avatar__intro authorDetails_lV9A"><div class=avatar__name><span class=authorName_yefp>Bradlee Speice</span></div><div class=authorSocials_rSDt><a href=https://github.com/bspeice target=_blank rel="noopener noreferrer" class=authorSocialLink_owbf title=GitHub><svg viewBox="0 0 256 250" width=1em height=1em class="authorSocialLink_owbf githubSvg_Uu4N" style=--dark:#000;--light:#fff preserveAspectRatio=xMidYMid><path d="M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z"/></svg></a></div></div></div></div></div></header><div id=__blog-post-container class=markdown><p>I've mostly been a lurker in Rust for a while, making a couple small contributions here and there.
So launching <a href=https://github.com/bspeice/dtparse target=_blank rel="noopener noreferrer">dtparse</a> feels like nice step towards becoming a
functioning member of society. But not too much, because then you know people start asking you to
pay bills, and ain't nobody got time for that.</p>
<p>But I built dtparse, and you can read about my thoughts on the process. Or don't. I won't tell you
what to do with your life (but you should totally keep reading).</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=slow-down-what>Slow down, what?<a href=#slow-down-what class=hash-link aria-label="Direct link to Slow down, what?" title="Direct link to Slow down, what?"></a></h2>
<p>OK, fine, I guess I should start with <em>why</em> someone would do this.</p>
<p><a href=https://github.com/dateutil/dateutil target=_blank rel="noopener noreferrer">Dateutil</a> is a Python library for handling dates. The
standard library support for time in Python is kinda dope, but there are a lot of extras that go
into making it useful beyond just the <a href=https://docs.python.org/3.6/library/datetime.html target=_blank rel="noopener noreferrer">datetime</a>
module. <code>dateutil.parser</code> specifically is code to take all the super-weird time formats people come
up with and turn them into something actually useful.</p>
<p>Date/time parsing, it turns out, is just like everything else involving
<a href=https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time target=_blank rel="noopener noreferrer">computers</a> and
<a href=https://infiniteundo.com/post/25509354022/more-falsehoods-programmers-believe-about-time target=_blank rel="noopener noreferrer">time</a>: it
feels like it shouldn't be that difficult to do, until you try to do it, and you realize that people
suck and this is why
<a href=https://zachholman.com/talk/utc-is-enough-for-everyone-right target=_blank rel="noopener noreferrer">we can't we have nice things</a>. But
alas, we'll try and make contemporary art out of the rubble and give it a pretentious name like
<em>Time</em>.</p>
<p><img decoding=async loading=lazy alt="A gravel mound" src=/assets/images/gravel-mound-4afad8bdb1cd6b0e40dd2fd41adca36f.jpg width=800 height=374 class=img_ev3q></p>
<blockquote>
<p><a href=https://www.goodfreephotos.com/united-states/montana/elkhorn/remains-of-the-mining-operation-elkhorn.jpg.php target=_blank rel="noopener noreferrer">Time</a></p>
</blockquote>
<p>What makes <code>dateutil.parser</code> great is that there's single function with a single argument that
drives what programmers interact with:
<a href=https://github.com/dateutil/dateutil/blob/6dde5d6298cfb81a4c594a38439462799ed2aef2/dateutil/parser/_parser.py#L1258 target=_blank rel="noopener noreferrer"><code>parse(timestr)</code></a>.
It takes in the time as a string, and gives you back a reasonable "look, this is the best anyone can
possibly do to make sense of your input" value. It doesn't expect much of you.</p>
<p><a href=https://github.com/bspeice/dtparse/blob/7d565d3a78876dbebd9711c9720364fe9eba7915/src/lib.rs#L1332 target=_blank rel="noopener noreferrer">And now it's in Rust.</a></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=lost-in-translation>Lost in Translation<a href=#lost-in-translation class=hash-link aria-label="Direct link to Lost in Translation" title="Direct link to Lost in Translation"></a></h2>
<p>Having worked at a bulge-bracket bank watching Java programmers try to be Python programmers, I'm
admittedly hesitant to publish Python code that's trying to be Rust. Interestingly, Rust code can
actually do a great job of mimicking Python. It's certainly not idiomatic Rust, but I've had better
experiences than
<a href="https://webcache.googleusercontent.com/search?q=cache:wkYMpktJtnUJ:https://jackstouffer.com/blog/porting_dateutil.html+&cd=3&hl=en&ct=clnk&gl=us" target=_blank rel="noopener noreferrer">this guy</a>
who attempted the same thing for D. These are the actual take-aways:</p>
<p>When transcribing code, <strong>stay as close to the original library as possible</strong>. I'm talking about
using the same variable names, same access patterns, the whole shebang. It's way too easy to make a
couple of typos, and all of a sudden your code blows up in new and exciting ways. Having a reference
manual for verbatim what your code should be means that you don't spend that long debugging
complicated logic, you're more looking for typos.</p>
<p>Also, <strong>don't use nice Rust things like enums</strong>. While
<a href=https://github.com/bspeice/dtparse/blob/7d565d3a78876dbebd9711c9720364fe9eba7915/src/lib.rs#L88-L94 target=_blank rel="noopener noreferrer">one time it worked out OK for me</a>,
I also managed to shoot myself in the foot a couple times because <code>dateutil</code> stores AM/PM as a
boolean and I mixed up which was true, and which was false (side note: AM is false, PM is true). In
general, writing nice code <em>should not be a first-pass priority</em> when you're just trying to recreate
the same functionality.</p>
<p><strong>Exceptions are a pain.</strong> Make peace with it. Python code is just allowed to skip stack frames. So
when a co-worker told me "Rust is getting try-catch syntax" I properly freaked out. Turns out
<a href=https://github.com/rust-lang/rfcs/pull/243 target=_blank rel="noopener noreferrer">he's not quite right</a>, and I'm OK with that. And while
<code>dateutil</code> is pretty well-behaved about not skipping multiple stack frames,
<a href=https://github.com/dateutil/dateutil/blob/16561fc99361979e88cccbd135393b06b1af7e90/dateutil/parser/_parser.py#L730-L865 target=_blank rel="noopener noreferrer">130-line try-catch blocks</a>
take a while to verify.</p>
<p>As another Python quirk, <strong>be very careful about
<a href=https://github.com/dateutil/dateutil/blob/16561fc99361979e88cccbd135393b06b1af7e90/dateutil/parser/_parser.py#L494-L568 target=_blank rel="noopener noreferrer">long nested if-elif-else blocks</a></strong>.
I used to think that Python's whitespace was just there to get you to format your code correctly. I
think that no longer. It's way too easy to close a block too early and have incredibly weird issues
in the logic. Make sure you use an editor that displays indentation levels so you can keep things
straight.</p>
<p><strong>Rust macros are not free.</strong> I originally had the
<a href=https://github.com/bspeice/dtparse/blob/b0e737f088eca8e83ab4244c6621a2797d247697/tests/compat.rs#L63-L217 target=_blank rel="noopener noreferrer">main test body</a>
wrapped up in a macro using <a href=https://github.com/PyO3/PyO3 target=_blank rel="noopener noreferrer">pyo3</a>. It took two minutes to compile.
After
<a href=https://github.com/bspeice/dtparse/blob/e017018295c670e4b6c6ee1cfff00dbb233db47d/tests/compat.rs#L76-L205 target=_blank rel="noopener noreferrer">moving things to a function</a>
compile times dropped down to ~5 seconds. Turns out 150 lines * 100 tests = a lot of redundant code
to be compiled. My new rule of thumb is that any macros longer than 10-15 lines are actually
functions that need to be liberated, man.</p>
<p>Finally, <strong>I really miss list comprehensions and dictionary comprehensions.</strong> As a quick comparison,
see
<a href=https://github.com/dateutil/dateutil/blob/16561fc99361979e88cccbd135393b06b1af7e90/dateutil/parser/_parser.py#L476 target=_blank rel="noopener noreferrer">this dateutil code</a>
and
<a href=https://github.com/bspeice/dtparse/blob/7d565d3a78876dbebd9711c9720364fe9eba7915/src/lib.rs#L619-L629 target=_blank rel="noopener noreferrer">the implementation in Rust</a>.
I probably wrote it wrong, and I'm sorry. Ultimately though, I hope that these comprehensions can be
added through macros or syntax extensions. Either way, they're expressive, save typing, and are
super-readable. Let's get more of that.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=using-a-young-language>Using a young language<a href=#using-a-young-language class=hash-link aria-label="Direct link to Using a young language" title="Direct link to Using a young language"></a></h2>
<p>Now, Rust is exciting and new, which means that there's opportunity to make a substantive impact. On
more than one occasion though, I've had issues navigating the Rust ecosystem.</p>
<p>What I'll call the "canonical library" is still being built. In Python, if you need datetime
parsing, you use <code>dateutil</code>. If you want <code>decimal</code> types, it's already in the
<a href=https://docs.python.org/3.6/library/decimal.html target=_blank rel="noopener noreferrer">standard library</a>. While I might've gotten away
with <code>f64</code>, <code>dateutil</code> uses decimals, and I wanted to follow the principle of <strong>staying as close to
the original library as possible</strong>. Thus began my quest to find a decimal library in Rust. What I
quickly found was summarized in a comment:</p>
<blockquote>
<p>Writing a BigDecimal is easy. Writing a <em>good</em> BigDecimal is hard.</p>
<p><a href=https://github.com/rust-lang/rust/issues/8937#issuecomment-34582794 target=_blank rel="noopener noreferrer">-cmr</a></p>
</blockquote>
<p>In practice, this means that there are at least <a href=https://crates.io/crates/bigdecimal target=_blank rel="noopener noreferrer">4</a>
<a href=https://crates.io/crates/rust_decimal target=_blank rel="noopener noreferrer">different</a>
<a href=https://crates.io/crates/decimal target=_blank rel="noopener noreferrer">implementations</a> <a href=https://crates.io/crates/decimate target=_blank rel="noopener noreferrer">available</a>.
And that's a lot of decisions to worry about when all I'm thinking is "why can't
<a href=https://en.wikipedia.org/wiki/Calendar_reform target=_blank rel="noopener noreferrer">calendar reform</a> be a thing" and I'm forced to dig
through a <a href=https://github.com/rust-lang/rust/issues/8937#issuecomment-31661916 target=_blank rel="noopener noreferrer">couple</a>
<a href=https://github.com/rust-lang/rfcs/issues/334 target=_blank rel="noopener noreferrer">different</a>
<a href=https://github.com/rust-num/num/issues/8 target=_blank rel="noopener noreferrer">threads</a> to figure out if the library I'm look at is dead
or just stable.</p>
<p>And even when the "canonical library" exists, there's no guarantees that it will be well-maintained.
<a href=https://github.com/chronotope/chrono target=_blank rel="noopener noreferrer">Chrono</a> is the <em>de facto</em> date/time library in Rust, and just
released version 0.4.4 like two days ago. Meanwhile,
<a href=https://github.com/chronotope/chrono-tz target=_blank rel="noopener noreferrer">chrono-tz</a> appears to be dead in the water even though
<a href=https://github.com/chronotope/chrono-tz/issues/19 target=_blank rel="noopener noreferrer">there are people happy to help maintain it</a>. I
know relatively little about it, but it appears that most of the release process is automated;
keeping that up to date should be a no-brainer.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=trial-maintenance-policy>Trial Maintenance Policy<a href=#trial-maintenance-policy class=hash-link aria-label="Direct link to Trial Maintenance Policy" title="Direct link to Trial Maintenance Policy"></a></h2>
<p>Specifically given "maintenance" being an
<a href=https://www.reddit.com/r/rust/comments/48540g/thoughts_on_initiators_vs_maintainers/ target=_blank rel="noopener noreferrer">oft-discussed</a>
issue, I'm going to try out the following policy to keep things moving on <code>dtparse</code>:</p>
<ol>
<li>
<p>Issues/PRs needing <em>maintainer</em> feedback will be updated at least weekly. I want to make sure
nobody's blocking on me.</p>
</li>
<li>
<p>To keep issues/PRs needing <em>contributor</em> feedback moving, I'm going to (kindly) ask the
contributor to check in after two weeks, and close the issue without resolution if I hear nothing
back after a month.</p>
</li>
</ol>
<p>The second point I think has the potential to be a bit controversial, so I'm happy to receive
feedback on that. And if a contributor responds with "hey, still working on it, had a kid and I'm
running on 30 seconds of sleep a night," then first: congratulations on sustaining human life. And
second: I don't mind keeping those requests going indefinitely. I just want to try and balance
keeping things moving with giving people the necessary time they need.</p>
<p>I should also note that I'm still getting some best practices in place - CONTRIBUTING and
CONTRIBUTORS files need to be added, as well as issue/PR templates. In progress. None of us are
perfect.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id=roadmap-and-conclusion>Roadmap and Conclusion<a href=#roadmap-and-conclusion class=hash-link aria-label="Direct link to Roadmap and Conclusion" title="Direct link to Roadmap and Conclusion"></a></h2>
<p>So if I've now built a <code>dateutil</code>-compatible parser, we're done, right? Of course not! That's not
nearly ambitious enough.</p>
<p>Ultimately, I'd love to have a library that's capable of parsing everything the Linux <code>date</code> command
can do (and not <code>date</code> on OSX, because seriously, BSD coreutils are the worst). I know Rust has a
coreutils rewrite going on, and <code>dtparse</code> would potentially be an interesting candidate since it
doesn't bring in a lot of extra dependencies. <a href=https://crates.io/crates/humantime target=_blank rel="noopener noreferrer"><code>humantime</code></a>
could help pick up some of the (current) slack in dtparse, so maybe we can share and care with each
other?</p>
<p>All in all, I'm mostly hoping that nobody's already done this and I haven't spent a bit over a month
on redundant code. So if it exists, tell me. I need to know, but be nice about it, because I'm going
to take it hard.</p>
<p>And in the mean time, I'm looking forward to building more. Onwards.</div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Blog post page navigation"><a class="pagination-nav__link pagination-nav__link--prev" href=/2018/05/hello><div class=pagination-nav__sublabel>Older post</div><div class=pagination-nav__label>Hello!</div></a><a class="pagination-nav__link pagination-nav__link--next" href=/2018/09/primitives-in-rust-are-weird><div class=pagination-nav__sublabel>Newer post</div><div class=pagination-nav__label>Primitives in Rust are weird (and cool)</div></a></nav></main><div class="col col--2"><div class="tableOfContents_bqdL thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href=#slow-down-what class="table-of-contents__link toc-highlight">Slow down, what?</a><li><a href=#lost-in-translation class="table-of-contents__link toc-highlight">Lost in Translation</a><li><a href=#using-a-young-language class="table-of-contents__link toc-highlight">Using a young language</a><li><a href=#trial-maintenance-policy class="table-of-contents__link toc-highlight">Trial Maintenance Policy</a><li><a href=#roadmap-and-conclusion class="table-of-contents__link toc-highlight">Roadmap and Conclusion</a></ul></div></div></div></div></div><footer class=footer><div class="container container-fluid"><div class="footer__bottom text--center"><div class=footer__copyright>Copyright © 2024 Bradlee Speice</div></div></div></footer></div>