diff --git a/.all-contributorsrc b/.all-contributorsrc index 84b2365e..f2033dcd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,6 +1,6 @@ { "files": [ - "README.md" + "AUTHORS.md" ], "imageSize": 100, "commit": false, @@ -637,6 +637,911 @@ "contributions": [ "maintenance" ] + }, + { + "login": "pcn", + "name": "Peter N", + "avatar_url": "https://avatars2.githubusercontent.com/u/1056756?v=4", + "profile": "https://github.com/pcn", + "contributions": [ + "maintenance" + ] + }, + { + "login": "seancad", + "name": "seancad", + "avatar_url": "https://avatars1.githubusercontent.com/u/47405611?v=4", + "profile": "https://github.com/seancad", + "contributions": [ + "maintenance" + ] + }, + { + "login": "wsh", + "name": "Will Hayworth", + "avatar_url": "https://avatars3.githubusercontent.com/u/181174?v=4", + "profile": "http://willhayworth.com", + "contributions": [ + "content" + ] + }, + { + "login": "chrizel", + "name": "Christian Zeller", + "avatar_url": "https://avatars3.githubusercontent.com/u/20802?v=4", + "profile": "https://github.com/chrizel", + "contributions": [ + "content" + ] + }, + { + "login": "jfchevrette", + "name": "Jean-Francois Chevrette", + "avatar_url": "https://avatars.githubusercontent.com/u/3001?v=4", + "profile": "https://github.com/jfchevrette", + "contributions": [ + "content", + "code" + ] + }, + { + "login": "jbaber", + "name": "John Baber-Lucero", + "avatar_url": "https://avatars.githubusercontent.com/u/1908117?v=4", + "profile": "https://github.com/jbaber", + "contributions": [ + "content" + ] + }, + { + "login": "tal-zvon", + "name": "Tal", + "avatar_url": "https://avatars.githubusercontent.com/u/3195851?v=4", + "profile": "https://github.com/tal-zvon", + "contributions": [ + "content" + ] + }, + { + "login": "apogeeoak", + "name": "apogeeoak", + "avatar_url": "https://avatars.githubusercontent.com/u/59737221?v=4", + "profile": "https://github.com/apogeeoak", + "contributions": [ + "content", + "code" + ] + }, + { + "login": "Crell", + "name": "Larry Garfield", + "avatar_url": "https://avatars.githubusercontent.com/u/254863?v=4", + "profile": "http://www.garfieldtech.com/", + "contributions": [ + "content" + ] + }, + { + "login": "circumspect", + "name": "circumspect", + "avatar_url": "https://avatars.githubusercontent.com/u/40770208?v=4", + "profile": "https://github.com/circumspect", + "contributions": [ + "content" + ] + }, + { + "login": "cjwyett", + "name": "Cyrus Wyett", + "avatar_url": "https://avatars.githubusercontent.com/u/34195737?v=4", + "profile": "https://github.com/cjwyett", + "contributions": [ + "content" + ] + }, + { + "login": "cadolphs", + "name": "cadolphs", + "avatar_url": "https://avatars.githubusercontent.com/u/13894820?v=4", + "profile": "https://github.com/cadolphs", + "contributions": [ + "code" + ] + }, + { + "login": "hpwxf", + "name": "Pascal H.", + "avatar_url": "https://avatars.githubusercontent.com/u/26146722?v=4", + "profile": "https://www.haveneer.com", + "contributions": [ + "content" + ] + }, + { + "login": "chapeupreto", + "name": "Rod Elias", + "avatar_url": "https://avatars.githubusercontent.com/u/834048?v=4", + "profile": "https://twitter.com/chapeupreto", + "contributions": [ + "content" + ] + }, + { + "login": "blerchy", + "name": "Matt Lebl", + "avatar_url": "https://avatars.githubusercontent.com/u/2555355?v=4", + "profile": "https://github.com/blerchy", + "contributions": [ + "code" + ] + }, + { + "login": "flakolefluk", + "name": "Ignacio Le Fluk", + "avatar_url": "https://avatars.githubusercontent.com/u/11986564?v=4", + "profile": "http://flakolefluk.dev", + "contributions": [ + "content" + ] + }, + { + "login": "tlyu", + "name": "Taylor Yu", + "avatar_url": "https://avatars.githubusercontent.com/u/431873?v=4", + "profile": "https://github.com/tlyu", + "contributions": [ + "code", + "content" + ] + }, + { + "login": "Zerotask", + "name": "Patrick Hintermayer", + "avatar_url": "https://avatars.githubusercontent.com/u/20150243?v=4", + "profile": "https://zerotask.github.io", + "contributions": [ + "code" + ] + }, + { + "login": "arthas168", + "name": "Pete Pavlovski", + "avatar_url": "https://avatars.githubusercontent.com/u/32264020?v=4", + "profile": "https://petkopavlovski.com/", + "contributions": [ + "content" + ] + }, + { + "login": "k12ish", + "name": "k12ish", + "avatar_url": "https://avatars.githubusercontent.com/u/45272873?v=4", + "profile": "https://github.com/k12ish", + "contributions": [ + "content" + ] + }, + { + "login": "hongshaoyang", + "name": "Shao Yang Hong", + "avatar_url": "https://avatars.githubusercontent.com/u/19281800?v=4", + "profile": "https://github.com/hongshaoyang", + "contributions": [ + "content" + ] + }, + { + "login": "bmacer", + "name": "Brandon Macer", + "avatar_url": "https://avatars.githubusercontent.com/u/13931806?v=4", + "profile": "https://github.com/bmacer", + "contributions": [ + "content" + ] + }, + { + "login": "stoiandan", + "name": "Stoian Dan", + "avatar_url": "https://avatars.githubusercontent.com/u/10388612?v=4", + "profile": "https://github.com/stoiandan", + "contributions": [ + "content" + ] + }, + { + "login": "PiDelport", + "name": "Pi Delport", + "avatar_url": "https://avatars.githubusercontent.com/u/630271?v=4", + "profile": "https://about.me/pjdelport", + "contributions": [ + "content" + ] + }, + { + "login": "sateeshkumarb", + "name": "Sateesh ", + "avatar_url": "https://avatars.githubusercontent.com/u/429263?v=4", + "profile": "https://github.com/sateeshkumarb", + "contributions": [ + "code", + "content" + ] + }, + { + "login": "kayuapi", + "name": "ZC", + "avatar_url": "https://avatars.githubusercontent.com/u/10304328?v=4", + "profile": "https://github.com/kayuapi", + "contributions": [ + "content" + ] + }, + { + "login": "hyperparabolic", + "name": "hyperparabolic", + "avatar_url": "https://avatars.githubusercontent.com/u/12348474?v=4", + "profile": "https://github.com/hyperparabolic", + "contributions": [ + "code" + ] + }, + { + "login": "kolbma", + "name": "arlecchino", + "avatar_url": "https://avatars.githubusercontent.com/u/5228369?v=4", + "profile": "https://www.net4visions.at", + "contributions": [ + "doc" + ] + }, + { + "login": "jazzplato", + "name": "Richthofen", + "avatar_url": "https://avatars.githubusercontent.com/u/7576730?v=4", + "profile": "https://richthofen.io/", + "contributions": [ + "code" + ] + }, + { + "login": "cseltol", + "name": "Ivan Nerazumov", + "avatar_url": "https://avatars.githubusercontent.com/u/64264529?v=4", + "profile": "https://github.com/cseltol", + "contributions": [ + "doc" + ] + }, + { + "login": "lauralindzey", + "name": "lauralindzey", + "avatar_url": "https://avatars.githubusercontent.com/u/65185744?v=4", + "profile": "https://github.com/lauralindzey", + "contributions": [ + "doc" + ] + }, + { + "login": "sinharaksh1t", + "name": "Rakshit Sinha", + "avatar_url": "https://avatars.githubusercontent.com/u/28585848?v=4", + "profile": "https://github.com/sinharaksh1t", + "contributions": [ + "content" + ] + }, + { + "login": "dbednar230", + "name": "Damian", + "avatar_url": "https://avatars.githubusercontent.com/u/54457902?v=4", + "profile": "https://github.com/dbednar230", + "contributions": [ + "content" + ] + }, + { + "login": "benarmstead", + "name": "Ben Armstead", + "avatar_url": "https://avatars.githubusercontent.com/u/70973680?v=4", + "profile": "https://benarmstead.co.uk", + "contributions": [ + "code" + ] + }, + { + "login": "anuk909", + "name": "anuk909", + "avatar_url": "https://avatars.githubusercontent.com/u/34924662?v=4", + "profile": "https://github.com/anuk909", + "contributions": [ + "content", + "code" + ] + }, + { + "login": "granddaifuku", + "name": "granddaifuku", + "avatar_url": "https://avatars.githubusercontent.com/u/49578068?v=4", + "profile": "https://granddaifuku.com/", + "contributions": [ + "content" + ] + }, + { + "login": "Weilet", + "name": "Weilet", + "avatar_url": "https://avatars.githubusercontent.com/u/32561597?v=4", + "profile": "https://weilet.me", + "contributions": [ + "content" + ] + }, + { + "login": "Millione", + "name": "LIU JIE", + "avatar_url": "https://avatars.githubusercontent.com/u/38575932?v=4", + "profile": "https://github.com/Millione", + "contributions": [ + "content" + ] + }, + { + "login": "abusch", + "name": "Antoine BΓΌsch", + "avatar_url": "https://avatars.githubusercontent.com/u/506344?v=4", + "profile": "https://github.com/abusch", + "contributions": [ + "code" + ] + }, + { + "login": "frogtd", + "name": "frogtd", + "avatar_url": "https://avatars.githubusercontent.com/u/31412003?v=4", + "profile": "https://frogtd.com/", + "contributions": [ + "content" + ] + }, + { + "login": "EmisonLu", + "name": "Zhenghao Lu", + "avatar_url": "https://avatars.githubusercontent.com/u/54395432?v=4", + "profile": "https://github.com/EmisonLu", + "contributions": [ + "content" + ] + }, + { + "login": "fredr", + "name": "Fredrik Enestad", + "avatar_url": "https://avatars.githubusercontent.com/u/762956?v=4", + "profile": "https://soundtrackyourbrand.com", + "contributions": [ + "content" + ] + }, + { + "login": "xuesongbj", + "name": "xuesong", + "avatar_url": "https://avatars.githubusercontent.com/u/18476085?v=4", + "profile": "http://xuesong.pydevops.com", + "contributions": [ + "content" + ] + }, + { + "login": "MpdWalsh", + "name": "Michael Walsh", + "avatar_url": "https://avatars.githubusercontent.com/u/48160144?v=4", + "profile": "https://github.com/MpdWalsh", + "contributions": [ + "code" + ] + }, + { + "login": "alirezaghey", + "name": "alirezaghey", + "avatar_url": "https://avatars.githubusercontent.com/u/26653424?v=4", + "profile": "https://github.com/alirezaghey", + "contributions": [ + "content" + ] + }, + { + "login": "frvannes16", + "name": "Franklin van Nes", + "avatar_url": "https://avatars.githubusercontent.com/u/3188475?v=4", + "profile": "https://github.com/frvannes16", + "contributions": [ + "code" + ] + }, + { + "login": "nekonako", + "name": "nekonako", + "avatar_url": "https://avatars.githubusercontent.com/u/46141275?v=4", + "profile": "https://nekonako.github.io", + "contributions": [ + "code" + ] + }, + { + "login": "tan-zx", + "name": "ZX", + "avatar_url": "https://avatars.githubusercontent.com/u/67887489?v=4", + "profile": "https://github.com/tan-zx", + "contributions": [ + "content" + ] + }, + { + "login": "sundevilyang", + "name": "Yang Wen", + "avatar_url": "https://avatars.githubusercontent.com/u/1499214?v=4", + "profile": "https://github.com/sundevilyang", + "contributions": [ + "content" + ] + }, + { + "login": "highb", + "name": "Brandon High", + "avatar_url": "https://avatars.githubusercontent.com/u/759848?v=4", + "profile": "https://brandon-high.com", + "contributions": [ + "doc" + ] + }, + { + "login": "x-hgg-x", + "name": "x-hgg-x", + "avatar_url": "https://avatars.githubusercontent.com/u/39058530?v=4", + "profile": "https://github.com/x-hgg-x", + "contributions": [ + "code" + ] + }, + { + "login": "KisaragiEffective", + "name": "Kisaragi", + "avatar_url": "https://avatars.githubusercontent.com/u/48310258?v=4", + "profile": "http://kisaragieffective.github.io", + "contributions": [ + "doc" + ] + }, + { + "login": "Kallu-A", + "name": "Lucas Aries", + "avatar_url": "https://avatars.githubusercontent.com/u/73198738?v=4", + "profile": "https://github.com/Kallu-A", + "contributions": [ + "content" + ] + }, + { + "login": "ragreenburg", + "name": "ragreenburg", + "avatar_url": "https://avatars.githubusercontent.com/u/24358100?v=4", + "profile": "https://github.com/ragreenburg", + "contributions": [ + "content" + ] + }, + { + "login": "stevenfukase", + "name": "stevenfukase", + "avatar_url": "https://avatars.githubusercontent.com/u/66785624?v=4", + "profile": "https://github.com/stevenfukase", + "contributions": [ + "content" + ] + }, + { + "login": "J-S-Kim", + "name": "J-S-Kim", + "avatar_url": "https://avatars.githubusercontent.com/u/17569303?v=4", + "profile": "https://github.com/J-S-Kim", + "contributions": [ + "content" + ] + }, + { + "login": "Fointard", + "name": "Fointard", + "avatar_url": "https://avatars.githubusercontent.com/u/9333398?v=4", + "profile": "https://github.com/Fointard", + "contributions": [ + "content" + ] + }, + { + "login": "rytheo", + "name": "Ryan Lowe", + "avatar_url": "https://avatars.githubusercontent.com/u/22184325?v=4", + "profile": "https://github.com/rytheo", + "contributions": [ + "code" + ] + }, + { + "login": "cuishuang", + "name": "cui fliter", + "avatar_url": "https://avatars.githubusercontent.com/u/15921519?v=4", + "profile": "http://www.dashen.tech", + "contributions": [ + "content" + ] + }, + { + "login": "luskwater", + "name": "Ron Lusk", + "avatar_url": "https://avatars.githubusercontent.com/u/42529?v=4", + "profile": "https://github.com/luskwater", + "contributions": [ + "content" + ] + }, + { + "login": "liby", + "name": "Bryan Lee", + "avatar_url": "https://avatars.githubusercontent.com/u/38807139?v=4", + "profile": "http://liby.github.io/liby/", + "contributions": [ + "content" + ] + }, + { + "login": "nandajavarma", + "name": "Nandaja Varma", + "avatar_url": "https://avatars.githubusercontent.com/u/2624550?v=4", + "profile": "http://nandaja.space", + "contributions": [ + "doc" + ] + }, + { + "login": "merelymyself", + "name": "pwygab", + "avatar_url": "https://avatars.githubusercontent.com/u/88221256?v=4", + "profile": "https://github.com/merelymyself", + "contributions": [ + "code" + ] + }, + { + "login": "lucasgrvarela", + "name": "Lucas Grigolon Varela", + "avatar_url": "https://avatars.githubusercontent.com/u/37870368?v=4", + "profile": "http://linkedin.com/in/lucasgrvarela", + "contributions": [ + "content" + ] + }, + { + "login": "bufo24", + "name": "Bufo", + "avatar_url": "https://avatars.githubusercontent.com/u/32884105?v=4", + "profile": "https://github.com/bufo24", + "contributions": [ + "content" + ] + }, + { + "login": "jackos", + "name": "Jack Clayton", + "avatar_url": "https://avatars.githubusercontent.com/u/77730378?v=4", + "profile": "http://rustnote.com", + "contributions": [ + "code" + ] + }, + { + "login": "klkl0808", + "name": "Konstantin", + "avatar_url": "https://avatars.githubusercontent.com/u/24694249?v=4", + "profile": "https://github.com/klkl0808", + "contributions": [ + "content" + ] + }, + { + "login": "0pling", + "name": "0pling", + "avatar_url": "https://avatars.githubusercontent.com/u/104090344?v=4", + "profile": "https://github.com/0pling", + "contributions": [ + "content" + ] + }, + { + "login": "KatanaFluorescent", + "name": "KatanaFluorescent", + "avatar_url": "https://avatars.githubusercontent.com/u/60199077?v=4", + "profile": "https://github.com/KatanaFluorescent", + "contributions": [ + "code" + ] + }, + { + "login": "Drew-Morris", + "name": "Drew Morris", + "avatar_url": "https://avatars.githubusercontent.com/u/95818166?v=4", + "profile": "https://github.com/Drew-Morris", + "contributions": [ + "code" + ] + }, + { + "login": "camperdue42", + "name": "camperdue42", + "avatar_url": "https://avatars.githubusercontent.com/u/43047763?v=4", + "profile": "https://github.com/camperdue42", + "contributions": [ + "content" + ] + }, + { + "login": "YsuOS", + "name": "YsuOS", + "avatar_url": "https://avatars.githubusercontent.com/u/30138661?v=4", + "profile": "https://github.com/YsuOS", + "contributions": [ + "content" + ] + }, + { + "login": "icecream17", + "name": "Steven Nguyen", + "avatar_url": "https://avatars.githubusercontent.com/u/58114641?v=4", + "profile": "https://lichess.org/@/StevenEmily", + "contributions": [ + "content" + ] + }, + { + "login": "nacairns1", + "name": "nacairns1", + "avatar_url": "https://avatars.githubusercontent.com/u/94420090?v=4", + "profile": "https://noahcairns.dev", + "contributions": [ + "content" + ] + }, + { + "login": "pgjbz", + "name": "Paulo Gabriel Justino Bezerra", + "avatar_url": "https://avatars.githubusercontent.com/u/22059237?v=4", + "profile": "https://github.com/pgjbz", + "contributions": [ + "content" + ] + }, + { + "login": "jaystile", + "name": "Jason", + "avatar_url": "https://avatars.githubusercontent.com/u/46078028?v=4", + "profile": "https://github.com/jaystile", + "contributions": [ + "content" + ] + }, + { + "login": "exdx", + "name": "exdx", + "avatar_url": "https://avatars.githubusercontent.com/u/31546601?v=4", + "profile": "https://exdx.github.io", + "contributions": [ + "content" + ] + }, + { + "login": "Jzow", + "name": "James Zow", + "avatar_url": "https://avatars.githubusercontent.com/u/68860495?v=4", + "profile": "https://github.com/Jzow", + "contributions": [ + "content" + ] + }, + { + "login": "jayber", + "name": "James Bromley", + "avatar_url": "https://avatars.githubusercontent.com/u/2474334?v=4", + "profile": "https://jamesabromley.wordpress.com/", + "contributions": [ + "content" + ] + }, + { + "login": "swhiteCQC", + "name": "swhiteCQC", + "avatar_url": "https://avatars.githubusercontent.com/u/77438466?v=4", + "profile": "https://github.com/swhiteCQC", + "contributions": [ + "content" + ] + }, + { + "login": "neilpate", + "name": "Neil Pate", + "avatar_url": "https://avatars.githubusercontent.com/u/7802334?v=4", + "profile": "https://github.com/neilpate", + "contributions": [ + "content" + ] + }, + { + "login": "wojexe", + "name": "wojexe", + "avatar_url": "https://avatars.githubusercontent.com/u/21208490?v=4", + "profile": "https://wojexe.com", + "contributions": [ + "content" + ] + }, + { + "login": "Tostapunk", + "name": "Mattia Schiavon", + "avatar_url": "https://avatars.githubusercontent.com/u/25140297?v=4", + "profile": "https://github.com/Tostapunk", + "contributions": [ + "content" + ] + }, + { + "login": "PrettyWood", + "name": "Eric Jolibois", + "avatar_url": "https://avatars.githubusercontent.com/u/18406791?v=4", + "profile": "http://toucantoco.com", + "contributions": [ + "content" + ] + }, + { + "login": "EdwinChang24", + "name": "Edwin Chang", + "avatar_url": "https://avatars.githubusercontent.com/u/88263098?v=4", + "profile": "http://edwinchang.vercel.app", + "contributions": [ + "content" + ] + }, + { + "login": "saikatdas0790", + "name": "Saikat Das", + "avatar_url": "https://avatars.githubusercontent.com/u/7412443?v=4", + "profile": "https://saikat.dev/", + "contributions": [ + "content" + ] + }, + { + "login": "thatlittleboy", + "name": "Jeremy Goh", + "avatar_url": "https://avatars.githubusercontent.com/u/30731072?v=4", + "profile": "https://github.com/thatlittleboy", + "contributions": [ + "content" + ] + }, + { + "login": "Lioness100", + "name": "Lioness100", + "avatar_url": "https://avatars.githubusercontent.com/u/65814829?v=4", + "profile": "https://github.com/Lioness100", + "contributions": [ + "content" + ] + }, + { + "login": "tvkn", + "name": "Tristan Nicholls", + "avatar_url": "https://avatars.githubusercontent.com/u/79277926?v=4", + "profile": "https://github.com/tvkn", + "contributions": [ + "content" + ] + }, + { + "login": "clairew", + "name": "Claire", + "avatar_url": "https://avatars.githubusercontent.com/u/9344258?v=4", + "profile": "http://clairewang.net", + "contributions": [ + "content" + ] + }, + { + "login": "Mouwrice", + "name": "Maurice Van Wassenhove", + "avatar_url": "https://avatars.githubusercontent.com/u/56763273?v=4", + "profile": "https://github.com/Mouwrice", + "contributions": [ + "content" + ] + }, + { + "login": "johnmendel", + "name": "John Mendelewski", + "avatar_url": "https://avatars.githubusercontent.com/u/77524?v=4", + "profile": "http://jmthree.com", + "contributions": [ + "code" + ] + }, + { + "login": "brianfakhoury", + "name": "Brian Fakhoury", + "avatar_url": "https://avatars.githubusercontent.com/u/20828724?v=4", + "profile": "http://fakhoury.xyz", + "contributions": [ + "content" + ] + }, + { + "login": "markusboehme", + "name": "Markus Boehme", + "avatar_url": "https://avatars.githubusercontent.com/u/5074759?v=4", + "profile": "https://github.com/markusboehme", + "contributions": [ + "code" + ] + }, + { + "login": "nico-vromans", + "name": "Nico Vromans", + "avatar_url": "https://avatars.githubusercontent.com/u/48183857?v=4", + "profile": "https://github.com/nico-vromans", + "contributions": [ + "content" + ] + }, + { + "login": "vostok92", + "name": "vostok92", + "avatar_url": "https://avatars.githubusercontent.com/u/540339?v=4", + "profile": "https://github.com/vostok92", + "contributions": [ + "content" + ] + }, + { + "login": "magnusrodseth", + "name": "Magnus RΓΈdseth", + "avatar_url": "https://avatars.githubusercontent.com/u/59113973?v=4", + "profile": "http://magnusrodseth.vercel.app", + "contributions": [ + "content" + ] + }, + { + "login": "rubiesonthesky", + "name": "rubiesonthesky", + "avatar_url": "https://avatars.githubusercontent.com/u/2591240?v=4", + "profile": "https://github.com/rubiesonthesky", + "contributions": [ + "content" + ] + }, + { + "login": "GabrielBianconi", + "name": "Gabriel Bianconi", + "avatar_url": "https://avatars.githubusercontent.com/u/1275491?v=4", + "profile": "http://www.gabrielbianconi.com/", + "contributions": [ + "content" + ] + }, + { + "login": "Kodylow", + "name": "Kody Low", + "avatar_url": "https://avatars.githubusercontent.com/u/74332828?v=4", + "profile": "https://github.com/Kodylow", + "contributions": [ + "content" + ] } ], "contributorsPerLine": 8, diff --git a/.clog.toml b/.clog.toml deleted file mode 100644 index 206c3b5a..00000000 --- a/.clog.toml +++ /dev/null @@ -1,4 +0,0 @@ -[clog] - -repository = "https://github.com/rust-lang/rustlings" -changelog = "CHANGELOG.md" \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index 89cf181d..aab09aa3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,6 +2,6 @@ root = true [*.rs] end_of_line = lf -insert_final_newfile = true +insert_final_newline = true indent_style = space indent_size = 4 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 00000000..bf2a041a --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,20 @@ +name: Rustlings Tests + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose diff --git a/.gitignore b/.gitignore index 06de8710..534453bc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,8 @@ target/ *.pdb exercises/clippy/Cargo.toml exercises/clippy/Cargo.lock +rust-project.json .idea .vscode +*.iml +*.o diff --git a/.gitpod.yml b/.gitpod.yml index 46b1a6a8..73cb802d 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -4,4 +4,4 @@ tasks: vscode: extensions: - - rust-lang.rust@0.7.8:CvNqMTgDdt3UXt+6BCDTVg== + - rust-lang.rust@0.7.8 diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 00000000..8edf09bf --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,229 @@ +## Authors + +This file lists the people that have contributed to this project. + +Excluded from this list are @carols10cents and @diannasoreil, the principal +authors. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Carol (Nichols || Goulding)

πŸ’» πŸ–‹

QuietMisdreavus

πŸ’» πŸ–‹

Robert M Lugg

πŸ–‹

Hynek Schlawack

πŸ’»

Katharina Fey

πŸ’»

lukabavdaz

πŸ’» πŸ–‹

Erik Vesteraas

πŸ’»

delet0r

πŸ’»

Shaun Bennett

πŸ’»

Andrew Bagshaw

πŸ’»

Kyle Isom

πŸ’»

Colin Pitrat

πŸ’»

Zac Anger

πŸ’»

Matthias Geier

πŸ’»

Chris Pearce

πŸ’»

Yvan Sraka

πŸ’»

Denys Smirnov

πŸ’»

eddyp

πŸ’»

Brian Kung

πŸ’» πŸ–‹

Russell

πŸ’»

Dan Wilhelm

πŸ“–

Jesse

πŸ’» πŸ–‹

Fredrik JambrΓ©n

πŸ’»

Pete McFarlane

πŸ–‹

nkanderson

πŸ’» πŸ–‹

Ajax M

πŸ“–

Dylan Nugent

πŸ–‹

vyaslav

πŸ’» πŸ–‹

George

πŸ’»

Thomas Holloway

πŸ’» πŸ–‹

Jubilee

πŸ’»

WofWca

πŸ’»

Roberto Vidal

πŸ’» πŸ“– πŸ€” 🚧

Jens

πŸ“–

Rahat Ahmed

πŸ“–

Abdou Seck

πŸ’» πŸ–‹ πŸ‘€

Katie

πŸ’»

Socrates

πŸ“–

gnodarse

πŸ–‹

Harrison Metzger

πŸ’»

Torben Jonas

πŸ’» πŸ–‹

Paul Bissex

πŸ“–

Steven Mann

πŸ’» πŸ–‹

Mario Reder

πŸ’» πŸ–‹

skim

πŸ’»

Sanjay K

πŸ’» πŸ–‹

Rohan Jain

πŸ’»

Said Aspen

πŸ’» πŸ–‹

Ufuk Celebi

πŸ’»

lebedevsergey

πŸ“–

Aleksei Trifonov

πŸ–‹

Darren Meehan

πŸ–‹

Jihchi Lee

πŸ–‹

Christofer Bertonha

πŸ–‹

Vivek Bharath Akupatni

πŸ’» ⚠️

DΓ­dac SementΓ© FernΓ‘ndez

πŸ’» πŸ–‹

Rob Story

πŸ’»

Siobhan Jacobson

πŸ’»

Evan Carroll

πŸ–‹

Jawaad Mahmood

πŸ–‹

Gaurang Tandon

πŸ–‹

Stefan Kupresak

πŸ–‹

Greg Leonard

πŸ–‹

Ryan McQuen

πŸ’»

Annika

πŸ‘€

Axel Viala

πŸ’»

Mohammed Sazid Al Rashid

πŸ–‹ πŸ’»

Caleb Webber

🚧

Peter N

🚧

seancad

🚧

Will Hayworth

πŸ–‹

Christian Zeller

πŸ–‹

Jean-Francois Chevrette

πŸ–‹ πŸ’»

John Baber-Lucero

πŸ–‹

Tal

πŸ–‹

apogeeoak

πŸ–‹ πŸ’»

Larry Garfield

πŸ–‹

circumspect

πŸ–‹

Cyrus Wyett

πŸ–‹

cadolphs

πŸ’»

Pascal H.

πŸ–‹

Rod Elias

πŸ–‹

Matt Lebl

πŸ’»

Ignacio Le Fluk

πŸ–‹

Taylor Yu

πŸ’» πŸ–‹

Patrick Hintermayer

πŸ’»

Pete Pavlovski

πŸ–‹

k12ish

πŸ–‹

Shao Yang Hong

πŸ–‹

Brandon Macer

πŸ–‹

Stoian Dan

πŸ–‹

Pi Delport

πŸ–‹

Sateesh

πŸ’» πŸ–‹

ZC

πŸ–‹

hyperparabolic

πŸ’»

arlecchino

πŸ“–

Richthofen

πŸ’»

Ivan Nerazumov

πŸ“–

lauralindzey

πŸ“–

Rakshit Sinha

πŸ–‹

Damian

πŸ–‹

Ben Armstead

πŸ’»

anuk909

πŸ–‹ πŸ’»

granddaifuku

πŸ–‹

Weilet

πŸ–‹

LIU JIE

πŸ–‹

Antoine BΓΌsch

πŸ’»

frogtd

πŸ–‹

Zhenghao Lu

πŸ–‹

Fredrik Enestad

πŸ–‹

xuesong

πŸ–‹

Michael Walsh

πŸ’»

alirezaghey

πŸ–‹

Franklin van Nes

πŸ’»

nekonako

πŸ’»

ZX

πŸ–‹

Yang Wen

πŸ–‹

Brandon High

πŸ“–

x-hgg-x

πŸ’»

Kisaragi

πŸ“–

Lucas Aries

πŸ–‹

ragreenburg

πŸ–‹

stevenfukase

πŸ–‹

J-S-Kim

πŸ–‹

Fointard

πŸ–‹

Ryan Lowe

πŸ’»

cui fliter

πŸ–‹

Ron Lusk

πŸ–‹

Bryan Lee

πŸ–‹

Nandaja Varma

πŸ“–

pwygab

πŸ’»

Lucas Grigolon Varela

πŸ–‹

Bufo

πŸ–‹

Jack Clayton

πŸ’»

Konstantin

πŸ–‹

0pling

πŸ–‹

KatanaFluorescent

πŸ’»

Drew Morris

πŸ’»

camperdue42

πŸ–‹

YsuOS

πŸ–‹

Steven Nguyen

πŸ–‹

nacairns1

πŸ–‹

Paulo Gabriel Justino Bezerra

πŸ–‹

Jason

πŸ–‹

exdx

πŸ–‹

James Zow

πŸ–‹

James Bromley

πŸ–‹

swhiteCQC

πŸ–‹

Neil Pate

πŸ–‹

wojexe

πŸ–‹

Mattia Schiavon

πŸ–‹

Eric Jolibois

πŸ–‹

Edwin Chang

πŸ–‹

Saikat Das

πŸ–‹

Jeremy Goh

πŸ–‹

Lioness100

πŸ–‹

Tristan Nicholls

πŸ–‹

Claire

πŸ–‹

Maurice Van Wassenhove

πŸ–‹

John Mendelewski

πŸ’»

Brian Fakhoury

πŸ–‹

Markus Boehme

πŸ’»

Nico Vromans

πŸ–‹

vostok92

πŸ–‹

Magnus RΓΈdseth

πŸ–‹

rubiesonthesky

πŸ–‹

Gabriel Bianconi

πŸ–‹

Kody Low

πŸ–‹
+ + + + + + +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/CHANGELOG.md b/CHANGELOG.md index d58d4e6b..0b15cf99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,338 @@ + +## 5.2.0 (2022-08-27) + +#### Added + +- Added a `reset` command + +#### Changed + +- **options2**: Convert the exercise to use tests + +#### Fixed + +- **threads3**: Fixed a typo +- **quiz1**: Adjusted the explanations to be consistent with + the tests + + +## 5.1.1 (2022-08-17) + +#### Bug Fixes + +- Fixed an incorrect assertion in options1 + + +## 5.1.0 (2022-08-16) + +#### Features + +- Added a new `rc1` exercise. +- Added a new `cow1` exercise. + +#### Bug Fixes + +- **variables5**: Corrected reference to previous exercise +- **functions4**: Fixed line number reference +- **strings3**: Clarified comment wording +- **traits4, traits5**: Fixed line number reference +- **traits5**: + - Fixed typo in "parameter" + - Made exercise prefer a traits-based solution +- **lifetimes2**: Improved hint +- **threads3**: Fixed typo in hint +- **box1**: Replaced `unimplemented!` with `todo!` +- **errors5**: Provided an explanation for usage of `Box` +- **quiz2**: Fixed a typo +- **macros**: Updated the macros book link +- **options1**: + - Removed unused code + - Added more granular tests +- Fixed some comment syntax shenanigans in info.toml + +#### Housekeeping + +- Fixed a typo in .editorconfig +- Fixed a typo in integration_tests.rs +- Clarified manual installation instructions using `cargo install --path .` +- Added a link to our Zulip in the readme file + + +## 5.0.0 (2022-07-16) + +#### Features + +- Hint comments in exercises now also include a reference to the + `hint` watch mode subcommand. +- **intro1**: Added more hints to point the user to the source file. +- **variables**: Switched variables3 and variables4. +- Moved `vec` and `primitive_types` exercises before `move_semantics`. +- Renamed `vec` to `vecs` to be more in line with the naming in general. +- Split up the `collections` exercises in their own folders. +- **vec2**: Added a second part of the function that provides an alternative, + immutable way of modifying vec values. +- **enums3**: Added a hint. +- Moved `strings` before `modules`. +- Added a `strings3` exercise to teach modifying strings. +- Added a `hashmaps3` exercise for some advanced usage of hashmaps. +- Moved the original `quiz2` to be `strings4`, since it only tested strings + anyways. +- Reworked `quiz2` into a new exercise that tests more chapters. +- Renamed `option` to `options`. +- **options1**: Rewrote parts of the exercise to remove the weird array + iteration stuff. +- Moved `generics3` to be `quiz3`. +- Moved box/arc exercises behind `iterators`. +- **iterators4**: Added a test for factorials of zero. +- Split `threads1` between two exercises, the first one focusing more on + `JoinHandle`s. +- Added a `threads3` exercises that uses `std::sync::mpsc`. +- Added a `clippy3` exercises with some more interesting checks. +- **as_ref_mut**: Added a section that actually tests `AsMut`. +- Added 3 new lifetimes exercises. +- Added 3 new traits exercises. + +#### Bug Fixes + +- **variables2**: Made output messages more verbose. +- **variables5**: Added a nudging hint about shadowing. +- **variables6**: Fixed link to book. +- **functions**: Clarified the README wording. Generally cleaned up + some hints and added some extra comments. +- **if2**: Renamed function name to `foo_if_fizz`. +- **move_semantics**: Clarified some hints. +- **quiz1**: Renamed the function name to be more verbose. +- **structs1**: Use an integer type instead of strings. Renamed "unit structs" + to "unit-like structs", as is used in the book. +- **structs3**: Added the `panic!` statement in from the beginning. +- **errors1**: Use `is_empty()` instead of `len() > 0` +- **errors3**: Improved the hint. +- **errors5**: Improved exercise instructions and the hint. +- **errors6**: Provided the skeleton of one of the functions that's supposed + to be implemented. +- **iterators3**: Inserted `todo!` into `divide()` to keep a compiler error + from happening. +- **from_str**: Added a hint comment about string error message conversion with + `Box`. +- **try_from_into**: Fixed the function name in comment. + +#### Removed + +- Removed the legacy LSP feature that was using `mod.rs` files. +- Removed `quiz4`. +- Removed `advanced_errs`. These were the last exercises in the recommended + order, and I've always felt like they didn't quite fit in with the mostly + simple, book-following style we've had in Rustlings. + +#### Housekeeping + +- Added missing exercises to the book index. +- Updated spacing in Cargo.toml. +- Added a GitHub actions config so that tests run on every PR/commit. + + +## 4.8.0 (2022-07-01) + +#### Features + +- Added a progress indicator for `rustlings watch`. +- The installation script now checks for Rustup being installed. +- Added a `rustlings lsp` command to enable `rust-analyzer`. + +#### Bug Fixes + +- **move_semantics5**: Replaced "in vogue" with "in scope" in hint. +- **if2**: Fixed a typo in the hint. +- **variables1**: Fixed an incorrect line reference in the hint. +- Fixed an out of bounds check in the installation Bash script. + +#### Housekeeping + +- Replaced the git.io URL with the fully qualified URL because of git.io's sunsetting. +- Removed the deprecated Rust GitPod extension. + + +## 4.7.1 (2022-04-20) + +#### Features + +- The amount of dependency crates that need to be compiled went down from ~65 to + ~45 by bumping dependency versions. +- The minimum Rust version in the install scripts has been bumped to 1.56.0 (this isn't in + the release itself, since install scripts don't really get versioned) + +#### Bug Fixes + +- **arc1**: A small part has been rewritten using a more functional code style (#968). +- **using_as**: A small part has been refactored to use `sum` instead of `fold`, resulting + in better readability. + +#### Housekeeping + +- The changelog will now be manually written instead of being automatically generated by the + Git log. + + +## 4.7.0 (2022-04-14) + + +#### Features + +* Add move_semantics6.rs exercise (#908) ([3f0e1303](https://github.com/rust-lang/rustlings/commit/3f0e1303e0b3bf3fecc0baced3c8b8a37f83c184)) +* **intro:** Add intro section. ([21c9f441](https://github.com/rust-lang/rustlings/commit/21c9f44168394e08338fd470b5f49b1fd235986f)) +* Include exercises folder in the project structure behind a feature, enabling rust-analyzer to work (#917) ([179a75a6](https://github.com/rust-lang/rustlings/commit/179a75a68d03ac9518dec2297fb17f91a4fc506b)) + +#### Bug Fixes + +* Fix a few spelling mistakes ([1c0fe3cb](https://github.com/rust-lang/rustlings/commit/1c0fe3cbcca85f90b3985985b8e265ee872a2ab2)) +* **cli:** + * Move long text strings into constants. ([f78c4802](https://github.com/rust-lang/rustlings/commit/f78c48020830d7900dd8d81f355606581670446d)) + * Replace `filter_map()` with `find_map()` ([9b27e8d](https://github.com/rust-lang/rustlings/commit/9b27e8d993ca20232fe38a412750c3f845a83b65)) +* **clippy1:** + * Set clippy::float_cmp lint to deny (#907) ([71a06044](https://github.com/rust-lang/rustlings/commit/71a06044e6a96ff756dc31d7b0ed665ae4badb57)) + * Updated code to test correctness clippy lint with approx_constant lint rule ([f2650de3](https://github.com/rust-lang/rustlings/commit/f2650de369810867d2763e935ac0963c32ec420e)) +* **errors1:** + * Add a comment to make the purpose more clear (#486) ([cbcde345](https://github.com/rust-lang/rustlings/commit/cbcde345409c3e550112e449242848eaa3391bb6)) + * Don't modify tests (#958) ([60bb7cc](https://github.com/rust-lang/rustlings/commit/60bb7cc3931d21d3986ad52b2b302e632a93831c)) +* **errors6:** Remove existing answer code ([43d0623](https://github.com/rust-lang/rustlings/commit/43d0623086edbc46fe896ba59c7afa22c3da9f7a)) +* **functions5:** Remove wrong new line and small English improvements (#885) ([8ef4869b](https://github.com/rust-lang/rustlings/commit/8ef4869b264094e5a9b50452b4534823a9df19c3)) +* **install:** protect path with whitespaces using quotes and stop at the first error ([d114847f](https://github.com/rust-lang/rustlings/commit/d114847f256c5f571c0b4c87e04b04bce3435509)) +* **intro1:** Add compiler error explanation. ([9b8de655](https://github.com/rust-lang/rustlings/commit/9b8de65525a5576b78cf0c8e4098cdd34296338f)) +* **iterators1:** reorder TODO steps ([0bd7a063](https://github.com/rust-lang/rustlings/commit/0bd7a0631a17a9d69af5746795a30efc9cf64e6e)) +* **move_semantics2:** Add comment ([89650f80](https://github.com/rust-lang/rustlings/commit/89650f808af23a32c9a2c6d46592b77547a6a464)) +* **move_semantics5:** correct typo (#857) ([46c28d5c](https://github.com/rust-lang/rustlings/commit/46c28d5cef3d8446b5a356b19d8dbc725f91a3a0)) +* **quiz1:** update to say quiz covers "If" ([1622e8c1](https://github.com/rust-lang/rustlings/commit/1622e8c198d89739765c915203efff0091bdeb78)) +* **structs3:** + * Add a hint for panic (#608) ([4f7ff5d9](https://github.com/rust-lang/rustlings/commit/4f7ff5d9c7b2d8b045194c1a9469d37e30257c4a)) + * remove redundant 'return' (#852) ([bf33829d](https://github.com/rust-lang/rustlings/commit/bf33829da240375d086f96267fc2e02fa6b07001)) + * Assigned value to `cents_per_gram` in test ([d1ee2da](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532)) +* **structs3.rs:** assigned value to cents_per_gram in test ([d1ee2daf](https://github.com/rust-lang/rustlings/commit/d1ee2daf14f19105e6db3f9c610f44293d688532)) +* **traits1:** rename test functions to snake case (#854) ([1663a16e](https://github.com/rust-lang/rustlings/commit/1663a16eade6ca646b6ed061735f7982434d530d)) + +#### Documentation improvements + +* Add hints on how to get GCC installed (#741) ([bc56861](https://github.com/rust-lang/rustlings/commit/bc5686174463ad6f4f6b824b0e9b97c3039d4886)) +* Fix some code blocks that were not highlighted ([17f9d74](https://github.com/rust-lang/rustlings/commit/17f9d7429ccd133a72e815fb5618e0ce79560929)) + + + +## 4.6.0 (2021-09-25) + + +#### Features + +* add advanced_errs2 ([abd6b70c](https://github.com/rust-lang/rustlings/commit/abd6b70c72dc6426752ff41f09160b839e5c449e)) +* add advanced_errs1 ([882d535b](https://github.com/rust-lang/rustlings/commit/882d535ba8628d5e0b37e8664b3e2f26260b2671)) +* Add a farewell message when quitting `watch` ([1caef0b4](https://github.com/rust-lang/rustlings/commit/1caef0b43494c8b8cdd6c9260147e70d510f1aca)) +* add more watch commands ([a7dc080b](https://github.com/rust-lang/rustlings/commit/a7dc080b95e49146fbaafe6922a6de2f8cb1582a), closes [#842](https://github.com/rust-lang/rustlings/issues/842)) +* **modules:** update exercises, add modules3 (#822) ([dfd2fab4](https://github.com/rust-lang/rustlings/commit/dfd2fab4f33d1bf59e2e5ee03123c0c9a67a9481)) +* **quiz1:** add default function name in comment (#838) ([0a11bad7](https://github.com/rust-lang/rustlings/commit/0a11bad71402b5403143d642f439f57931278c07)) + +#### Bug Fixes + +* Correct small typo in exercises/conversions/from_str.rs ([86cc8529](https://github.com/rust-lang/rustlings/commit/86cc85295ae36948963ae52882e285d7e3e29323)) +* **cli:** typo in exercise.rs (#848) ([06d5c097](https://github.com/rust-lang/rustlings/commit/06d5c0973a3dffa3c6c6f70acb775d4c6630323c)) +* **from_str, try_from_into:** custom error types ([2dc93cad](https://github.com/rust-lang/rustlings/commit/2dc93caddad43821743e4903d89b355df58d7a49)) +* **modules2:** fix typo (#835) ([1c3beb0a](https://github.com/rust-lang/rustlings/commit/1c3beb0a59178c950dc05fe8ee2346b017429ae0)) +* **move_semantics5:** + * change &mut *y to &mut x (#814) ([d75759e8](https://github.com/rust-lang/rustlings/commit/d75759e829fdcd64ef071cf4b6eae2a011a7718b)) + * Clarify instructions ([df25684c](https://github.com/rust-lang/rustlings/commit/df25684cb79f8413915e00b5efef29369849cef1)) +* **quiz1:** Fix inconsistent wording (#826) ([03131a3d](https://github.com/rust-lang/rustlings/commit/03131a3d35d9842598150f9da817f7cc26e2669a)) + + + + +## 4.5.0 (2021-07-07) + + +#### Features + +* Add move_semantics5 exercise. (#746) ([399ab328](https://github.com/rust-lang/rustlings/commit/399ab328d8d407265c09563aa4ef4534b2503ff2)) +* **cli:** Add "next" to run the next unsolved exercise. (#785) ([d20e413a](https://github.com/rust-lang/rustlings/commit/d20e413a68772cd493561f2651cf244e822b7ca5)) + +#### Bug Fixes + +* rename result1 to errors4 ([50ab289d](https://github.com/rust-lang/rustlings/commit/50ab289da6b9eb19a7486c341b00048c516b88c0)) +* move_semantics5 hints ([1b858285](https://github.com/rust-lang/rustlings/commit/1b85828548f46f58b622b5e0c00f8c989f928807)) +* remove trailing whitespaces from iterators1 ([4d4fa774](https://github.com/rust-lang/rustlings/commit/4d4fa77459392acd3581c6068aa8be9a02de12fc)) +* add hints to generics1 and generics2 exercises ([31457940](https://github.com/rust-lang/rustlings/commit/31457940846b3844d78d4a4d2b074bc8d6aaf1eb)) +* remove trailing whitespace ([d9b69bd1](https://github.com/rust-lang/rustlings/commit/d9b69bd1a0a7a99f2c0d80933ad2eea44c8c71b2)) +* **installation:** first PowerShell command ([aa9a943d](https://github.com/rust-lang/rustlings/commit/aa9a943ddf3ae260782e73c26bcc9db60e5894b6)) +* **iterators5:** derive Clone, Copy ([91fc9e31](https://github.com/rust-lang/rustlings/commit/91fc9e3118f4af603c9911698cc2a234725cb032)) +* **quiz1:** Updated question description (#794) ([d8766496](https://github.com/rust-lang/rustlings/commit/d876649616cc8a8dd5f539f8bc1a5434b960b1e9)) +* **try_from_into, from_str:** hints for dyn Error ([11d2cf0d](https://github.com/rust-lang/rustlings/commit/11d2cf0d604dee3f5023c17802d69438e69fa50e)) +* **variables5:** confine the answer further ([48ffcbd2](https://github.com/rust-lang/rustlings/commit/48ffcbd2c4cc4d936c2c7480019190f179813cc5)) + + + + +## 4.4.0 (2021-04-24) + + +#### Bug Fixes + +* Fix spelling error in main.rs ([91ee27f2](https://github.com/rust-lang/rustlings/commit/91ee27f22bd3797a9db57e5fd430801c170c5db8)) +* typo in default out text ([644c49f1](https://github.com/rust-lang/rustlings/commit/644c49f1e04cbb24e95872b3a52b07d692ae3bc8)) +* **collections:** Naming exercises for vectors and hashmap ([bef39b12](https://github.com/rust-lang/rustlings/commit/bef39b125961310b34b34871e480a82e82af4678)) +* **from_str:** + * Correct typos ([5f7c89f8](https://github.com/rust-lang/rustlings/commit/5f7c89f85db1f33da01911eaa479c3a2d4721678)) + * test for error instead of unwrap/should_panic ([15e71535](https://github.com/rust-lang/rustlings/commit/15e71535f37cfaed36e22eb778728d186e2104ab)) + * use trait objects for from_str ([c3e7b831](https://github.com/rust-lang/rustlings/commit/c3e7b831786c9172ed8bd5d150f3c432f242fba9)) +* **functions3:** improve function argument type (#687) ([a6509cc4](https://github.com/rust-lang/rustlings/commit/a6509cc4d545d8825f01ddf7ee37823b372154dd)) +* **hashmap2:** Update incorrect assertion (#660) ([72aaa15e](https://github.com/rust-lang/rustlings/commit/72aaa15e6ab4b72b3422f1c6356396e20a2a2bb8)) +* **info:** Fix typo (#635) ([cddc1e86](https://github.com/rust-lang/rustlings/commit/cddc1e86e7ec744ee644cc774a4887b1a0ded3e8)) +* **iterators2:** Moved errors out of tests. ([baf4ba17](https://github.com/rust-lang/rustlings/commit/baf4ba175ba6eb92989e3dd54ecbec4bedc9a863), closes [#359](https://github.com/rust-lang/rustlings/issues/359)) +* **iterators3:** Enabled iterators3.rs to run without commented out tests. ([c6712dfc](https://github.com/rust-lang/rustlings/commit/c6712dfccd1a093e590ad22bbc4f49edc417dac0)) +* **main:** Let find_exercise work with borrows ([347f30bd](https://github.com/rust-lang/rustlings/commit/347f30bd867343c5ace1097e085a1f7e356553f7)) +* **move_semantics4:** + * Remove redundant "instead" (#640) ([cc266d7d](https://github.com/rust-lang/rustlings/commit/cc266d7d80b91e79df3f61984f231b7f1587218e)) + * Small readbility improvement (#617) ([10965920](https://github.com/rust-lang/rustlings/commit/10965920fbdf8a1efc85bed869e55a1787006404)) +* **option2:** Rename uninformative variables (#675) ([b4de6594](https://github.com/rust-lang/rustlings/commit/b4de6594380636817d13c2677ec6f472a964cf43)) +* **quiz3:** Force an answer to Q2 (#672) ([0d894e6f](https://github.com/rust-lang/rustlings/commit/0d894e6ff739943901e1ae8c904582e5c2f843bd)) +* **structs:** Add 5.3 to structs/README (#652) ([6bd791f2](https://github.com/rust-lang/rustlings/commit/6bd791f2f44aa7f0ad926df767f6b1fa8f12a9a9)) +* **structs2:** correct grammar in hint (#663) ([ebdb66c7](https://github.com/rust-lang/rustlings/commit/ebdb66c7bfb6d687a14cc511a559a222e6fc5de4)) +* **structs3:** + * reword heading comment (#664) ([9f3e8c2d](https://github.com/rust-lang/rustlings/commit/9f3e8c2dde645e5264c2d2200e68842b5f47bfa3)) + * add check to prevent naive implementation of is_international ([05a753fe](https://github.com/rust-lang/rustlings/commit/05a753fe6333d36dbee5f68c21dec04eacdc75df)) +* **threads1:** line number correction ([7857b0a6](https://github.com/rust-lang/rustlings/commit/7857b0a689b0847f48d8c14cbd1865e3b812d5ca)) +* **try_from_into:** use trait objects ([2e93a588](https://github.com/rust-lang/rustlings/commit/2e93a588e0abe8badb7eafafb9e7d073c2be5df8)) + +#### Features + +* Replace clap with argh ([7928122f](https://github.com/rust-lang/rustlings/commit/7928122fcef9ca7834d988b1ec8ca0687478beeb)) +* Replace emojis when NO_EMOJI env variable present ([8d62a996](https://github.com/rust-lang/rustlings/commit/8d62a9963708dbecd9312e8bcc4b47049c72d155)) +* Added iterators5.rs exercise. ([b29ea17e](https://github.com/rust-lang/rustlings/commit/b29ea17ea94d1862114af2cf5ced0e09c197dc35)) +* **arc1:** Add more details to description and hint (#710) ([81be4044](https://github.com/rust-lang/rustlings/commit/81be40448777fa338ebced3b0bfc1b32d6370313)) +* **cli:** Improve the list command with options, and then some ([8bbe4ff1](https://github.com/rust-lang/rustlings/commit/8bbe4ff1385c5c169c90cd3ff9253f9a91daaf8e)) +* **list:** + * updated progress percentage ([1c6f7e4b](https://github.com/rust-lang/rustlings/commit/1c6f7e4b7b9b3bd36f4da2bb2b69c549cc8bd913)) + * added progress info ([c0e3daac](https://github.com/rust-lang/rustlings/commit/c0e3daacaf6850811df5bc57fa43e0f249d5cfa4)) + + + + +## 4.3.0 (2020-12-29) + +#### Features + +* Rewrite default out text ([44d39112](https://github.com/rust-lang/rustlings/commit/44d39112ff122b29c9793fe52e605df1612c6490)) +* match exercise order to book chapters (#541) ([033bf119](https://github.com/rust-lang/rustlings/commit/033bf1198fc8bfce1b570e49da7cde010aa552e3)) +* Crab? (#586) ([fa9f522b](https://github.com/rust-lang/rustlings/commit/fa9f522b7f043d7ef73a39f003a9272dfe72c4f4)) +* add "rustlings list" command ([838f9f30](https://github.com/rust-lang/rustlings/commit/838f9f30083d0b23fd67503dcf0fbeca498e6647)) +* **try_from_into:** remove duplicate annotation ([04f1d079](https://github.com/rust-lang/rustlings/commit/04f1d079aa42a2f49af694bc92c67d731d31a53f)) + +#### Bug Fixes + +* update structs README ([bcf14cf6](https://github.com/rust-lang/rustlings/commit/bcf14cf677adb3a38a3ac3ca53f3c69f61153025)) +* added missing exercises to info.toml ([90cfb6ff](https://github.com/rust-lang/rustlings/commit/90cfb6ff28377531bfc34acb70547bdb13374f6b)) +* gives a bit more context to magic number ([30644c9a](https://github.com/rust-lang/rustlings/commit/30644c9a062b825c0ea89435dc59f0cad86b110e)) +* **functions2:** Change signature to trigger precise error message: (#605) ([0ef95947](https://github.com/rust-lang/rustlings/commit/0ef95947cc30482e63a7045be6cc2fb6f6dcb4cc)) +* **structs1:** Adjust wording (#573) ([9334783d](https://github.com/rust-lang/rustlings/commit/9334783da31d821cc59174fbe8320df95828926c)) +* **try_from_into:** + * type error ([4f4cfcf3](https://github.com/rust-lang/rustlings/commit/4f4cfcf3c36c8718c7c170c9c3a6935e6ef0618c)) + * Update description (#584) ([96347df9](https://github.com/rust-lang/rustlings/commit/96347df9df294f01153b29d9ad4ba361f665c755)) +* **vec1:** Have test compare every element in a and v ([9b6c6293](https://github.com/rust-lang/rustlings/commit/9b6c629397b24b944f484f5b2bbd8144266b5695)) + ## 4.2.0 (2020-11-07) @@ -103,7 +438,7 @@ #### Features * add Option2 exercise (#290) ([86b5c08b](https://github.com/rust-lang/rustlings/commit/86b5c08b9bea1576127a7c5f599f5752072c087d)) -* add excercise for option (#282) ([135e5d47](https://github.com/rust-lang/rustlings/commit/135e5d47a7c395aece6f6022117fb20c82f2d3d4)) +* add exercise for option (#282) ([135e5d47](https://github.com/rust-lang/rustlings/commit/135e5d47a7c395aece6f6022117fb20c82f2d3d4)) * add new exercises for generics (#280) ([76be5e4e](https://github.com/rust-lang/rustlings/commit/76be5e4e991160f5fd9093f03ee2ba260e8f7229)) * **ci:** add buildkite config ([b049fa2c](https://github.com/rust-lang/rustlings/commit/b049fa2c84dba0f0c8906ac44e28fd45fba51a71)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d566df08..cc8ac923 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,17 +21,17 @@ _implement a new feature! ➑️ [open an Issue to discuss it first, then a Pull `rustlings` is basically a glorified `rustc` wrapper. Therefore the source code isn't really that complicated since the bulk of the work is done by `rustc`. -`src/main.rs` contains a simple `clap` CLI that loads from `src/verify.rs` and `src/run.rs`. +`src/main.rs` contains a simple `argh` CLI that connects to most of the other source files. ### Adding an exercise -First step is to add the exercise! Call it `exercises/yourTopic/yourTopicN.rs`, make sure to +The first step is to add the exercise! Name the file `exercises/yourTopic/yourTopicN.rs`, make sure to put in some helpful links, and link to sections of the book in `exercises/yourTopic/README.md`. -Next you want to make sure it runs when using `rustlings`. All exercises are stored in `info.toml`, under the `exercises` array. They're ordered by the order they're ran when using `rustlings verify`. +Next make sure it runs with `rustlings`. The exercise metadata is stored in `info.toml`, under the `exercises` array. The order of the `exercises` array determines the order the exercises are run by `rustlings verify` and `rustlings watch`. -You want to make sure where in the file you add your exercise. If you're not sure, add it at the bottom and ask in your pull request. To add an exercise, edit the file like this: +Add the metadata for your exercise in the correct order in the `exercises` array. If you are unsure of the correct ordering, add it at the bottom and ask in your pull request. The exercise metadata should contain the following: ```diff ... + [[exercises]] @@ -43,7 +43,7 @@ You want to make sure where in the file you add your exercise. If you're not sur ... ``` -The `mode` attribute decides whether Rustlings will only compile your exercise, or compile and test it. If you have tests to verify in your exercise, choose `test`, otherwise `compile`. +The `mode` attribute decides whether Rustlings will only compile your exercise, or compile and test it. If you have tests to verify in your exercise, choose `test`, otherwise `compile`. If you're working on a Clippy exercise, use `mode = "clippy"`. That's all! Feel free to put up a pull request. @@ -67,19 +67,19 @@ changes. There's a couple of things to watch out for: #### Write correct commit messages We follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) -specification, because it makes it easier to generate changelogs automatically. +specification. This means that you have to format your commit messages in a specific way. Say you're working on adding a new exercise called `foobar1.rs`. You could write the following commit message: ``` -feat: Add foobar1.rs exercise +feat: add foobar1.rs exercise ``` If you're just fixing a bug, please use the `fix` type: ``` -fix(verify): Make sure verify doesn't self-destruct +fix(verify): make sure verify doesn't self-destruct ``` The scope within the brackets is optional, but should be any of these: @@ -96,7 +96,7 @@ When the commit also happens to close an existing issue, link it in the message body: ``` -fix: Update foobar +fix: update foobar closes #101029908 ``` @@ -104,13 +104,13 @@ closes #101029908 If you're doing simple changes, like updating a book link, use `chore`: ``` -chore: Update exercise1.rs book link +chore: update exercise1.rs book link ``` If you're updating documentation, use `docs`: ``` -docs: Add more information to Readme +docs: add more information to Readme ``` If, and only if, you're absolutely sure you want to make a breaking change @@ -118,7 +118,7 @@ If, and only if, you're absolutely sure you want to make a breaking change explain the breaking change in the message body: ``` -fix!: Completely change verification +fix!: completely change verification BREAKING CHANGE: This has to be done because lorem ipsum dolor ``` @@ -126,6 +126,5 @@ BREAKING CHANGE: This has to be done because lorem ipsum dolor #### Pull Request Workflow Once you open a Pull Request, it may be reviewed or labeled (or both) until -the maintainers accept your change. Then, [bors](https://github.com/bors) will -run the test suite with your changes and if it's successful, automatically -merge it in! +the maintainers accept your change. Please be patient, it may take some time +for this to happen! diff --git a/Cargo.lock b/Cargo.lock index 9f90e0a1..2370e8dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,920 +1,646 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "aho-corasick" -version = "0.7.3" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] -name = "ansi_term" -version = "0.11.0" +name = "argh" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7317a549bc17c5278d9e72bb6e62c6aa801ac2567048e39ebc1c194249323e" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "argh_derive", + "argh_shared", ] +[[package]] +name = "argh_derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60949c42375351e9442e354434b0cba2ac402c1237edf673cac3a4bf983b8d3c" +dependencies = [ + "argh_shared", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "argh_shared" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a61eb019cb8f415d162cb9f12130ee6bbe9168b7d953c17f4ad049e4051ca00" + [[package]] name = "assert_cmd" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dc477793bd82ec39799b6f6b3df64938532fdf2ab0d49ef817eac65856a5a1e" dependencies = [ - "escargot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "predicates 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "atty" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "escargot", + "predicates", + "predicates-core", + "predicates-tree", ] [[package]] name = "autocfg" -version = "0.1.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" -version = "1.0.4" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cfg-if" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "clap" -version = "2.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "clicolors-control" +name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "console" -version = "0.7.7" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "console" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "encode_unicode", + "libc", + "once_cell", + "regex", + "terminal_size", + "unicode-width", + "winapi 0.3.9", ] [[package]] name = "difference" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" [[package]] name = "encode_unicode" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "escargot" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceb9adbf9874d5d028b5e4c5739d22b71988252b25c9c98fe7cf9738bee84597" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "log", + "serde", + "serde_json", ] [[package]] name = "filetime" -version = "0.2.5" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "winapi 0.3.9", ] [[package]] name = "float-cmp" -version = "0.4.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" dependencies = [ - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits", ] [[package]] name = "fsevent" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "fsevent-sys", ] [[package]] name = "fsevent-sys" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "fuchsia-zircon-sys", ] [[package]] name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "home" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +dependencies = [ + "winapi 0.3.9", +] [[package]] name = "indicatif" -version = "0.10.3" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b" dependencies = [ - "console 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "console", + "lazy_static", + "number_prefix", + "regex", ] [[package]] name = "inotify" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "inotify-sys", + "libc", ] [[package]] name = "inotify-sys" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "iovec" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "itoa" -version = "0.4.4" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] [[package]] name = "lazy_static" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.58" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "lock_api" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" [[package]] name = "log" -version = "0.4.6" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] [[package]] name = "memchr" -version = "2.2.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "mio" -version = "0.6.19" +version = "0.6.23" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" dependencies = [ - "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", ] [[package]] name = "mio-extras" -version = "2.0.5" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" dependencies = [ - "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell", + "log", + "mio", + "slab", ] [[package]] name = "miow" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] name = "net2" -version = "0.2.33" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", ] [[package]] name = "normalize-line-endings" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "notify" -version = "4.0.15" +version = "4.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257" dependencies = [ - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "filetime 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "inotify 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "filetime", + "fsevent", + "fsevent-sys", + "inotify", + "libc", + "mio", + "mio-extras", + "walkdir", + "winapi 0.3.9", ] [[package]] name = "num-traits" -version = "0.2.8" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "number_prefix" -version = "0.2.8" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] -name = "numtoa" -version = "0.1.0" +name = "once_cell" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "parking_lot" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "predicates" -version = "1.0.1" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" dependencies = [ - "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "normalize-line-endings 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "difference", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", ] [[package]] name = "predicates-core" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451" [[package]] name = "predicates-tree" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dd0fd014130206c9352efbdc92be592751b2b9274dff685348341082c6ea3d" dependencies = [ - "predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "predicates-core", + "treeline", ] [[package]] name = "proc-macro2" -version = "0.4.30" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quote" -version = "0.6.12" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_core" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_os" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rdrand" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "redox_termios" -version = "0.1.1" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", ] [[package]] name = "regex" -version = "1.1.6" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ - "aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.6" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rustlings" -version = "4.2.0" +version = "5.1.1" dependencies = [ - "assert_cmd 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "indicatif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "notify 4.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "predicates 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "argh", + "assert_cmd", + "console", + "glob", + "home", + "indicatif", + "notify", + "predicates", + "regex", + "serde", + "serde_json", + "toml", ] [[package]] name = "ryu" -version = "0.2.8" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "same-file" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "serde" -version = "1.0.92" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1" dependencies = [ - "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.92" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "serde_json" -version = "1.0.39" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ - "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "slab" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "smallvec" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "strsim" -version = "0.8.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" [[package]] name = "syn" -version = "0.15.34" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] -name = "termion" -version = "1.5.2" +name = "terminal_size" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", - "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "termios" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "thread_local" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi 0.3.9", ] [[package]] name = "toml" -version = "0.4.10" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ - "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "treeline" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" [[package]] -name = "ucd-util" -version = "0.1.3" +name = "unicode-segmentation" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" [[package]] name = "unicode-width" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "utf8-ranges" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "vec_map" -version = "0.8.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "walkdir" -version = "2.2.7" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ - "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file", + "winapi 0.3.9", + "winapi-util", ] [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.2" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8", + "winapi-build", ] - -[metadata] -"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum assert_cmd 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2dc477793bd82ec39799b6f6b3df64938532fdf2ab0d49ef817eac65856a5a1e" -"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" -"checksum clicolors-control 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "73abfd4c73d003a674ce5d2933fca6ce6c42480ea84a5ffe0a2dc39ed56300f9" -"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -"checksum console 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8ca57c2c14b8a2bf3105bc9d15574aad80babf6a9c44b1058034cdf8bd169628" -"checksum console 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b147390a412132d75d10dd3b7b175a69cf5fd95032f7503c7091b8831ba10242" -"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" -"checksum encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90b2c9496c001e8cb61827acdefad780795c42264c137744cae6f7d9e3450abd" -"checksum escargot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ceb9adbf9874d5d028b5e4c5739d22b71988252b25c9c98fe7cf9738bee84597" -"checksum filetime 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2f8c63033fcba1f51ef744505b3cad42510432b904c062afa67ad7ece008429d" -"checksum float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "134a8fa843d80a51a5b77d36d42bc2def9edcb0262c914861d08129fd1926600" -"checksum fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" -"checksum fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0" -"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -"checksum indicatif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "40ecd1e2ee08e6c255ce890f5a99d17000850e664e7acf119fb03b25b0575bfe" -"checksum inotify 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24e40d6fd5d64e2082e0c796495c8ef5ad667a96d03e5aaa0becfd9d47bcbfb8" -"checksum inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0" -"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" -"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" -"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319" -"checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" -"checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" -"checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" -"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" -"checksum normalize-line-endings 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2e0a1a39eab95caf4f5556da9289b9e68f0aafac901b2ce80daaf020d3b733a8" -"checksum notify 4.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "80ae4a7688d1fab81c5bf19c64fc8db920be8d519ce6336ed4e7efe024724dbd" -"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" -"checksum number_prefix 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf9993e59c894e3c08aa1c2712914e9e6bf1fcbfc6bef283e2183df345a4fee" -"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" -"checksum parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa7767817701cce701d5585b9c4db3cdd02086398322c1d7e8bf5094a96a2ce7" -"checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" -"checksum predicates 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53e09015b0d3f5a0ec2d4428f7559bb7b3fff341b4e159fedd1d57fac8b939ff" -"checksum predicates-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06075c3a3e92559ff8929e7a280684489ea27fe44805174c3ebd9328dcb37178" -"checksum predicates-tree 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8e63c4859013b38a76eca2414c64911fba30def9e3202ac461a2d22831220124" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" -"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" -"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" -"checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" -"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58" -"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" -"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "32746bf0f26eab52f06af0d0aa1984f641341d06d8d673c693871da2d188c9be" -"checksum serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "46a3223d0c9ba936b61c0d2e3e559e3217dbfb8d65d06d26e8b3c25de38bae3e" -"checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" -"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" -"checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" -"checksum termios 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72b620c5ea021d75a735c943269bb07d30c9b77d6ac6b236bc8b5c496ef05625" -"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" -"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" -"checksum treeline 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41" -"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" -"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" -"checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" -"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" -"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" -"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" -"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/Cargo.toml b/Cargo.toml index ebce25eb..25cd7bfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,20 @@ [package] name = "rustlings" -version = "4.2.0" -authors = ["Marisa ", "Carol (Nichols || Goulding) "] -edition = "2018" +version = "5.2.0" +authors = ["Liv ", "Carol (Nichols || Goulding) "] +edition = "2021" [dependencies] -clap = "2.32.0" -indicatif = "0.10.3" -console = "0.7.7" -notify = "4.0.15" -toml = "0.4.10" -regex = "1.1.6" -serde = {version = "1.0.10", features = ["derive"]} +argh = "0.1" +indicatif = "0.16" +console = "0.15" +notify = "4.0" +toml = "0.5" +regex = "1.5" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.81" +home = "0.5.3" +glob = "0.3.0" [[bin]] name = "rustlings" diff --git a/README.md b/README.md index fd45d945..2392ba9d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,3 @@ -![crab pet](https://i.imgur.com/LbZJgmm.gif) - -[![All Contributors](https://img.shields.io/badge/all_contributors-68-orange.svg?style=flat-square)](#contributors-) - - # rustlings πŸ¦€β€οΈ Greetings and welcome to `rustlings`. This project contains small exercises to get you used to reading and writing Rust code. This includes reading and responding to compiler messages! @@ -17,6 +12,7 @@ Alternatively, for a first-time Rust learner, there are several other resources: ## Getting Started _Note: If you're on MacOS, make sure you've installed Xcode and its developer tools by typing `xcode-select --install`._ +_Note: If you're on Linux, make sure you've installed gcc. Deb: `sudo apt install gcc`. Yum: `sudo yum -y install gcc`._ You will need to have Rust installed. You can get it by visiting https://rustup.rs. This'll also install Cargo, Rust's package/project manager. @@ -25,47 +21,50 @@ You will need to have Rust installed. You can get it by visiting https://rustup. Just run: ```bash -curl -L https://git.io/rustlings | bash +curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash # Or if you want it to be installed to a different path: -curl -L https://git.io/rustlings | bash -s mypath/ +curl -L https://raw.githubusercontent.com/rust-lang/rustlings/main/install.sh | bash -s mypath/ ``` This will install Rustlings and give you access to the `rustlings` command. Run it to get started! ## Windows -In PowerShell, set `ExecutionPolicy` to `RemoteSigned`: +In PowerShell (Run as Administrator), set `ExecutionPolicy` to `RemoteSigned`: -```ps -Set-ExecutionPolicy RemoteSigned +```ps1 +Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser ``` Then, you can run: -```ps -Start-BitsTransfer -Source https://git.io/rustlings-win -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 +```ps1 +Start-BitsTransfer -Source https://raw.githubusercontent.com/rust-lang/rustlings/main/install.ps1 -Destination $env:TMP/install_rustlings.ps1; Unblock-File $env:TMP/install_rustlings.ps1; Invoke-Expression $env:TMP/install_rustlings.ps1 ``` To install Rustlings. Same as on MacOS/Linux, you will have access to the `rustlings` command after it. -## Browser: +If you get a permission denied message, you might have to exclude the directory where you cloned Rustlings in your antivirus. + +## Browser [Run on Repl.it](https://repl.it/github/rust-lang/rustlings) -[Open in Gitpod](https://gitpod.io/#https://github.com/rust-lang/rustlings) +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/rust-lang/rustlings) ## Manually -Basically: Clone the repository, checkout to the latest tag, run `cargo install`. +Basically: Clone the repository at the latest tag, run `cargo install --path .`. ```bash -git clone https://github.com/rust-lang/rustlings +# find out the latest version at https://github.com/rust-lang/rustlings/releases/latest (on edit 5.1.1) +git clone -b 5.1.1 --depth 1 https://github.com/rust-lang/rustlings cd rustlings -git checkout tags/4.2.0 # or whatever the latest version is (find out at https://github.com/rust-lang/rustlings/releases/latest) cargo install --force --path . ``` If there are installation errors, ensure that your toolchain is up to date. For the latest, run: + ```bash rustup update ``` @@ -96,148 +95,69 @@ In case you want to go by your own order, or want to only verify a single exerci rustlings run myExercise1 ``` +Or simply use the following command to run the next unsolved exercise in the course: + +```bash +rustlings run next +``` + In case you get stuck, you can run the following command to get a hint for your exercise: -``` bash +```bash rustlings hint myExercise1 ``` +You can also get the hint for the next unsolved exercise with the following command: + +```bash +rustlings hint next +``` + +To check your progress, you can run the following command: + +```bash +rustlings list +``` + ## Testing yourself After every couple of sections, there will be a quiz that'll test your knowledge on a bunch of sections at once. These quizzes are found in `exercises/quizN.rs`. +## Enabling `rust-analyzer` + +Run the command `rustlings lsp` which will generate a `rust-project.json` at the root of the project, this allows [rust-analyzer](https://rust-analyzer.github.io/) to parse each exercise. + ## Continuing On Once you've completed Rustlings, put your new knowledge to good use! Continue practicing your Rust skills by building your own projects, contributing to Rustlings, or finding other open-source projects to contribute to. -If you'd like to uninstall Rustlings, you can do so by invoking cargo and removing the rustlings directory: +## Uninstalling Rustlings + +If you want to remove Rustlings from your system, there's two steps. First, you'll need to remove the exercises folder that the install script created +for you: + +```bash +rm -rf rustlings # or your custom folder name, if you chose and or renamed it +``` + +Second, since Rustlings got installed via `cargo install`, it's only reasonable to assume that you can also remove it using Cargo, and +exactly that is the case. Run `cargo uninstall` to remove the `rustlings` binary: ```bash cargo uninstall rustlings -rm -r rustlings/ # or on Windows: rmdir /s rustlings ``` -## Completion - -Rustlings isn't done; there are a couple of sections that are very experimental and don't have proper documentation. These include: - -- Errors (`exercises/errors/`) -- Option (`exercises/option/`) -- Result (`exercises/result/`) -- Move Semantics (could still be improved, `exercises/move_semantics/`) - -Additionally, we could use exercises on a couple of topics: - -- Structs -- Better ownership stuff -- `impl` -- ??? probably more - -If you are interested in improving or adding new ones, please feel free to contribute! Read on for more information :) +Now you should be done! ## Contributing See [CONTRIBUTING.md](./CONTRIBUTING.md). +Development-focused discussion about Rustlings happens in the [**rustlings** stream](https://rust-lang.zulipchat.com/#narrow/stream/334454-rustlings) +on the [Rust Project Zulip](https://rust-lang.zulipchat.com). Feel free to start a new thread there +if you have ideas or suggestions! + ## Contributors ✨ -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Carol (Nichols || Goulding)

πŸ’» πŸ–‹

QuietMisdreavus

πŸ’» πŸ–‹

Robert M Lugg

πŸ–‹

Hynek Schlawack

πŸ’»

Katharina Fey

πŸ’»

lukabavdaz

πŸ’» πŸ–‹

Erik Vesteraas

πŸ’»

delet0r

πŸ’»

Shaun Bennett

πŸ’»

Andrew Bagshaw

πŸ’»

Kyle Isom

πŸ’»

Colin Pitrat

πŸ’»

Zac Anger

πŸ’»

Matthias Geier

πŸ’»

Chris Pearce

πŸ’»

Yvan Sraka

πŸ’»

Denys Smirnov

πŸ’»

eddyp

πŸ’»

Brian Kung

πŸ’» πŸ–‹

Russell

πŸ’»

Dan Wilhelm

πŸ“–

Jesse

πŸ’» πŸ–‹

Fredrik JambrΓ©n

πŸ’»

Pete McFarlane

πŸ–‹

nkanderson

πŸ’» πŸ–‹

Ajax M

πŸ“–

Dylan Nugent

πŸ–‹

vyaslav

πŸ’» πŸ–‹

George

πŸ’»

Thomas Holloway

πŸ’» πŸ–‹

Jubilee

πŸ’»

WofWca

πŸ’»

Roberto Vidal

πŸ’» πŸ“– πŸ€” 🚧

Jens

πŸ“–

Rahat Ahmed

πŸ“–

Abdou Seck

πŸ’» πŸ–‹ πŸ‘€

Katie

πŸ’»

Socrates

πŸ“–

gnodarse

πŸ–‹

Harrison Metzger

πŸ’»

Torben Jonas

πŸ’» πŸ–‹

Paul Bissex

πŸ“–

Steven Mann

πŸ’» πŸ–‹

Mario Reder

πŸ’» πŸ–‹

skim

πŸ’»

Sanjay K

πŸ’» πŸ–‹

Rohan Jain

πŸ’»

Said Aspen

πŸ’» πŸ–‹

Ufuk Celebi

πŸ’»

lebedevsergey

πŸ“–

Aleksei Trifonov

πŸ–‹

Darren Meehan

πŸ–‹

Jihchi Lee

πŸ–‹

Christofer Bertonha

πŸ–‹

Vivek Bharath Akupatni

πŸ’» ⚠️

DΓ­dac SementΓ© FernΓ‘ndez

πŸ’» πŸ–‹

Rob Story

πŸ’»

Siobhan Jacobson

πŸ’»

Evan Carroll

πŸ–‹

Jawaad Mahmood

πŸ–‹

Gaurang Tandon

πŸ–‹

Stefan Kupresak

πŸ–‹

Greg Leonard

πŸ–‹

Ryan McQuen

πŸ’»

Annika

πŸ‘€

Axel Viala

πŸ’»

Mohammed Sazid Al Rashid

πŸ–‹ πŸ’»

Caleb Webber

🚧
- - - - - -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! +Thanks goes to the wonderful people listed in [AUTHORS.md](./AUTHORS.md) πŸŽ‰ diff --git a/default_out.txt b/default_out.txt deleted file mode 100644 index 05267591..00000000 --- a/default_out.txt +++ /dev/null @@ -1,19 +0,0 @@ -Thanks for installing Rustlings! - -Is this your first time? - -Let's make sure you're up to speed: -- You have Rust installed, preferably via `rustup` -- You have `~/.cargo/bin` added to your PATH variable -- You have cloned this repository (https://github.com/rust-lang/rustlings) -- You have installed Rust language support for your editor -- You have locally installed the `rustlings` command by running an - installation script or manually executing: - -cargo install --force --path . - -If you've done all of this (or even most of it), congrats! You're ready -to start working with Rust. - -To get started, run `rustlings watch` in order to get the first exercise. -Make sure to have your editor open! diff --git a/exercises/README.md b/exercises/README.md new file mode 100644 index 00000000..e52137ca --- /dev/null +++ b/exercises/README.md @@ -0,0 +1,26 @@ +# Exercise to Book Chapter mapping + +| Exercise | Book Chapter | +| ---------------------- | ------------------- | +| variables | Β§3.1 | +| functions | Β§3.3 | +| if | Β§3.5 | +| primitive_types | Β§3.2, Β§4.3 | +| vecs | Β§8.1 | +| move_semantics | Β§4.1, Β§4.2 | +| structs | Β§5.1, Β§5.3 | +| enums | Β§6, Β§18.3 | +| strings | Β§8.2 | +| modules | Β§7 | +| hashmaps | Β§8.3 | +| options | Β§10.1 | +| error_handling | Β§9 | +| generics | Β§10 | +| traits | Β§10.2 | +| tests | Β§11.1 | +| lifetimes | Β§10.3 | +| standard_library_types | Β§13.2, Β§15.1, Β§16.3 | +| threads | Β§16.1, Β§16.2, Β§16.3 | +| macros | Β§19.6 | +| clippy | n/a | +| conversions | n/a | diff --git a/exercises/clippy/README.md b/exercises/clippy/README.md index 60a12fe5..55438af6 100644 --- a/exercises/clippy/README.md +++ b/exercises/clippy/README.md @@ -1,8 +1,10 @@ -### Clippy +# Clippy The Clippy tool is a collection of lints to analyze your code so you can catch common mistakes and improve your Rust code. If you used the installation script for Rustlings, Clippy should be already installed. If not you can install it manually via `rustup component add clippy`. -For more information about Clippy lints, please see [their documentation page](https://rust-lang.github.io/rust-clippy/master/). +## Further information + +- [GitHub Repository](https://github.com/rust-lang/rust-clippy). diff --git a/exercises/clippy/clippy1.rs b/exercises/clippy/clippy1.rs index bdb5dd2c..bad46891 100644 --- a/exercises/clippy/clippy1.rs +++ b/exercises/clippy/clippy1.rs @@ -4,14 +4,20 @@ // // For these exercises the code will fail to compile when there are clippy warnings // check clippy's suggestions from the output to solve the exercise. -// Execute `rustlings hint clippy1` for hints :) +// Execute `rustlings hint clippy1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE +use std::f32; + fn main() { - let x = 1.2331f64; - let y = 1.2332f64; - if y != x { - println!("Success!"); - } + let pi = 3.14f32; + let radius = 5.00f32; + + let area = pi * f32::powi(radius, 2); + + println!( + "The area of a circle with radius {:.2} is {:.5}!", + radius, area + ) } diff --git a/exercises/clippy/clippy2.rs b/exercises/clippy/clippy2.rs index 37af9ed0..dac40dbe 100644 --- a/exercises/clippy/clippy2.rs +++ b/exercises/clippy/clippy2.rs @@ -1,5 +1,5 @@ // clippy2.rs -// Make me compile! Execute `rustlings hint clippy2` for hints :) +// Execute `rustlings hint clippy2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/clippy/clippy3.rs b/exercises/clippy/clippy3.rs new file mode 100644 index 00000000..b0159ebe --- /dev/null +++ b/exercises/clippy/clippy3.rs @@ -0,0 +1,28 @@ +// clippy3.rs +// Here's a couple more easy Clippy fixes, so you can see its utility. + +// I AM NOT DONE + +#[allow(unused_variables, unused_assignments)] +fn main() { + let my_option: Option<()> = None; + if my_option.is_none() { + my_option.unwrap(); + } + + let my_arr = &[ + -1, -2, -3 + -4, -5, -6 + ]; + println!("My array! Here it is: {:?}", my_arr); + + let my_empty_vec = vec![1, 2, 3, 4, 5].resize(0, 5); + println!("This Vec is empty, see? {:?}", my_empty_vec); + + let mut value_a = 45; + let mut value_b = 66; + // Let's swap these two! + value_a = value_b; + value_b = value_a; + println!("value a: {}; value b: {}", value_a, value_b); +} diff --git a/exercises/collections/README.md b/exercises/collections/README.md deleted file mode 100644 index 9ded29a0..00000000 --- a/exercises/collections/README.md +++ /dev/null @@ -1,20 +0,0 @@ -### Collections - -Rust’s standard library includes a number of very useful data -structures called collections. Most other data types represent one -specific value, but collections can contain multiple values. Unlike -the built-in array and tuple types, the data these collections point -to is stored on the heap, which means the amount of data does not need -to be known at compile time and can grow or shrink as the program -runs. - -This exercise will get you familiar with two fundamental data -structures that are used very often in Rust programs: - -* A *vector* allows you to store a variable number of values next to - each other. -* A *hash map* allows you to associate a value with a particular key. - You may also know this by the names *map* in C++, *dictionary* in - Python or an *associative array* in other languages. - -[Rust book chapter](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html) diff --git a/exercises/conversions/README.md b/exercises/conversions/README.md index 114bd428..8d7da93e 100644 --- a/exercises/conversions/README.md +++ b/exercises/conversions/README.md @@ -1,5 +1,4 @@ -### Type conversions - +# Type conversions Rust offers a multitude of ways to convert a value of a given type into another type. @@ -15,6 +14,8 @@ Furthermore, the `std::str` module offers a trait called [`FromStr`](https://doc These should be the main ways ***within the standard library*** to convert data into your desired types. -#### Book Sections +## Further information -These are not directly covered in the book, but the standard library has great documentation for [conversions here](https://doc.rust-lang.org/std/convert/index.html). The `FromStr` trait is also covered [here](https://doc.rust-lang.org/std/str/trait.FromStr.html). \ No newline at end of file +These are not directly covered in the book, but the standard library has a great documentation for it. +- [conversions](https://doc.rust-lang.org/std/convert/index.html) +- [`FromStr` trait](https://doc.rust-lang.org/std/str/trait.FromStr.html) \ No newline at end of file diff --git a/exercises/conversions/as_ref_mut.rs b/exercises/conversions/as_ref_mut.rs index 84f4a60c..9f479739 100644 --- a/exercises/conversions/as_ref_mut.rs +++ b/exercises/conversions/as_ref_mut.rs @@ -1,6 +1,7 @@ // AsRef and AsMut allow for cheap reference-to-reference conversions. // Read more about them at https://doc.rust-lang.org/std/convert/trait.AsRef.html // and https://doc.rust-lang.org/std/convert/trait.AsMut.html, respectively. +// Execute `rustlings hint as_ref_mut` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -16,10 +17,10 @@ fn char_counter(arg: T) -> usize { arg.as_ref().chars().count() } -fn main() { - let s = "CafΓ© au lait"; - println!("{}", char_counter(s)); - println!("{}", byte_counter(s)); +// Squares a number using AsMut. Add the trait bound as is appropriate and +// implement the function body. +fn num_sq(arg: &mut T) { + ??? } #[cfg(test)] @@ -49,4 +50,11 @@ mod tests { let s = String::from("Cafe au lait"); assert_eq!(char_counter(s.clone()), byte_counter(s)); } + + #[test] + fn mult_box() { + let mut num: Box = Box::new(3); + num_sq(&mut num); + assert_eq!(*num, 9); + } } diff --git a/exercises/conversions/from_into.rs b/exercises/conversions/from_into.rs index f24cf61b..6c272c3b 100644 --- a/exercises/conversions/from_into.rs +++ b/exercises/conversions/from_into.rs @@ -1,6 +1,8 @@ // The From trait is used for value-to-value conversions. // If From is implemented correctly for a type, the Into trait should work conversely. // You can read more about it at https://doc.rust-lang.org/std/convert/trait.From.html +// Execute `rustlings hint from_into` or use the `hint` watch subcommand for a hint. + #[derive(Debug)] struct Person { name: String, @@ -115,4 +117,18 @@ mod tests { assert_eq!(p.name, "John"); assert_eq!(p.age, 30); } + + #[test] + fn test_trailing_comma() { + let p: Person = Person::from("Mike,32,"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } + + #[test] + fn test_trailing_comma_and_some_string() { + let p: Person = Person::from("Mike,32,man"); + assert_eq!(p.name, "John"); + assert_eq!(p.age, 30); + } } diff --git a/exercises/conversions/from_str.rs b/exercises/conversions/from_str.rs index af9eee6d..fe168159 100644 --- a/exercises/conversions/from_str.rs +++ b/exercises/conversions/from_str.rs @@ -1,27 +1,50 @@ -// This does practically the same thing that TryFrom<&str> does. +// from_str.rs +// This is similar to from_into.rs, but this time we'll implement `FromStr` +// and return errors instead of falling back to a default value. // Additionally, upon implementing FromStr, you can use the `parse` method // on strings to generate an object of the implementor type. // You can read more about it at https://doc.rust-lang.org/std/str/trait.FromStr.html +// Execute `rustlings hint from_str` or use the `hint` watch subcommand for a hint. + +use std::num::ParseIntError; use std::str::FromStr; -#[derive(Debug)] +#[derive(Debug, PartialEq)] struct Person { name: String, age: usize, } +// We will use this error type for the `FromStr` implementation. +#[derive(Debug, PartialEq)] +enum ParsePersonError { + // Empty input string + Empty, + // Incorrect number of fields + BadLen, + // Empty name field + NoName, + // Wrapped error from parse::() + ParseInt(ParseIntError), +} + // I AM NOT DONE + // Steps: -// 1. If the length of the provided string is 0, then return an error +// 1. If the length of the provided string is 0, an error should be returned // 2. Split the given string on the commas present in it -// 3. Extract the first element from the split operation and use it as the name -// 4. If the name is empty, then return an error +// 3. Only 2 elements should be returned from the split, otherwise return an error +// 4. Extract the first element from the split operation and use it as the name // 5. Extract the other element from the split operation and parse it into a `usize` as the age -// with something like `"4".parse::()`. -// If while parsing the age, something goes wrong, then return an error -// Otherwise, then return a Result of a Person object +// with something like `"4".parse::()` +// 6. If while extracting the name and the age something goes wrong, an error should be returned +// If everything goes well, then return a Result of a Person object +// +// As an aside: `Box` implements `From<&'_ str>`. This means that if you want to return a +// string error message, you can do so via just using return `Err("my error message".into())`. + impl FromStr for Person { - type Err = String; + type Err = ParsePersonError; fn from_str(s: &str) -> Result { } } @@ -37,7 +60,7 @@ mod tests { #[test] fn empty_input() { - assert!("".parse::().is_err()); + assert_eq!("".parse::(), Err(ParsePersonError::Empty)); } #[test] fn good_input() { @@ -48,38 +71,57 @@ mod tests { assert_eq!(p.age, 32); } #[test] - #[should_panic] fn missing_age() { - "John,".parse::().unwrap(); + assert!(matches!( + "John,".parse::(), + Err(ParsePersonError::ParseInt(_)) + )); } #[test] - #[should_panic] fn invalid_age() { - "John,twenty".parse::().unwrap(); + assert!(matches!( + "John,twenty".parse::(), + Err(ParsePersonError::ParseInt(_)) + )); } #[test] - #[should_panic] fn missing_comma_and_age() { - "John".parse::().unwrap(); + assert_eq!("John".parse::(), Err(ParsePersonError::BadLen)); } #[test] - #[should_panic] fn missing_name() { - ",1".parse::().unwrap(); + assert_eq!(",1".parse::(), Err(ParsePersonError::NoName)); } #[test] - #[should_panic] fn missing_name_and_age() { - ",".parse::().unwrap(); + assert!(matches!( + ",".parse::(), + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)) + )); } #[test] - #[should_panic] fn missing_name_and_invalid_age() { - ",one".parse::().unwrap(); + assert!(matches!( + ",one".parse::(), + Err(ParsePersonError::NoName | ParsePersonError::ParseInt(_)) + )); + } + + #[test] + fn trailing_comma() { + assert_eq!("John,32,".parse::(), Err(ParsePersonError::BadLen)); + } + + #[test] + fn trailing_comma_and_some_string() { + assert_eq!( + "John,32,man".parse::(), + Err(ParsePersonError::BadLen) + ); } } diff --git a/exercises/conversions/try_from_into.rs b/exercises/conversions/try_from_into.rs index 897b364e..fa98bc90 100644 --- a/exercises/conversions/try_from_into.rs +++ b/exercises/conversions/try_from_into.rs @@ -1,7 +1,10 @@ +// try_from_into.rs // TryFrom is a simple and safe type conversion that may fail in a controlled way under some circumstances. // Basically, this is the same as From. The main difference is that this should return a Result type // instead of the target type itself. // You can read more about it at https://doc.rust-lang.org/std/convert/trait.TryFrom.html +// Execute `rustlings hint try_from_into` or use the `hint` watch subcommand for a hint. + use std::convert::{TryFrom, TryInto}; #[derive(Debug, PartialEq)] @@ -11,12 +14,21 @@ struct Color { blue: u8, } +// We will use this error type for these `TryFrom` conversions. +#[derive(Debug, PartialEq)] +enum IntoColorError { + // Incorrect length of slice + BadLen, + // Integer conversion error + IntConversion, +} + // I AM NOT DONE // Your task is to complete this implementation // and return an Ok result of inner type Color. // You need to create an implementation for a tuple of three integers, -// an array of three integers and a slice of integers. +// an array of three integers, and a slice of integers. // // Note that the implementation for tuple and array will be checked at compile time, // but the slice implementation needs to check the slice length! @@ -24,36 +36,39 @@ struct Color { // Tuple implementation impl TryFrom<(i16, i16, i16)> for Color { - type Error = String; - fn try_from(tuple: (i16, i16, i16)) -> Result {} + type Error = IntoColorError; + fn try_from(tuple: (i16, i16, i16)) -> Result { + } } // Array implementation impl TryFrom<[i16; 3]> for Color { - type Error = String; - fn try_from(arr: [i16; 3]) -> Result {} + type Error = IntoColorError; + fn try_from(arr: [i16; 3]) -> Result { + } } // Slice implementation impl TryFrom<&[i16]> for Color { - type Error = String; - fn try_from(slice: &[i16]) -> Result {} + type Error = IntoColorError; + fn try_from(slice: &[i16]) -> Result { + } } fn main() { - // Use the `from` function + // Use the `try_from` function let c1 = Color::try_from((183, 65, 14)); println!("{:?}", c1); - // Since From is implemented for Color, we should be able to use Into + // Since TryFrom is implemented for Color, we should be able to use TryInto let c2: Result = [183, 65, 14].try_into(); println!("{:?}", c2); let v = vec![183, 65, 14]; - // With slice we should use `from` function + // With slice we should use `try_from` function let c3 = Color::try_from(&v[..]); println!("{:?}", c3); - // or take slice within round brackets and use Into + // or take slice within round brackets and use TryInto let c4: Result = (&v[..]).try_into(); println!("{:?}", c4); } @@ -64,92 +79,112 @@ mod tests { #[test] fn test_tuple_out_of_range_positive() { - assert!(Color::try_from((256, 1000, 10000)).is_err()); + assert_eq!( + Color::try_from((256, 1000, 10000)), + Err(IntoColorError::IntConversion) + ); } #[test] fn test_tuple_out_of_range_negative() { - assert!(Color::try_from((-1, -10, -256)).is_err()); + assert_eq!( + Color::try_from((-1, -10, -256)), + Err(IntoColorError::IntConversion) + ); } #[test] fn test_tuple_sum() { - assert!(Color::try_from((-1, 255, 255)).is_err()); + assert_eq!( + Color::try_from((-1, 255, 255)), + Err(IntoColorError::IntConversion) + ); } #[test] fn test_tuple_correct() { - let c: Result = (183, 65, 14).try_into(); + let c: Result = (183, 65, 14).try_into(); + assert!(c.is_ok()); assert_eq!( - c, - Ok(Color { + c.unwrap(), + Color { red: 183, green: 65, blue: 14 - }) + } ); } #[test] fn test_array_out_of_range_positive() { - let c: Result = [1000, 10000, 256].try_into(); - assert!(c.is_err()); + let c: Result = [1000, 10000, 256].try_into(); + assert_eq!(c, Err(IntoColorError::IntConversion)); } #[test] fn test_array_out_of_range_negative() { - let c: Result = [-10, -256, -1].try_into(); - assert!(c.is_err()); + let c: Result = [-10, -256, -1].try_into(); + assert_eq!(c, Err(IntoColorError::IntConversion)); } #[test] fn test_array_sum() { - let c: Result = [-1, 255, 255].try_into(); - assert!(c.is_err()); + let c: Result = [-1, 255, 255].try_into(); + assert_eq!(c, Err(IntoColorError::IntConversion)); } #[test] - #[test] fn test_array_correct() { - let c: Result = [183, 65, 14].try_into(); + let c: Result = [183, 65, 14].try_into(); + assert!(c.is_ok()); assert_eq!( - c, - Ok(Color { + c.unwrap(), + Color { red: 183, green: 65, blue: 14 - }) + } ); } #[test] fn test_slice_out_of_range_positive() { let arr = [10000, 256, 1000]; - assert!(Color::try_from(&arr[..]).is_err()); + assert_eq!( + Color::try_from(&arr[..]), + Err(IntoColorError::IntConversion) + ); } #[test] fn test_slice_out_of_range_negative() { let arr = [-256, -1, -10]; - assert!(Color::try_from(&arr[..]).is_err()); + assert_eq!( + Color::try_from(&arr[..]), + Err(IntoColorError::IntConversion) + ); } #[test] fn test_slice_sum() { let arr = [-1, 255, 255]; - assert!(Color::try_from(&arr[..]).is_err()); + assert_eq!( + Color::try_from(&arr[..]), + Err(IntoColorError::IntConversion) + ); } #[test] fn test_slice_correct() { let v = vec![183, 65, 14]; - let c: Result = Color::try_from(&v[..]); + let c: Result = Color::try_from(&v[..]); + assert!(c.is_ok()); assert_eq!( - c, - Ok(Color { + c.unwrap(), + Color { red: 183, green: 65, blue: 14 - }) + } ); } #[test] fn test_slice_excess_length() { let v = vec![0, 0, 0, 0]; - assert!(Color::try_from(&v[..]).is_err()); + assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen)); } #[test] fn test_slice_insufficient_length() { let v = vec![0, 0]; - assert!(Color::try_from(&v[..]).is_err()); + assert_eq!(Color::try_from(&v[..]), Err(IntoColorError::BadLen)); } } diff --git a/exercises/conversions/using_as.rs b/exercises/conversions/using_as.rs index 821309ec..8c9b7113 100644 --- a/exercises/conversions/using_as.rs +++ b/exercises/conversions/using_as.rs @@ -4,11 +4,12 @@ // // The goal is to make sure that the division does not fail to compile // and returns the proper type. +// Execute `rustlings hint using_as` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn average(values: &[f64]) -> f64 { - let total = values.iter().fold(0.0, |a, b| a + b); + let total = values.iter().sum::(); total / values.len() } diff --git a/exercises/enums/README.md b/exercises/enums/README.md index 091f5d04..30d4d91d 100644 --- a/exercises/enums/README.md +++ b/exercises/enums/README.md @@ -1,10 +1,10 @@ -### Enums +# Enums Rust allows you to define types called "enums" which enumerate possible values. Enums are a feature in many languages, but their capabilities differ in each language. Rust’s enums are most similar to algebraic data types in functional languages, such as F#, OCaml, and Haskell. Useful in combination with enums is Rust's "pattern matching" facility, which makes it easy to run different code for different values of an enumeration. -#### Book Sections +## Further information - [Enums](https://doc.rust-lang.org/book/ch06-00-enums.html) - [Pattern syntax](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html) diff --git a/exercises/enums/enums1.rs b/exercises/enums/enums1.rs index a2223d33..511ba740 100644 --- a/exercises/enums/enums1.rs +++ b/exercises/enums/enums1.rs @@ -1,5 +1,5 @@ // enums1.rs -// Make me compile! Execute `rustlings hint enums1` for hints! +// No hints this time! ;) // I AM NOT DONE diff --git a/exercises/enums/enums2.rs b/exercises/enums/enums2.rs index ec32d952..18479f87 100644 --- a/exercises/enums/enums2.rs +++ b/exercises/enums/enums2.rs @@ -1,5 +1,5 @@ // enums2.rs -// Make me compile! Execute `rustlings hint enums2` for hints! +// Execute `rustlings hint enums2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/enums/enums3.rs b/exercises/enums/enums3.rs index 178b40c4..55acf6bc 100644 --- a/exercises/enums/enums3.rs +++ b/exercises/enums/enums3.rs @@ -1,5 +1,6 @@ // enums3.rs // Address all the TODOs to make the tests pass! +// Execute `rustlings hint enums3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/error_handling/README.md b/exercises/error_handling/README.md index 77a58d18..5255ace9 100644 --- a/exercises/error_handling/README.md +++ b/exercises/error_handling/README.md @@ -1,5 +1,11 @@ -For this exercise check out the sections: +# Error handling +Most errors aren’t serious enough to require the program to stop entirely. +Sometimes, when a function fails, it’s for a reason that you can easily interpret and respond to. +For example, if you try to open a file and that operation fails because the file doesn’t exist, you might want to create the file instead of terminating the process. + +## Further information + - [Error Handling](https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html) - [Generics](https://doc.rust-lang.org/book/ch10-01-syntax.html) - -of the Rust Book. +- [Result](https://doc.rust-lang.org/rust-by-example/error/result.html) +- [Boxing errors](https://doc.rust-lang.org/rust-by-example/error/multiple_error_types/boxing_errors.html) diff --git a/exercises/error_handling/errors1.rs b/exercises/error_handling/errors1.rs index 9c24d85d..bcee9723 100644 --- a/exercises/error_handling/errors1.rs +++ b/exercises/error_handling/errors1.rs @@ -1,19 +1,18 @@ // errors1.rs // This function refuses to generate text to be printed on a nametag if // you pass it an empty string. It'd be nicer if it explained what the problem -// was, instead of just sometimes returning `None`. The 2nd test currently -// does not compile or pass, but it illustrates the behavior we would like -// this function to have. -// Execute `rustlings hint errors1` for hints! +// was, instead of just sometimes returning `None`. Thankfully, Rust has a similar +// construct to `Option` that can be used to express error conditions. Let's use it! +// Execute `rustlings hint errors1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE pub fn generate_nametag_text(name: String) -> Option { - if name.len() > 0 { - Some(format!("Hi! My name is {}", name)) - } else { + if name.is_empty() { // Empty names aren't allowed. None + } else { + Some(format!("Hi! My name is {}", name)) } } @@ -21,14 +20,11 @@ pub fn generate_nametag_text(name: String) -> Option { mod tests { use super::*; - // This test passes initially if you comment out the 2nd test. - // You'll need to update what this test expects when you change - // the function under test! #[test] fn generates_nametag_text_for_a_nonempty_name() { assert_eq!( generate_nametag_text("BeyoncΓ©".into()), - Some("Hi! My name is BeyoncΓ©".into()) + Ok("Hi! My name is BeyoncΓ©".into()) ); } @@ -36,6 +32,7 @@ mod tests { fn explains_why_generating_nametag_text_fails() { assert_eq!( generate_nametag_text("".into()), + // Don't change this line Err("`name` was empty; it must be nonempty.".into()) ); } diff --git a/exercises/error_handling/errors2.rs b/exercises/error_handling/errors2.rs index aad3a93f..1cd8fc66 100644 --- a/exercises/error_handling/errors2.rs +++ b/exercises/error_handling/errors2.rs @@ -14,7 +14,8 @@ // and add. // There are at least two ways to implement this that are both correct-- but -// one is a lot shorter! Execute `rustlings hint errors2` for hints to both ways. +// one is a lot shorter! +// Execute `rustlings hint errors2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/error_handling/errors3.rs b/exercises/error_handling/errors3.rs index 460ac5c4..a2d2d190 100644 --- a/exercises/error_handling/errors3.rs +++ b/exercises/error_handling/errors3.rs @@ -2,7 +2,7 @@ // This is a program that is trying to use a completed version of the // `total_cost` function from the previous exercise. It's not working though! // Why not? What should we do to fix it? -// Execute `rustlings hint errors3` for hints! +// Execute `rustlings hint errors3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/error_handling/result1.rs b/exercises/error_handling/errors4.rs similarity index 78% rename from exercises/error_handling/result1.rs rename to exercises/error_handling/errors4.rs index b978001b..0efe8ccd 100644 --- a/exercises/error_handling/result1.rs +++ b/exercises/error_handling/errors4.rs @@ -1,5 +1,5 @@ -// result1.rs -// Make this test pass! Execute `rustlings hint result1` for hints :) +// errors4.rs +// Execute `rustlings hint errors4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -14,6 +14,7 @@ enum CreationError { impl PositiveNonzeroInteger { fn new(value: i64) -> Result { + // Hmm...? Why is this only returning an Ok value? Ok(PositiveNonzeroInteger(value as u64)) } } diff --git a/exercises/error_handling/errors5.rs b/exercises/error_handling/errors5.rs new file mode 100644 index 00000000..2ba8f903 --- /dev/null +++ b/exercises/error_handling/errors5.rs @@ -0,0 +1,65 @@ +// errors5.rs + +// This program uses an altered version of the code from errors4. + +// This exercise uses some concepts that we won't get to until later in the course, like `Box` and the +// `From` trait. It's not important to understand them in detail right now, but you can read ahead if you like. +// For now, think of the `Box` type as an "I want anything that does ???" type, which, given +// Rust's usual standards for runtime safety, should strike you as somewhat lenient! + +// In short, this particular use case for boxes is for when you want to own a value and you care only that it is a +// type which implements a particular trait. To do so, The Box is declared as of type Box where Trait is the trait +// the compiler looks for on any value used in that context. For this exercise, that context is the potential errors +// which can be returned in a Result. + +// What can we use to describe both errors? In other words, is there a trait which both errors implement? + +// Execute `rustlings hint errors5` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +use std::error; +use std::fmt; +use std::num::ParseIntError; + +// TODO: update the return type of `main()` to make this compile. +fn main() -> Result<(), Box> { + let pretend_user_input = "42"; + let x: i64 = pretend_user_input.parse()?; + println!("output={:?}", PositiveNonzeroInteger::new(x)?); + Ok(()) +} + +// Don't change anything below this line. + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + x if x < 0 => Err(CreationError::Negative), + x if x == 0 => Err(CreationError::Zero), + x => Ok(PositiveNonzeroInteger(x as u64)) + } + } +} + +// This is required so that `CreationError` can implement `error::Error`. +impl fmt::Display for CreationError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let description = match *self { + CreationError::Negative => "number is negative", + CreationError::Zero => "number is zero", + }; + f.write_str(description) + } +} + +impl error::Error for CreationError {} diff --git a/exercises/error_handling/errors6.rs b/exercises/error_handling/errors6.rs new file mode 100644 index 00000000..1306fb03 --- /dev/null +++ b/exercises/error_handling/errors6.rs @@ -0,0 +1,96 @@ +// errors6.rs + +// Using catch-all error types like `Box` isn't recommended +// for library code, where callers might want to make decisions based on the +// error content, instead of printing it out or propagating it further. Here, +// we define a custom error type to make it possible for callers to decide +// what to do next when our function returns an error. + +// Execute `rustlings hint errors6` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +use std::num::ParseIntError; + +// This is a custom error type that we will be using in `parse_pos_nonzero()`. +#[derive(PartialEq, Debug)] +enum ParsePosNonzeroError { + Creation(CreationError), + ParseInt(ParseIntError) +} + +impl ParsePosNonzeroError { + fn from_creation(err: CreationError) -> ParsePosNonzeroError { + ParsePosNonzeroError::Creation(err) + } + // TODO: add another error conversion function here. + // fn from_parseint... +} + +fn parse_pos_nonzero(s: &str) + -> Result +{ + // TODO: change this to return an appropriate error instead of panicking + // when `parse()` returns an error. + let x: i64 = s.parse().unwrap(); + PositiveNonzeroInteger::new(x) + .map_err(ParsePosNonzeroError::from_creation) +} + +// Don't change anything below this line. + +#[derive(PartialEq, Debug)] +struct PositiveNonzeroInteger(u64); + +#[derive(PartialEq, Debug)] +enum CreationError { + Negative, + Zero, +} + +impl PositiveNonzeroInteger { + fn new(value: i64) -> Result { + match value { + x if x < 0 => Err(CreationError::Negative), + x if x == 0 => Err(CreationError::Zero), + x => Ok(PositiveNonzeroInteger(x as u64)) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_parse_error() { + // We can't construct a ParseIntError, so we have to pattern match. + assert!(matches!( + parse_pos_nonzero("not a number"), + Err(ParsePosNonzeroError::ParseInt(_)) + )); + } + + #[test] + fn test_negative() { + assert_eq!( + parse_pos_nonzero("-555"), + Err(ParsePosNonzeroError::Creation(CreationError::Negative)) + ); + } + + #[test] + fn test_zero() { + assert_eq!( + parse_pos_nonzero("0"), + Err(ParsePosNonzeroError::Creation(CreationError::Zero)) + ); + } + + #[test] + fn test_positive() { + let x = PositiveNonzeroInteger::new(42); + assert!(x.is_ok()); + assert_eq!(parse_pos_nonzero("42"), Ok(x.unwrap())); + } +} diff --git a/exercises/error_handling/errorsn.rs b/exercises/error_handling/errorsn.rs deleted file mode 100644 index 5fe212bf..00000000 --- a/exercises/error_handling/errorsn.rs +++ /dev/null @@ -1,117 +0,0 @@ -// errorsn.rs -// This is a bigger error exercise than the previous ones! -// You can do it! :) -// -// Edit the `read_and_validate` function ONLY. Don't create any Errors -// that do not already exist. -// -// So many things could go wrong! -// -// - Reading from stdin could produce an io::Error -// - Parsing the input could produce a num::ParseIntError -// - Validating the input could produce a CreationError (defined below) -// -// How can we lump these errors into one general error? That is, what -// type goes where the question marks are, and how do we return -// that type from the body of read_and_validate? -// -// Execute `rustlings hint errorsn` for hints :) - -// I AM NOT DONE - -use std::error; -use std::fmt; -use std::io; - -// PositiveNonzeroInteger is a struct defined below the tests. -fn read_and_validate(b: &mut dyn io::BufRead) -> Result { - let mut line = String::new(); - b.read_line(&mut line); - let num: i64 = line.trim().parse(); - let answer = PositiveNonzeroInteger::new(num); - answer -} - -// -// Nothing below this needs to be modified -// - -// This is a test helper function that turns a &str into a BufReader. -fn test_with_str(s: &str) -> Result> { - let mut b = io::BufReader::new(s.as_bytes()); - read_and_validate(&mut b) -} - -#[test] -fn test_success() { - let x = test_with_str("42\n"); - assert_eq!(PositiveNonzeroInteger(42), x.unwrap()); -} - -#[test] -fn test_not_num() { - let x = test_with_str("eleven billion\n"); - assert!(x.is_err()); -} - -#[test] -fn test_non_positive() { - let x = test_with_str("-40\n"); - assert!(x.is_err()); -} - -#[test] -fn test_ioerror() { - struct Broken; - impl io::Read for Broken { - fn read(&mut self, _buf: &mut [u8]) -> io::Result { - Err(io::Error::new(io::ErrorKind::BrokenPipe, "uh-oh!")) - } - } - let mut b = io::BufReader::new(Broken); - assert!(read_and_validate(&mut b).is_err()); - assert_eq!("uh-oh!", read_and_validate(&mut b).unwrap_err().to_string()); -} - -#[derive(PartialEq, Debug)] -struct PositiveNonzeroInteger(u64); - -impl PositiveNonzeroInteger { - fn new(value: i64) -> Result { - if value == 0 { - Err(CreationError::Zero) - } else if value < 0 { - Err(CreationError::Negative) - } else { - Ok(PositiveNonzeroInteger(value as u64)) - } - } -} - -#[test] -fn test_positive_nonzero_integer_creation() { - assert!(PositiveNonzeroInteger::new(10).is_ok()); - assert_eq!( - Err(CreationError::Negative), - PositiveNonzeroInteger::new(-10) - ); - assert_eq!(Err(CreationError::Zero), PositiveNonzeroInteger::new(0)); -} - -#[derive(PartialEq, Debug)] -enum CreationError { - Negative, - Zero, -} - -impl fmt::Display for CreationError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let description = match *self { - CreationError::Negative => "Number is negative", - CreationError::Zero => "Number is zero", - }; - f.write_str(description) - } -} - -impl error::Error for CreationError {} diff --git a/exercises/functions/README.md b/exercises/functions/README.md index 351ae023..6662d0da 100644 --- a/exercises/functions/README.md +++ b/exercises/functions/README.md @@ -1,7 +1,8 @@ -### Functions +# Functions -Here, you'll learn how to write functions and how Rust's compiler can trace things way back. +Here, you'll learn how to write functions and how the Rust compiler can help you debug errors even +in more complex code. -#### Book Sections +## Further information - [How Functions Work](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html) diff --git a/exercises/functions/functions1.rs b/exercises/functions/functions1.rs index 31125278..03d8af70 100644 --- a/exercises/functions/functions1.rs +++ b/exercises/functions/functions1.rs @@ -1,5 +1,5 @@ // functions1.rs -// Make me compile! Execute `rustlings hint functions1` for hints :) +// Execute `rustlings hint functions1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/functions/functions2.rs b/exercises/functions/functions2.rs index 108ba38b..7d40a578 100644 --- a/exercises/functions/functions2.rs +++ b/exercises/functions/functions2.rs @@ -1,5 +1,5 @@ // functions2.rs -// Make me compile! Execute `rustlings hint functions2` for hints :) +// Execute `rustlings hint functions2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -7,7 +7,7 @@ fn main() { call_me(3); } -fn call_me(num) { +fn call_me(num:) { for i in 0..num { println!("Ring! Call number {}", i + 1); } diff --git a/exercises/functions/functions3.rs b/exercises/functions/functions3.rs index e3c1bf73..3b9e585b 100644 --- a/exercises/functions/functions3.rs +++ b/exercises/functions/functions3.rs @@ -1,5 +1,5 @@ // functions3.rs -// Make me compile! Execute `rustlings hint functions3` for hints :) +// Execute `rustlings hint functions3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -7,7 +7,7 @@ fn main() { call_me(); } -fn call_me(num: i32) { +fn call_me(num: u32) { for i in 0..num { println!("Ring! Call number {}", i + 1); } diff --git a/exercises/functions/functions4.rs b/exercises/functions/functions4.rs index 58637e4c..65d5be4f 100644 --- a/exercises/functions/functions4.rs +++ b/exercises/functions/functions4.rs @@ -1,8 +1,11 @@ // functions4.rs -// Make me compile! Execute `rustlings hint functions4` for hints :) +// Execute `rustlings hint functions4` or use the `hint` watch subcommand for a hint. // This store is having a sale where if the price is an even number, you get // 10 Rustbucks off, but if it's an odd number, it's 3 Rustbucks off. +// (Don't worry about the function bodies themselves, we're only interested +// in the signatures for now. If anything, this is a good way to peek ahead +// to future exercises!) // I AM NOT DONE diff --git a/exercises/functions/functions5.rs b/exercises/functions/functions5.rs index d22aa6c8..5d762961 100644 --- a/exercises/functions/functions5.rs +++ b/exercises/functions/functions5.rs @@ -1,11 +1,11 @@ // functions5.rs -// Make me compile! Execute `rustlings hint functions5` for hints :) +// Execute `rustlings hint functions5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let answer = square(3); - println!("The answer is {}", answer); + println!("The square of 3 is {}", answer); } fn square(num: i32) -> i32 { diff --git a/exercises/generics/README.md b/exercises/generics/README.md index 7105f06f..de46d503 100644 --- a/exercises/generics/README.md +++ b/exercises/generics/README.md @@ -1,7 +1,11 @@ -### Generics +# Generics -In this section you'll learn about saving yourself many lines of code with generics! +Generics is the topic of generalizing types and functionalities to broader cases. +This is extremely useful for reducing code duplication in many ways, but can call for rather involving syntax. +Namely, being generic requires taking great care to specify over which types a generic type is actually considered valid. +The simplest and most common use of generics is for type parameters. -### Book Sections +## Further information -- [Generic Data Types](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html) \ No newline at end of file +- [Generic Data Types](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html) +- [Bounds](https://doc.rust-lang.org/rust-by-example/generics/bounds.html) diff --git a/exercises/generics/generics1.rs b/exercises/generics/generics1.rs index 967287ef..4c34ae47 100644 --- a/exercises/generics/generics1.rs +++ b/exercises/generics/generics1.rs @@ -1,6 +1,8 @@ // This shopping list program isn't compiling! // Use your knowledge of generics to fix it. +// Execute `rustlings hint generics1` or use the `hint` watch subcommand for a hint. + // I AM NOT DONE fn main() { diff --git a/exercises/generics/generics2.rs b/exercises/generics/generics2.rs index 0cb59adc..aedbd55c 100644 --- a/exercises/generics/generics2.rs +++ b/exercises/generics/generics2.rs @@ -1,6 +1,8 @@ // This powerful wrapper provides the ability to store a positive integer value. // Rewrite it using generics so that it supports wrapping ANY type. +// Execute `rustlings hint generics2` or use the `hint` watch subcommand for a hint. + // I AM NOT DONE struct Wrapper { diff --git a/exercises/generics/generics3.rs b/exercises/generics/generics3.rs deleted file mode 100644 index 64dd9bc1..00000000 --- a/exercises/generics/generics3.rs +++ /dev/null @@ -1,58 +0,0 @@ -// An imaginary magical school has a new report card generation system written in Rust! -// Currently the system only supports creating report cards where the student's grade -// is represented numerically (e.g. 1.0 -> 5.5). -// However, the school also issues alphabetical grades (A+ -> F-) and needs -// to be able to print both types of report card! - -// Make the necessary code changes in the struct ReportCard and the impl block -// to support alphabetical report cards. Change the Grade in the second test to "A+" -// to show that your changes allow alphabetical grades. - -// Execute 'rustlings hint generics3' for hints! - -// I AM NOT DONE - -pub struct ReportCard { - pub grade: f32, - pub student_name: String, - pub student_age: u8, -} - -impl ReportCard { - pub fn print(&self) -> String { - format!("{} ({}) - achieved a grade of {}", - &self.student_name, &self.student_age, &self.grade) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn generate_numeric_report_card() { - let report_card = ReportCard { - grade: 2.1, - student_name: "Tom Wriggle".to_string(), - student_age: 12, - }; - assert_eq!( - report_card.print(), - "Tom Wriggle (12) - achieved a grade of 2.1" - ); - } - - #[test] - fn generate_alphabetic_report_card() { - // TODO: Make sure to change the grade here after you finish the exercise. - let report_card = ReportCard { - grade: 2.1, - student_name: "Gary Plotter".to_string(), - student_age: 11, - }; - assert_eq!( - report_card.print(), - "Gary Plotter (11) - achieved a grade of A+" - ); - } -} diff --git a/exercises/hashmaps/README.md b/exercises/hashmaps/README.md new file mode 100644 index 00000000..30471cf9 --- /dev/null +++ b/exercises/hashmaps/README.md @@ -0,0 +1,11 @@ +# Hashmaps +A *hash map* allows you to associate a value with a particular key. +You may also know this by the names [*unordered map* in C++](https://en.cppreference.com/w/cpp/container/unordered_map), +[*dictionary* in Python](https://docs.python.org/3/tutorial/datastructures.html#dictionaries) or an *associative array* in other languages. + +This is the other data structure that we've been talking about before, when +talking about Vecs. + +## Further information + +- [Storing Keys with Associated Values in Hash Maps](https://doc.rust-lang.org/book/ch08-03-hash-maps.html) diff --git a/exercises/collections/hashmap1.rs b/exercises/hashmaps/hashmaps1.rs similarity index 85% rename from exercises/collections/hashmap1.rs rename to exercises/hashmaps/hashmaps1.rs index b1dc0bbd..fd8dd2f8 100644 --- a/exercises/collections/hashmap1.rs +++ b/exercises/hashmaps/hashmaps1.rs @@ -1,4 +1,4 @@ -// hashmap1.rs +// hashmaps1.rs // A basket of fruits in the form of a hash map needs to be defined. // The key represents the name of the fruit and the value represents // how many of that particular fruit is in the basket. You have to put @@ -8,8 +8,7 @@ // // Make me compile and pass the tests! // -// Execute the command `rustlings hint collections3` if you need -// hints. +// Execute `rustlings hint hashmaps1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -39,8 +38,6 @@ mod tests { #[test] fn at_least_five_fruits() { let basket = fruit_basket(); - assert!(basket - .values() - .sum::() >= 5); + assert!(basket.values().sum::() >= 5); } } diff --git a/exercises/collections/hashmap2.rs b/exercises/hashmaps/hashmaps2.rs similarity index 81% rename from exercises/collections/hashmap2.rs rename to exercises/hashmaps/hashmaps2.rs index 7e25be7c..454b3e1d 100644 --- a/exercises/collections/hashmap2.rs +++ b/exercises/hashmaps/hashmaps2.rs @@ -1,16 +1,15 @@ -// hashmap2.rs +// hashmaps2.rs // A basket of fruits in the form of a hash map is given. The key // represents the name of the fruit and the value represents how many // of that particular fruit is in the basket. You have to put *MORE // THAN 11* fruits in the basket. Three types of fruits - Apple (4), -// Mango (2) and Lichi (5) are already given in the basket. You are +// Mango (2) and Lychee (5) are already given in the basket. You are // not allowed to insert any more of these fruits! // // Make me pass the tests! // -// Execute the command `rustlings hint collections4` if you need -// hints. +// Execute `rustlings hint hashmaps2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -21,7 +20,7 @@ enum Fruit { Apple, Banana, Mango, - Lichi, + Lychee, Pineapple, } @@ -30,7 +29,7 @@ fn fruit_basket(basket: &mut HashMap) { Fruit::Apple, Fruit::Banana, Fruit::Mango, - Fruit::Lichi, + Fruit::Lychee, Fruit::Pineapple, ]; @@ -49,7 +48,7 @@ mod tests { let mut basket = HashMap::::new(); basket.insert(Fruit::Apple, 4); basket.insert(Fruit::Mango, 2); - basket.insert(Fruit::Lichi, 5); + basket.insert(Fruit::Lychee, 5); basket } @@ -60,7 +59,7 @@ mod tests { fruit_basket(&mut basket); assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4); assert_eq!(*basket.get(&Fruit::Mango).unwrap(), 2); - assert_eq!(*basket.get(&Fruit::Lichi).unwrap(), 5); + assert_eq!(*basket.get(&Fruit::Lychee).unwrap(), 5); } #[test] @@ -68,16 +67,14 @@ mod tests { let mut basket = get_fruit_basket(); fruit_basket(&mut basket); let count_fruit_kinds = basket.len(); - assert!(count_fruit_kinds == 5); + assert!(count_fruit_kinds >= 5); } #[test] fn greater_than_eleven_fruits() { let mut basket = get_fruit_basket(); fruit_basket(&mut basket); - let count = basket - .values() - .sum::(); + let count = basket.values().sum::(); assert!(count > 11); } } diff --git a/exercises/hashmaps/hashmaps3.rs b/exercises/hashmaps/hashmaps3.rs new file mode 100644 index 00000000..18dd44c9 --- /dev/null +++ b/exercises/hashmaps/hashmaps3.rs @@ -0,0 +1,87 @@ +// hashmaps3.rs + +// A list of scores (one per line) of a soccer match is given. Each line +// is of the form : +// ,,, +// Example: England,France,4,2 (England scored 4 goals, France 2). + +// You have to build a scores table containing the name of the team, goals +// the team scored, and goals the team conceded. One approach to build +// the scores table is to use a Hashmap. The solution is partially +// written to use a Hashmap, complete it to pass the test. + +// Make me pass the tests! + +// Execute `rustlings hint hashmaps3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +use std::collections::HashMap; + +// A structure to store team name and its goal details. +struct Team { + name: String, + goals_scored: u8, + goals_conceded: u8, +} + +fn build_scores_table(results: String) -> HashMap { + // The name of the team is the key and its associated struct is the value. + let mut scores: HashMap = HashMap::new(); + + for r in results.lines() { + let v: Vec<&str> = r.split(',').collect(); + let team_1_name = v[0].to_string(); + let team_1_score: u8 = v[2].parse().unwrap(); + let team_2_name = v[1].to_string(); + let team_2_score: u8 = v[3].parse().unwrap(); + // TODO: Populate the scores table with details extracted from the + // current line. Keep in mind that goals scored by team_1 + // will be number of goals conceded from team_2, and similarly + // goals scored by team_2 will be the number of goals conceded by + // team_1. + } + scores +} + +#[cfg(test)] +mod tests { + use super::*; + + fn get_results() -> String { + let results = "".to_string() + + "England,France,4,2\n" + + "France,Italy,3,1\n" + + "Poland,Spain,2,0\n" + + "Germany,England,2,1\n"; + results + } + + #[test] + fn build_scores() { + let scores = build_scores_table(get_results()); + + let mut keys: Vec<&String> = scores.keys().collect(); + keys.sort(); + assert_eq!( + keys, + vec!["England", "France", "Germany", "Italy", "Poland", "Spain"] + ); + } + + #[test] + fn validate_team_score_1() { + let scores = build_scores_table(get_results()); + let team = scores.get("England").unwrap(); + assert_eq!(team.goals_scored, 5); + assert_eq!(team.goals_conceded, 4); + } + + #[test] + fn validate_team_score_2() { + let scores = build_scores_table(get_results()); + let team = scores.get("Spain").unwrap(); + assert_eq!(team.goals_scored, 0); + assert_eq!(team.goals_conceded, 2); + } +} diff --git a/exercises/if/README.md b/exercises/if/README.md index b1157218..b52c3922 100644 --- a/exercises/if/README.md +++ b/exercises/if/README.md @@ -1,7 +1,7 @@ -### If +# If -`if`, the most basic type of control flow, is what you'll learn here. +`if`, the most basic (but still surprisingly versatile!) type of control flow, is what you'll learn here. -#### Book Sections +## Further information - [Control Flow - if expressions](https://doc.rust-lang.org/book/ch03-05-control-flow.html#if-expressions) diff --git a/exercises/if/if1.rs b/exercises/if/if1.rs index 90867545..587e03f8 100644 --- a/exercises/if/if1.rs +++ b/exercises/if/if1.rs @@ -1,4 +1,5 @@ // if1.rs +// Execute `rustlings hint if1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -7,7 +8,6 @@ pub fn bigger(a: i32, b: i32) -> i32 { // Do not use: // - another function call // - additional variables - // Execute `rustlings hint if1` for hints } // Don't mind this for now :) diff --git a/exercises/if/if2.rs b/exercises/if/if2.rs index 80effbdf..effddbb6 100644 --- a/exercises/if/if2.rs +++ b/exercises/if/if2.rs @@ -2,11 +2,11 @@ // Step 1: Make me compile! // Step 2: Get the bar_for_fuzz and default_to_baz tests passing! -// Execute the command `rustlings hint if2` if you want a hint :) +// Execute `rustlings hint if2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE -pub fn fizz_if_foo(fizzish: &str) -> &str { +pub fn foo_if_fizz(fizzish: &str) -> &str { if fizzish == "fizz" { "foo" } else { @@ -21,16 +21,16 @@ mod tests { #[test] fn foo_for_fizz() { - assert_eq!(fizz_if_foo("fizz"), "foo") + assert_eq!(foo_if_fizz("fizz"), "foo") } #[test] fn bar_for_fuzz() { - assert_eq!(fizz_if_foo("fuzz"), "bar") + assert_eq!(foo_if_fizz("fuzz"), "bar") } #[test] fn default_to_baz() { - assert_eq!(fizz_if_foo("literally anything"), "baz") + assert_eq!(foo_if_fizz("literally anything"), "baz") } } diff --git a/exercises/intro/README.md b/exercises/intro/README.md new file mode 100644 index 00000000..d32e4a8b --- /dev/null +++ b/exercises/intro/README.md @@ -0,0 +1,8 @@ +# Intro + +Rust uses the `print!` and `println!` macros to print text to the console. + +## Further information + +- [Hello World](https://doc.rust-lang.org/rust-by-example/hello.html) +- [Formatted print](https://doc.rust-lang.org/rust-by-example/hello/print.html) diff --git a/exercises/intro/intro1.rs b/exercises/intro/intro1.rs new file mode 100644 index 00000000..45c5acba --- /dev/null +++ b/exercises/intro/intro1.rs @@ -0,0 +1,30 @@ +// intro1.rs +// About this `I AM NOT DONE` thing: +// We sometimes encourage you to keep trying things on a given exercise, even +// after you already figured it out. If you got everything working and feel +// ready for the next exercise, remove the `I AM NOT DONE` comment below. +// Execute `rustlings hint intro1` or use the `hint` watch subcommand for a hint. +// +// If you're running this using `rustlings watch`: The exercise file will be reloaded +// when you change one of the lines below! Try adding a `println!` line, or try changing +// what it outputs in your terminal. Try removing a semicolon and see what happens! + +// I AM NOT DONE + +fn main() { + println!("Hello and"); + println!(r#" welcome to... "#); + println!(r#" _ _ _ "#); + println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#); + println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#); + println!(r#" | | | |_| \__ \ |_| | | | | | (_| \__ \ "#); + println!(r#" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ "#); + println!(r#" |___/ "#); + println!(); + println!("This exercise compiles successfully. The remaining exercises contain a compiler"); + println!("or logic error. The central concept behind Rustlings is to fix these errors and"); + println!("solve the exercises. Good luck!"); + println!(); + println!("The source for this exercise is in `exercises/intro/intro1.rs`. Have a look!"); + println!("Going forward, the source of the exercises will always be in the success/failure output."); +} diff --git a/exercises/intro/intro2.rs b/exercises/intro/intro2.rs new file mode 100644 index 00000000..efc1af20 --- /dev/null +++ b/exercises/intro/intro2.rs @@ -0,0 +1,9 @@ +// intro2.rs +// Make the code print a greeting to the world. +// Execute `rustlings hint intro2` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +fn main() { + println!("Hello {}!"); +} diff --git a/exercises/lifetimes/README.md b/exercises/lifetimes/README.md new file mode 100644 index 00000000..72befb3e --- /dev/null +++ b/exercises/lifetimes/README.md @@ -0,0 +1,17 @@ +# Lifetimes + +Lifetimes tell the compiler how to check whether references live long +enough to be valid in any given situation. For example lifetimes say +"make sure parameter 'a' lives as long as parameter 'b' so that the return +value is valid". + +They are only necessary on borrows, i.e. references, +since copied parameters or moves are owned in their scope and cannot +be referenced outside. Lifetimes mean that calling code of e.g. functions +can be checked to make sure their arguments are valid. Lifetimes are +restrictive of their callers. + +## Further information + +- [Validating References with Lifetimes](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html) +- [Lifetimes (in Rust By Example)](https://doc.rust-lang.org/stable/rust-by-example/scope/lifetime.html) diff --git a/exercises/lifetimes/lifetimes1.rs b/exercises/lifetimes/lifetimes1.rs new file mode 100644 index 00000000..58e995c6 --- /dev/null +++ b/exercises/lifetimes/lifetimes1.rs @@ -0,0 +1,26 @@ +// lifetimes1.rs +// +// The Rust compiler needs to know how to check whether supplied references are +// valid, so that it can let the programmer know if a reference is at risk +// of going out of scope before it is used. Remember, references are borrows +// and do not own their own data. What if their owner goes out of scope? +// +// Execute `rustlings hint lifetimes1` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +fn longest(x: &str, y: &str) -> &str { + if x.len() > y.len() { + x + } else { + y + } +} + +fn main() { + let string1 = String::from("abcd"); + let string2 = "xyz"; + + let result = longest(string1.as_str(), string2); + println!("The longest string is {}", result); +} diff --git a/exercises/lifetimes/lifetimes2.rs b/exercises/lifetimes/lifetimes2.rs new file mode 100644 index 00000000..c73a28ad --- /dev/null +++ b/exercises/lifetimes/lifetimes2.rs @@ -0,0 +1,27 @@ +// lifetimes2.rs +// +// So if the compiler is just validating the references passed +// to the annotated parameters and the return type, what do +// we need to change? +// +// Execute `rustlings hint lifetimes2` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + if x.len() > y.len() { + x + } else { + y + } +} + +fn main() { + let string1 = String::from("long string is long"); + let result; + { + let string2 = String::from("xyz"); + result = longest(string1.as_str(), string2.as_str()); + } + println!("The longest string is {}", result); +} diff --git a/exercises/lifetimes/lifetimes3.rs b/exercises/lifetimes/lifetimes3.rs new file mode 100644 index 00000000..ea483708 --- /dev/null +++ b/exercises/lifetimes/lifetimes3.rs @@ -0,0 +1,20 @@ +// lifetimes3.rs +// +// Lifetimes are also needed when structs hold references. +// +// Execute `rustlings hint lifetimes3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +struct Book { + author: &str, + title: &str, +} + +fn main() { + let name = String::from("Jill Smith"); + let title = String::from("Fish Flying"); + let book = Book { author: &name, title: &title }; + + println!("{} by {}", book.title, book.author); +} diff --git a/exercises/macros/README.md b/exercises/macros/README.md index b48b880a..31a941b7 100644 --- a/exercises/macros/README.md +++ b/exercises/macros/README.md @@ -1,10 +1,10 @@ -### Macros +# Macros Rust's macro system is very powerful, but also kind of difficult to wrap your head around. We're not going to teach you how to write your own fully-featured macros. Instead, we'll show you how to use and create them. -#### Book Sections +## Further information - [Macros](https://doc.rust-lang.org/book/ch19-06-macros.html) -- [The Little Book of Rust Macros](https://danielkeep.github.io/tlborm/book/index.html) +- [The Little Book of Rust Macros](https://veykril.github.io/tlborm/) diff --git a/exercises/macros/macros1.rs b/exercises/macros/macros1.rs index ed0dac85..634d0a70 100644 --- a/exercises/macros/macros1.rs +++ b/exercises/macros/macros1.rs @@ -1,5 +1,5 @@ // macros1.rs -// Make me compile! Execute `rustlings hint macros1` for hints :) +// Execute `rustlings hint macros1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/macros/macros2.rs b/exercises/macros/macros2.rs index d0be1236..f6092cab 100644 --- a/exercises/macros/macros2.rs +++ b/exercises/macros/macros2.rs @@ -1,5 +1,5 @@ // macros2.rs -// Make me compile! Execute `rustlings hint macros2` for hints :) +// Execute `rustlings hint macros2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/macros/macros3.rs b/exercises/macros/macros3.rs index 93a43113..106f1c6d 100644 --- a/exercises/macros/macros3.rs +++ b/exercises/macros/macros3.rs @@ -1,6 +1,6 @@ // macros3.rs // Make me compile, without taking the macro out of the module! -// Execute `rustlings hint macros3` for hints :) +// Execute `rustlings hint macros3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/macros/macros4.rs b/exercises/macros/macros4.rs index 3a748078..c1fc5e8b 100644 --- a/exercises/macros/macros4.rs +++ b/exercises/macros/macros4.rs @@ -1,5 +1,5 @@ // macros4.rs -// Make me compile! Execute `rustlings hint macros4` for hints :) +// Execute `rustlings hint macros4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/modules/README.md b/exercises/modules/README.md index bb765106..3dc8a482 100644 --- a/exercises/modules/README.md +++ b/exercises/modules/README.md @@ -1,7 +1,7 @@ -### Modules +# Modules In this section we'll give you an introduction to Rust's module system. -#### Book Sections +## Further information -- [The Module System](https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html) +- [The Module System](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html) diff --git a/exercises/modules/modules1.rs b/exercises/modules/modules1.rs index 812dfeef..8dd0e402 100644 --- a/exercises/modules/modules1.rs +++ b/exercises/modules/modules1.rs @@ -1,10 +1,16 @@ // modules1.rs -// Make me compile! Execute `rustlings hint modules1` for hints :) +// Execute `rustlings hint modules1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE mod sausage_factory { + // Don't let anybody outside of this module see this! + fn get_secret_recipe() -> String { + String::from("Ginger") + } + fn make_sausage() { + get_secret_recipe(); println!("sausage!"); } } diff --git a/exercises/modules/modules2.rs b/exercises/modules/modules2.rs index fde439d1..c30a3897 100644 --- a/exercises/modules/modules2.rs +++ b/exercises/modules/modules2.rs @@ -1,11 +1,14 @@ // modules2.rs -// Make me compile! Execute `rustlings hint modules2` for hints :) +// You can bring module paths into scopes and provide new names for them with the +// 'use' and 'as' keywords. Fix these 'use' statements to make the code compile. +// Execute `rustlings hint modules2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE mod delicious_snacks { - use self::fruits::PEAR as fruit; - use self::veggies::CUCUMBER as veggie; + // TODO: Fix these use statements + use self::fruits::PEAR as ??? + use self::veggies::CUCUMBER as ??? mod fruits { pub const PEAR: &'static str = "Pear"; diff --git a/exercises/modules/modules3.rs b/exercises/modules/modules3.rs new file mode 100644 index 00000000..35e07990 --- /dev/null +++ b/exercises/modules/modules3.rs @@ -0,0 +1,18 @@ +// modules3.rs +// You can use the 'use' keyword to bring module paths from modules from anywhere +// and especially from the Rust standard library into your scope. +// Bring SystemTime and UNIX_EPOCH +// from the std::time module. Bonus style points if you can do it with one line! +// Execute `rustlings hint modules3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +// TODO: Complete this use statement +use ??? + +fn main() { + match SystemTime::now().duration_since(UNIX_EPOCH) { + Ok(n) => println!("1970-01-01 00:00:00 UTC was {} seconds ago!", n.as_secs()), + Err(_) => panic!("SystemTime before UNIX EPOCH!"), + } +} diff --git a/exercises/move_semantics/README.md b/exercises/move_semantics/README.md index 6842af7c..54ddd8e6 100644 --- a/exercises/move_semantics/README.md +++ b/exercises/move_semantics/README.md @@ -1,8 +1,8 @@ -### Move Semantics +# Move Semantics These exercises are adapted from [pnkfelix](https://github.com/pnkfelix)'s [Rust Tutorial](https://pnkfelix.github.io/rust-examples-icfp2014/) -- Thank you Felix!!! -#### Book Sections +## Further information For this section, the book links are especially important. diff --git a/exercises/move_semantics/move_semantics1.rs b/exercises/move_semantics/move_semantics1.rs index e2f5876d..aac6dfc3 100644 --- a/exercises/move_semantics/move_semantics1.rs +++ b/exercises/move_semantics/move_semantics1.rs @@ -1,5 +1,5 @@ // move_semantics1.rs -// Make me compile! Execute `rustlings hint move_semantics1` for hints :) +// Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics2.rs b/exercises/move_semantics/move_semantics2.rs index bd21fbb7..64870850 100644 --- a/exercises/move_semantics/move_semantics2.rs +++ b/exercises/move_semantics/move_semantics2.rs @@ -1,6 +1,6 @@ // move_semantics2.rs -// Make me compile without changing line 13! -// Execute `rustlings hint move_semantics2` for hints :) +// Make me compile without changing line 13 or moving line 10! +// Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics3.rs b/exercises/move_semantics/move_semantics3.rs index 43fef74f..eaa30e33 100644 --- a/exercises/move_semantics/move_semantics3.rs +++ b/exercises/move_semantics/move_semantics3.rs @@ -1,7 +1,7 @@ // move_semantics3.rs // Make me compile without adding new lines-- just changing existing lines! // (no lines with multiple semicolons necessary!) -// Execute `rustlings hint move_semantics3` for hints :) +// Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/move_semantics/move_semantics4.rs b/exercises/move_semantics/move_semantics4.rs index a1c4a413..99834ec3 100644 --- a/exercises/move_semantics/move_semantics4.rs +++ b/exercises/move_semantics/move_semantics4.rs @@ -1,8 +1,8 @@ // move_semantics4.rs -// Refactor this code so that instead of having `vec0` and creating the vector -// in `fn main`, we instead create it within `fn fill_vec` and transfer the -// freshly created vector from fill_vec to its caller. -// Execute `rustlings hint move_semantics4` for hints! +// Refactor this code so that instead of passing `vec0` into the `fill_vec` function, +// the Vector gets created in the function itself and passed back to the main +// function. +// Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -18,7 +18,7 @@ fn main() { println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } -// `fill_vec()` no longer take `vec: Vec` as argument +// `fill_vec()` no longer takes `vec: Vec` as argument fn fill_vec() -> Vec { let mut vec = vec; diff --git a/exercises/move_semantics/move_semantics5.rs b/exercises/move_semantics/move_semantics5.rs new file mode 100644 index 00000000..36eae127 --- /dev/null +++ b/exercises/move_semantics/move_semantics5.rs @@ -0,0 +1,15 @@ +// move_semantics5.rs +// Make me compile only by reordering the lines in `main()`, but without +// adding, changing or removing any of them. +// Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +fn main() { + let mut x = 100; + let y = &mut x; + let z = &mut x; + *y += 100; + *z += 1000; + assert_eq!(x, 1200); +} diff --git a/exercises/move_semantics/move_semantics6.rs b/exercises/move_semantics/move_semantics6.rs new file mode 100644 index 00000000..eb52a848 --- /dev/null +++ b/exercises/move_semantics/move_semantics6.rs @@ -0,0 +1,25 @@ +// move_semantics6.rs +// Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand for a hint. +// You can't change anything except adding or removing references. + +// I AM NOT DONE + +fn main() { + let data = "Rust is great!".to_string(); + + get_char(data); + + string_uppercase(&data); +} + +// Should not take ownership +fn get_char(data: String) -> char { + data.chars().last().unwrap() +} + +// Should take ownership +fn string_uppercase(mut data: &String) { + data = &data.to_uppercase(); + + println!("{}", data); +} diff --git a/exercises/option/README.md b/exercises/option/README.md deleted file mode 100644 index d17b79cc..00000000 --- a/exercises/option/README.md +++ /dev/null @@ -1,9 +0,0 @@ -### Option - -#### Book Sections - -To learn about Option, check out these links: - -- [Option Enum Format](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-enum-definitions) -- [Option Module Documentation](https://doc.rust-lang.org/std/option/) -- [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html) diff --git a/exercises/option/option1.rs b/exercises/option/option1.rs deleted file mode 100644 index 602ff1a9..00000000 --- a/exercises/option/option1.rs +++ /dev/null @@ -1,23 +0,0 @@ -// option1.rs -// Make me compile! Execute `rustlings hint option1` for hints - -// I AM NOT DONE - -// you can modify anything EXCEPT for this function's sig -fn print_number(maybe_number: Option) { - println!("printing: {}", maybe_number.unwrap()); -} - -fn main() { - print_number(13); - print_number(99); - - let mut numbers: [Option; 5]; - for iter in 0..5 { - let number_to_add: u16 = { - ((iter * 1235) + 2) / (4 * 16) - }; - - numbers[iter as usize] = number_to_add; - } -} diff --git a/exercises/option/option2.rs b/exercises/option/option2.rs deleted file mode 100644 index a1517d7c..00000000 --- a/exercises/option/option2.rs +++ /dev/null @@ -1,25 +0,0 @@ -// option2.rs -// Make me compile! Execute `rustlings hint option2` for hints - -// I AM NOT DONE - -fn main() { - let optional_value = Some(String::from("rustlings")); - // TODO: Make this an if let statement whose value is "Some" type - value = optional_value { - println!("the value of optional value is: {}", value); - } else { - println!("The optional value doesn't contain anything!"); - } - - let mut optional_values_vec: Vec> = Vec::new(); - for x in 1..10 { - optional_values_vec.push(Some(x)); - } - - // TODO: make this a while let statement - remember that vector.pop also adds another layer of Option - // You can stack `Option`'s into while let and if let - value = optional_values_vec.pop() { - println!("current value: {}", value); - } -} diff --git a/exercises/options/README.md b/exercises/options/README.md new file mode 100644 index 00000000..6140a167 --- /dev/null +++ b/exercises/options/README.md @@ -0,0 +1,20 @@ +# Options + +Type Option represents an optional value: every Option is either Some and contains a value, or None, and does not. +Option types are very common in Rust code, as they have a number of uses: +- Initial values +- Return values for functions that are not defined over their entire input range (partial functions) +- Return value for otherwise reporting simple errors, where None is returned on error +- Optional struct fields +- Struct fields that can be loaned or "taken" +- Optional function arguments +- Nullable pointers +- Swapping things out of difficult situations + +## Further Information + +- [Option Enum Format](https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-enum-definitions) +- [Option Module Documentation](https://doc.rust-lang.org/std/option/) +- [Option Enum Documentation](https://doc.rust-lang.org/std/option/enum.Option.html) +- [if let](https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html) +- [while let](https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html) diff --git a/exercises/options/options1.rs b/exercises/options/options1.rs new file mode 100644 index 00000000..d1735c2f --- /dev/null +++ b/exercises/options/options1.rs @@ -0,0 +1,35 @@ +// options1.rs +// Execute `rustlings hint options1` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +// This function returns how much icecream there is left in the fridge. +// If it's before 10PM, there's 5 pieces left. At 10PM, someone eats them +// all, so there'll be no more left :( +// TODO: Return an Option! +fn maybe_icecream(time_of_day: u16) -> Option { + // We use the 24-hour system here, so 10PM is a value of 22 + // The Option output should gracefully handle cases where time_of_day > 24. + ??? +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_icecream() { + assert_eq!(maybe_icecream(9), Some(5)); + assert_eq!(maybe_icecream(10), Some(5)); + assert_eq!(maybe_icecream(23), Some(0)); + assert_eq!(maybe_icecream(22), Some(0)); + assert_eq!(maybe_icecream(25), None); + } + + #[test] + fn raw_value() { + // TODO: Fix this test. How do you get at the value contained in the Option? + let icecreams = maybe_icecream(12); + assert_eq!(icecreams, 5); + } +} diff --git a/exercises/options/options2.rs b/exercises/options/options2.rs new file mode 100644 index 00000000..b1120471 --- /dev/null +++ b/exercises/options/options2.rs @@ -0,0 +1,36 @@ +// options2.rs +// Execute `rustlings hint options2` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simple_option() { + let target = "rustlings"; + let optional_target = Some(target); + + // TODO: Make this an if let statement whose value is "Some" type + word = optional_target { + assert_eq!(word, target); + } + } + + #[test] + fn layered_option() { + let mut range = 10; + let mut optional_integers: Vec> = Vec::new(); + for i in 0..(range + 1) { + optional_integers.push(Some(i)); + } + + // TODO: make this a while let statement - remember that vector.pop also adds another layer of Option + // You can stack `Option`'s into while let and if let + integer = optional_integers.pop() { + assert_eq!(integer, range); + range -= 1; + } + } +} diff --git a/exercises/options/options3.rs b/exercises/options/options3.rs new file mode 100644 index 00000000..3f995c52 --- /dev/null +++ b/exercises/options/options3.rs @@ -0,0 +1,19 @@ +// options3.rs +// Execute `rustlings hint options3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +struct Point { + x: i32, + y: i32, +} + +fn main() { + let y: Option = Some(Point { x: 100, y: 200 }); + + match y { + Some(p) => println!("Co-ordinates are {},{} ", p.x, p.y), + _ => println!("no match"), + } + y; // Fix without deleting this line. +} diff --git a/exercises/primitive_types/README.md b/exercises/primitive_types/README.md index daa70eea..cea69b02 100644 --- a/exercises/primitive_types/README.md +++ b/exercises/primitive_types/README.md @@ -1,9 +1,9 @@ -### Primitive Types +# Primitive Types Rust has a couple of basic types that are directly implemented into the compiler. In this section, we'll go through the most important ones. -#### Book Sections +## Further information - [Data Types](https://doc.rust-lang.org/stable/book/ch03-02-data-types.html) - [The Slice Type](https://doc.rust-lang.org/stable/book/ch04-03-slices.html) diff --git a/exercises/primitive_types/primitive_types2.rs b/exercises/primitive_types/primitive_types2.rs index 6576a4d5..8730baab 100644 --- a/exercises/primitive_types/primitive_types2.rs +++ b/exercises/primitive_types/primitive_types2.rs @@ -7,6 +7,8 @@ fn main() { // Characters (`char`) + // Note the _single_ quotes, these are different from the double quotes + // you've been seeing around. let my_first_initial = 'C'; if my_first_initial.is_alphabetic() { println!("Alphabetical!"); diff --git a/exercises/primitive_types/primitive_types3.rs b/exercises/primitive_types/primitive_types3.rs index aaa518be..fa7d019a 100644 --- a/exercises/primitive_types/primitive_types3.rs +++ b/exercises/primitive_types/primitive_types3.rs @@ -1,6 +1,6 @@ // primitive_types3.rs // Create an array with at least 100 elements in it where the ??? is. -// Execute `rustlings hint primitive_types3` for hints! +// Execute `rustlings hint primitive_types3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types4.rs b/exercises/primitive_types/primitive_types4.rs index 10b553e9..71fa243c 100644 --- a/exercises/primitive_types/primitive_types4.rs +++ b/exercises/primitive_types/primitive_types4.rs @@ -1,6 +1,6 @@ // primitive_types4.rs // Get a slice out of Array a where the ??? is so that the test passes. -// Execute `rustlings hint primitive_types4` for hints!! +// Execute `rustlings hint primitive_types4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types5.rs b/exercises/primitive_types/primitive_types5.rs index 680d8d23..4fd9141f 100644 --- a/exercises/primitive_types/primitive_types5.rs +++ b/exercises/primitive_types/primitive_types5.rs @@ -1,6 +1,6 @@ // primitive_types5.rs // Destructure the `cat` tuple so that the println will work. -// Execute `rustlings hint primitive_types5` for hints! +// Execute `rustlings hint primitive_types5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/primitive_types/primitive_types6.rs b/exercises/primitive_types/primitive_types6.rs index b8c9b82b..ddf8b423 100644 --- a/exercises/primitive_types/primitive_types6.rs +++ b/exercises/primitive_types/primitive_types6.rs @@ -1,7 +1,7 @@ // primitive_types6.rs // Use a tuple index to access the second element of `numbers`. // You can put the expression for the second element where ??? is so that the test passes. -// Execute `rustlings hint primitive_types6` for hints! +// Execute `rustlings hint primitive_types6` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/quiz1.rs b/exercises/quiz1.rs index 5c5c355d..d1e76e59 100644 --- a/exercises/quiz1.rs +++ b/exercises/quiz1.rs @@ -2,22 +2,25 @@ // This is a quiz for the following sections: // - Variables // - Functions +// - If // Mary is buying apples. One apple usually costs 2 Rustbucks, but if you buy -// more than 40 at once, each apple only costs 1! Write a function that calculates -// the price of an order of apples given the order amount. No hints this time! +// 40 or more at once, each apple only costs 1! Write a function that calculates +// the price of an order of apples given the quantity bought. No hints this time! // I AM NOT DONE // Put your function here! -// fn ..... { +// fn calculate_price_of_apples { // Don't modify this function! #[test] fn verify_test() { - let price1 = calculate_apple_price(35); - let price2 = calculate_apple_price(65); + let price1 = calculate_price_of_apples(35); + let price2 = calculate_price_of_apples(40); + let price3 = calculate_price_of_apples(65); assert_eq!(70, price1); - assert_eq!(65, price2); + assert_eq!(80, price2); + assert_eq!(65, price3); } diff --git a/exercises/quiz2.rs b/exercises/quiz2.rs index de0dce95..d8fa954a 100644 --- a/exercises/quiz2.rs +++ b/exercises/quiz2.rs @@ -1,30 +1,62 @@ // quiz2.rs // This is a quiz for the following sections: // - Strings +// - Vecs +// - Move semantics +// - Modules +// - Enums -// Ok, here are a bunch of values-- some are `String`s, some are `&str`s. Your -// task is to call one of these two functions on each value depending on what -// you think each value is. That is, add either `string_slice` or `string` -// before the parentheses on each line. If you're right, it will compile! +// Let's build a little machine in form of a function. +// As input, we're going to give a list of strings and commands. These commands +// determine what action is going to be applied to the string. It can either be: +// - Uppercase the string +// - Trim the string +// - Append "bar" to the string a specified amount of times +// The exact form of this will be: +// - The input is going to be a Vector of a 2-length tuple, +// the first element is the string, the second one is the command. +// - The output element is going to be a Vector of strings. +// Execute `rustlings hint quiz2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE -fn string_slice(arg: &str) { - println!("{}", arg); -} -fn string(arg: String) { - println!("{}", arg); +pub enum Command { + Uppercase, + Trim, + Append(usize), } -fn main() { - ???("blue"); - ???("red".to_string()); - ???(String::from("hi")); - ???("rust is fun!".to_owned()); - ???("nice weather".into()); - ???(format!("Interpolation {}", "Station")); - ???(&String::from("abc")[0..1]); - ???(" hello there ".trim()); - ???("Happy Monday!".to_string().replace("Mon", "Tues")); - ???("mY sHiFt KeY iS sTiCkY".to_lowercase()); +mod my_module { + use super::Command; + + // TODO: Complete the function signature! + pub fn transformer(input: ???) -> ??? { + // TODO: Complete the output declaration! + let mut output: ??? = vec![]; + for (string, command) in input.iter() { + // TODO: Complete the function body. You can do it! + } + output + } +} + +#[cfg(test)] +mod tests { + // TODO: What do we have to import to have `transformer` in scope? + use ???; + use super::Command; + + #[test] + fn it_works() { + let output = transformer(vec![ + ("hello".into(), Command::Uppercase), + (" all roads lead to rome! ".into(), Command::Trim), + ("foo".into(), Command::Append(1)), + ("bar".into(), Command::Append(5)), + ]); + assert_eq!(output[0], "HELLO"); + assert_eq!(output[1], "all roads lead to rome!"); + assert_eq!(output[2], "foobar"); + assert_eq!(output[3], "barbarbarbarbarbar"); + } } diff --git a/exercises/quiz3.rs b/exercises/quiz3.rs index a0cd3712..15dc4699 100644 --- a/exercises/quiz3.rs +++ b/exercises/quiz3.rs @@ -1,16 +1,32 @@ // quiz3.rs -// This is a quiz for the following sections: -// - Tests +// This quiz tests: +// - Generics +// - Traits +// An imaginary magical school has a new report card generation system written in Rust! +// Currently the system only supports creating report cards where the student's grade +// is represented numerically (e.g. 1.0 -> 5.5). +// However, the school also issues alphabetical grades (A+ -> F-) and needs +// to be able to print both types of report card! -// This quiz isn't testing our function -- make it do that in such a way that -// the test passes. Then write a second test that tests that we get the result -// we expect to get when we call `times_two` with a negative number. -// No hints, you can do this :) +// Make the necessary code changes in the struct ReportCard and the impl block +// to support alphabetical report cards. Change the Grade in the second test to "A+" +// to show that your changes allow alphabetical grades. + +// Execute `rustlings hint quiz3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE -pub fn times_two(num: i32) -> i32 { - num * 2 +pub struct ReportCard { + pub grade: f32, + pub student_name: String, + pub student_age: u8, +} + +impl ReportCard { + pub fn print(&self) -> String { + format!("{} ({}) - achieved a grade of {}", + &self.student_name, &self.student_age, &self.grade) + } } #[cfg(test)] @@ -18,12 +34,29 @@ mod tests { use super::*; #[test] - fn returns_twice_of_positive_numbers() { - assert_eq!(times_two(4), ???); + fn generate_numeric_report_card() { + let report_card = ReportCard { + grade: 2.1, + student_name: "Tom Wriggle".to_string(), + student_age: 12, + }; + assert_eq!( + report_card.print(), + "Tom Wriggle (12) - achieved a grade of 2.1" + ); } #[test] - fn returns_twice_of_negative_numbers() { - // TODO write an assert for `times_two(-4)` + fn generate_alphabetic_report_card() { + // TODO: Make sure to change the grade here after you finish the exercise. + let report_card = ReportCard { + grade: 2.1, + student_name: "Gary Plotter".to_string(), + student_age: 11, + }; + assert_eq!( + report_card.print(), + "Gary Plotter (11) - achieved a grade of A+" + ); } } diff --git a/exercises/quiz4.rs b/exercises/quiz4.rs deleted file mode 100644 index 6c47480d..00000000 --- a/exercises/quiz4.rs +++ /dev/null @@ -1,23 +0,0 @@ -// quiz4.rs -// This quiz covers the sections: -// - Modules -// - Macros - -// Write a macro that passes the quiz! No hints this time, you can do it! - -// I AM NOT DONE - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_my_macro_world() { - assert_eq!(my_macro!("world!"), "Hello world!"); - } - - #[test] - fn test_my_macro_goodbye() { - assert_eq!(my_macro!("goodbye!"), "Hello goodbye!"); - } -} diff --git a/exercises/standard_library_types/README.md b/exercises/standard_library_types/README.md index 8b53dd81..809d61fe 100644 --- a/exercises/standard_library_types/README.md +++ b/exercises/standard_library_types/README.md @@ -1,5 +1,10 @@ -For the Box exercise check out the chapter [Using Box to Point to Data on the Heap](https://doc.rust-lang.org/book/ch15-01-box.html). +# Standard library types -For the Arc exercise check out the chapter [Shared-State Concurrency](https://doc.rust-lang.org/book/ch16-03-shared-state.html) of the Rust Book. +This section will teach you about Box, Shared-State Concurrency and Iterators. -For the Iterator exercise check out the chapters [Iterator](https://doc.rust-lang.org/book/ch13-02-iterators.html) of the Rust Book and the [Iterator documentation](https://doc.rust-lang.org/stable/std/iter/). +## Further information + +- [Using Box to Point to Data on the Heap](https://doc.rust-lang.org/book/ch15-01-box.html) +- [Shared-State Concurrency](https://doc.rust-lang.org/book/ch16-03-shared-state.html) +- [Iterator](https://doc.rust-lang.org/book/ch13-02-iterators.html) +- [Iterator documentation](https://doc.rust-lang.org/stable/std/iter/) diff --git a/exercises/standard_library_types/arc1.rs b/exercises/standard_library_types/arc1.rs index 4ad649f1..93a27036 100644 --- a/exercises/standard_library_types/arc1.rs +++ b/exercises/standard_library_types/arc1.rs @@ -1,8 +1,22 @@ // arc1.rs +// In this exercise, we are given a Vec of u32 called "numbers" with values ranging +// from 0 to 99 -- [ 0, 1, 2, ..., 98, 99 ] +// We would like to use this set of numbers within 8 different threads simultaneously. +// Each thread is going to get the sum of every eighth value, with an offset. +// The first thread (offset 0), will sum 0, 8, 16, ... +// The second thread (offset 1), will sum 1, 9, 17, ... +// The third thread (offset 2), will sum 2, 10, 18, ... +// ... +// The eighth thread (offset 7), will sum 7, 15, 23, ... + +// Because we are using threads, our values need to be thread-safe. Therefore, +// we are using Arc. We need to make a change in each of the two TODOs. + + // Make this code compile by filling in a value for `shared_numbers` where the -// TODO comment is and create an initial binding for `child_numbers` -// somewhere. Try not to create any copies of the `numbers` Vec! -// Execute `rustlings hint arc1` for hints :) +// first TODO comment is, and create an initial binding for `child_numbers` +// where the second TODO comment is. Try not to create any copies of the `numbers` Vec! +// Execute `rustlings hint arc1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -16,13 +30,9 @@ fn main() { let mut joinhandles = Vec::new(); for offset in 0..8 { + let child_numbers = // TODO joinhandles.push(thread::spawn(move || { - let mut i = offset; - let mut sum = 0; - while i < child_numbers.len() { - sum += child_numbers[i]; - i += 8; - } + let sum: u32 = child_numbers.iter().filter(|n| *n % 8 == offset).sum(); println!("Sum of offset {} is {}", offset, sum); })); } diff --git a/exercises/standard_library_types/box1.rs b/exercises/standard_library_types/box1.rs index f312f3d6..66cf00f3 100644 --- a/exercises/standard_library_types/box1.rs +++ b/exercises/standard_library_types/box1.rs @@ -10,11 +10,11 @@ // elements: the value of the current item and the next item. The last item is a value called `Nil`. // // Step 1: use a `Box` in the enum definition to make the code compile -// Step 2: create both empty and non-empty cons lists by replacing `unimplemented!()` +// Step 2: create both empty and non-empty cons lists by replacing `todo!()` // // Note: the tests should not be changed // -// Execute `rustlings hint box1` for hints :) +// Execute `rustlings hint box1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -33,11 +33,11 @@ fn main() { } pub fn create_empty_list() -> List { - unimplemented!() + todo!() } pub fn create_non_empty_list() -> List { - unimplemented!() + todo!() } #[cfg(test)] diff --git a/exercises/standard_library_types/cow1.rs b/exercises/standard_library_types/cow1.rs new file mode 100644 index 00000000..5fba2519 --- /dev/null +++ b/exercises/standard_library_types/cow1.rs @@ -0,0 +1,48 @@ +// cow1.rs + +// This exercise explores the Cow, or Clone-On-Write type. +// Cow is a clone-on-write smart pointer. +// It can enclose and provide immutable access to borrowed data, and clone the data lazily when mutation or ownership is required. +// The type is designed to work with general borrowed data via the Borrow trait. + +// I AM NOT DONE + +use std::borrow::Cow; + +fn abs_all<'a, 'b>(input: &'a mut Cow<'b, [i32]>) -> &'a mut Cow<'b, [i32]> { + for i in 0..input.len() { + let v = input[i]; + if v < 0 { + // Clones into a vector if not already owned. + input.to_mut()[i] = -v; + } + } + input +} + +fn main() { + // No clone occurs because `input` doesn't need to be mutated. + let slice = [0, 1, 2]; + let mut input = Cow::from(&slice[..]); + match abs_all(&mut input) { + Cow::Borrowed(_) => println!("I borrowed the slice!"), + _ => panic!("expected borrowed value"), + } + + // Clone occurs because `input` needs to be mutated. + let slice = [-1, 0, 1]; + let mut input = Cow::from(&slice[..]); + match abs_all(&mut input) { + Cow::Owned(_) => println!("I modified the slice and now own it!"), + _ => panic!("expected owned value"), + } + + // No clone occurs because `input` is already owned. + let slice = vec![-1, 0, 1]; + let mut input = Cow::from(slice); + match abs_all(&mut input) { + // TODO + Cow::Borrowed(_) => println!("I own this slice!"), + _ => panic!("expected borrowed value"), + } +} diff --git a/exercises/standard_library_types/iterators1.rs b/exercises/standard_library_types/iterators1.rs index 3fd519d6..0379c6bb 100644 --- a/exercises/standard_library_types/iterators1.rs +++ b/exercises/standard_library_types/iterators1.rs @@ -1,12 +1,12 @@ // iterators1.rs -// +// // Make me compile by filling in the `???`s // // When performing operations on elements within a collection, iterators are essential. -// This module helps you get familiar with the structure of using an iterator and +// This module helps you get familiar with the structure of using an iterator and // how to go through elements within an iterable collection. -// -// Execute `rustlings hint iterators1` for hints :D +// +// Execute `rustlings hint iterators1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -18,7 +18,7 @@ fn main () { assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana")); assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 2 assert_eq!(my_iterable_fav_fruits.next(), Some(&"avocado")); - assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 2.1 - assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry")); assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 3 + assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry")); + assert_eq!(my_iterable_fav_fruits.next(), ???); // TODO: Step 4 } diff --git a/exercises/standard_library_types/iterators2.rs b/exercises/standard_library_types/iterators2.rs index 84d14ae6..29c53afb 100644 --- a/exercises/standard_library_types/iterators2.rs +++ b/exercises/standard_library_types/iterators2.rs @@ -1,28 +1,41 @@ // iterators2.rs -// In this module, you'll learn some of the unique advantages that iterators can offer. -// Step 1. Complete the `capitalize_first` function to pass the first two cases. -// Step 2. Apply the `capitalize_first` function to a vector of strings. -// Ensure that it returns a vector of strings as well. -// Step 3. Apply the `capitalize_first` function again to a list. -// Try to ensure it returns a single string. -// As always, there are hints if you execute `rustlings hint iterators2`! +// In this exercise, you'll learn some of the unique advantages that iterators +// can offer. Follow the steps to complete the exercise. +// Execute `rustlings hint iterators2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE +// Step 1. +// Complete the `capitalize_first` function. +// "hello" -> "Hello" pub fn capitalize_first(input: &str) -> String { let mut c = input.chars(); match c.next() { None => String::new(), - Some(first) => first.collect::() + c.as_str(), + Some(first) => ???, } } +// Step 2. +// Apply the `capitalize_first` function to a slice of string slices. +// Return a vector of strings. +// ["hello", "world"] -> ["Hello", "World"] +pub fn capitalize_words_vector(words: &[&str]) -> Vec { + vec![] +} + +// Step 3. +// Apply the `capitalize_first` function again to a slice of string slices. +// Return a single string. +// ["hello", " ", "world"] -> "Hello World" +pub fn capitalize_words_string(words: &[&str]) -> String { + String::new() +} + #[cfg(test)] mod tests { use super::*; - // Step 1. - // Tests that verify your `capitalize_first` function implementation #[test] fn test_success() { assert_eq!(capitalize_first("hello"), "Hello"); @@ -33,18 +46,15 @@ mod tests { assert_eq!(capitalize_first(""), ""); } - // Step 2. #[test] fn test_iterate_string_vec() { let words = vec!["hello", "world"]; - let capitalized_words: Vec = // TODO - assert_eq!(capitalized_words, ["Hello", "World"]); + assert_eq!(capitalize_words_vector(&words), ["Hello", "World"]); } #[test] fn test_iterate_into_string() { let words = vec!["hello", " ", "world"]; - let capitalized_words = // TODO - assert_eq!(capitalized_words, "Hello World"); + assert_eq!(capitalize_words_string(&words), "Hello World"); } } diff --git a/exercises/standard_library_types/iterators3.rs b/exercises/standard_library_types/iterators3.rs index 353cea62..c97a6258 100644 --- a/exercises/standard_library_types/iterators3.rs +++ b/exercises/standard_library_types/iterators3.rs @@ -1,11 +1,10 @@ // iterators3.rs // This is a bigger exercise than most of the others! You can do it! // Here is your mission, should you choose to accept it: -// 1. Complete the divide function to get the first four tests to pass -// 2. Uncomment the last two tests and get them to pass by filling in -// values for `x` using `division_results`. -// Execute `rustlings hint iterators3` to get some hints! -// Have fun :-) +// 1. Complete the divide function to get the first four tests to pass. +// 2. Get the remaining tests to pass by completing the result_with_list and +// list_of_results functions. +// Execute `rustlings hint iterators3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -21,16 +20,30 @@ pub struct NotDivisibleError { divisor: i32, } -// This function should calculate `a` divided by `b` if `a` is -// evenly divisible by b. -// Otherwise, it should return a suitable error. -pub fn divide(a: i32, b: i32) -> Result {} +// Calculate `a` divided by `b` if `a` is evenly divisible by `b`. +// Otherwise, return a suitable error. +pub fn divide(a: i32, b: i32) -> Result { + todo!(); +} + +// Complete the function and return a value of the correct type so the test passes. +// Desired output: Ok([1, 11, 1426, 3]) +fn result_with_list() -> () { + let numbers = vec![27, 297, 38502, 81]; + let division_results = numbers.into_iter().map(|n| divide(n, 27)); +} + +// Complete the function and return a value of the correct type so the test passes. +// Desired output: [Ok(1), Ok(11), Ok(1426), Ok(3)] +fn list_of_results() -> () { + let numbers = vec![27, 297, 38502, 81]; + let division_results = numbers.into_iter().map(|n| divide(n, 27)); +} #[cfg(test)] mod tests { use super::*; - // Tests that verify your `divide` function implementation #[test] fn test_success() { assert_eq!(divide(81, 9), Ok(9)); @@ -57,22 +70,16 @@ mod tests { assert_eq!(divide(0, 81), Ok(0)); } - // Iterator exercises using your `divide` function - /* #[test] - fn result_with_list() { - let numbers = vec![27, 297, 38502, 81]; - let division_results = numbers.into_iter().map(|n| divide(n, 27)); - let x //... Fill in here! - assert_eq!(format!("{:?}", x), "Ok([1, 11, 1426, 3])"); + fn test_result_with_list() { + assert_eq!(format!("{:?}", result_with_list()), "Ok([1, 11, 1426, 3])"); } #[test] - fn list_of_results() { - let numbers = vec![27, 297, 38502, 81]; - let division_results = numbers.into_iter().map(|n| divide(n, 27)); - let x //... Fill in here! - assert_eq!(format!("{:?}", x), "[Ok(1), Ok(11), Ok(1426), Ok(3)]"); + fn test_list_of_results() { + assert_eq!( + format!("{:?}", list_of_results()), + "[Ok(1), Ok(11), Ok(1426), Ok(3)]" + ); } - */ } diff --git a/exercises/standard_library_types/iterators4.rs b/exercises/standard_library_types/iterators4.rs index 88862838..a02470ec 100644 --- a/exercises/standard_library_types/iterators4.rs +++ b/exercises/standard_library_types/iterators4.rs @@ -1,4 +1,5 @@ // iterators4.rs +// Execute `rustlings hint iterators4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -18,6 +19,11 @@ pub fn factorial(num: u64) -> u64 { mod tests { use super::*; + #[test] + fn factorial_of_0() { + assert_eq!(1, factorial(0)); + } + #[test] fn factorial_of_1() { assert_eq!(1, factorial(1)); diff --git a/exercises/standard_library_types/iterators5.rs b/exercises/standard_library_types/iterators5.rs new file mode 100644 index 00000000..0593d123 --- /dev/null +++ b/exercises/standard_library_types/iterators5.rs @@ -0,0 +1,124 @@ +// iterators5.rs +// Let's define a simple model to track Rustlings exercise progress. Progress +// will be modelled using a hash map. The name of the exercise is the key and +// the progress is the value. Two counting functions were created to count the +// number of exercises with a given progress. These counting functions use +// imperative style for loops. Recreate this counting functionality using +// iterators. Only the two iterator methods (count_iterator and +// count_collection_iterator) need to be modified. +// Execute `rustlings hint iterators5` or use the `hint` watch subcommand for a hint. +// +// Make the code compile and the tests pass. + +// I AM NOT DONE + +use std::collections::HashMap; + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Progress { + None, + Some, + Complete, +} + +fn count_for(map: &HashMap, value: Progress) -> usize { + let mut count = 0; + for val in map.values() { + if val == &value { + count += 1; + } + } + count +} + +fn count_iterator(map: &HashMap, value: Progress) -> usize { + // map is a hashmap with String keys and Progress values. + // map = { "variables1": Complete, "from_str": None, ... } + todo!(); +} + +fn count_collection_for(collection: &[HashMap], value: Progress) -> usize { + let mut count = 0; + for map in collection { + for val in map.values() { + if val == &value { + count += 1; + } + } + } + count +} + +fn count_collection_iterator(collection: &[HashMap], value: Progress) -> usize { + // collection is a slice of hashmaps. + // collection = [{ "variables1": Complete, "from_str": None, ... }, + // { "variables2": Complete, ... }, ... ] + todo!(); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn count_complete() { + let map = get_map(); + assert_eq!(3, count_iterator(&map, Progress::Complete)); + } + + #[test] + fn count_equals_for() { + let map = get_map(); + assert_eq!( + count_for(&map, Progress::Complete), + count_iterator(&map, Progress::Complete) + ); + } + + #[test] + fn count_collection_complete() { + let collection = get_vec_map(); + assert_eq!( + 6, + count_collection_iterator(&collection, Progress::Complete) + ); + } + + #[test] + fn count_collection_equals_for() { + let collection = get_vec_map(); + assert_eq!( + count_collection_for(&collection, Progress::Complete), + count_collection_iterator(&collection, Progress::Complete) + ); + } + + fn get_map() -> HashMap { + use Progress::*; + + let mut map = HashMap::new(); + map.insert(String::from("variables1"), Complete); + map.insert(String::from("functions1"), Complete); + map.insert(String::from("hashmap1"), Complete); + map.insert(String::from("arc1"), Some); + map.insert(String::from("as_ref_mut"), None); + map.insert(String::from("from_str"), None); + + map + } + + fn get_vec_map() -> Vec> { + use Progress::*; + + let map = get_map(); + + let mut other = HashMap::new(); + other.insert(String::from("variables2"), Complete); + other.insert(String::from("functions2"), Complete); + other.insert(String::from("if1"), Complete); + other.insert(String::from("from_into"), None); + other.insert(String::from("try_from_into"), None); + + vec![map, other] + } +} diff --git a/exercises/standard_library_types/rc1.rs b/exercises/standard_library_types/rc1.rs new file mode 100644 index 00000000..9b907fde --- /dev/null +++ b/exercises/standard_library_types/rc1.rs @@ -0,0 +1,98 @@ +// rc1.rs +// In this exercise, we want to express the concept of multiple owners via the Rc type. +// This is a model of our solar system - there is a Sun type and multiple Planets. +// The Planets take ownership of the sun, indicating that they revolve around the sun. + +// Make this code compile by using the proper Rc primitives to express that the sun has multiple owners. + +// I AM NOT DONE +use std::rc::Rc; + +#[derive(Debug)] +struct Sun {} + +#[derive(Debug)] +enum Planet { + Mercury(Rc), + Venus(Rc), + Earth(Rc), + Mars(Rc), + Jupiter(Rc), + Saturn(Rc), + Uranus(Rc), + Neptune(Rc), +} + +impl Planet { + fn details(&self) { + println!("Hi from {:?}!", self) + } +} + +fn main() { + let sun = Rc::new(Sun {}); + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + + let mercury = Planet::Mercury(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + mercury.details(); + + let venus = Planet::Venus(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + venus.details(); + + let earth = Planet::Earth(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + earth.details(); + + let mars = Planet::Mars(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + mars.details(); + + let jupiter = Planet::Jupiter(Rc::clone(&sun)); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + jupiter.details(); + + // TODO + let saturn = Planet::Saturn(Rc::new(Sun {})); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + saturn.details(); + + // TODO + let uranus = Planet::Uranus(Rc::new(Sun {})); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + uranus.details(); + + // TODO + let neptune = Planet::Neptune(Rc::new(Sun {})); + println!("reference count = {}", Rc::strong_count(&sun)); // 9 references + neptune.details(); + + assert_eq!(Rc::strong_count(&sun), 9); + + drop(neptune); + println!("reference count = {}", Rc::strong_count(&sun)); // 8 references + + drop(uranus); + println!("reference count = {}", Rc::strong_count(&sun)); // 7 references + + drop(saturn); + println!("reference count = {}", Rc::strong_count(&sun)); // 6 references + + drop(jupiter); + println!("reference count = {}", Rc::strong_count(&sun)); // 5 references + + drop(mars); + println!("reference count = {}", Rc::strong_count(&sun)); // 4 references + + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 3 references + + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 2 references + + // TODO + println!("reference count = {}", Rc::strong_count(&sun)); // 1 reference + + assert_eq!(Rc::strong_count(&sun), 1); +} diff --git a/exercises/strings/README.md b/exercises/strings/README.md index 38d24c84..fa2104cc 100644 --- a/exercises/strings/README.md +++ b/exercises/strings/README.md @@ -1,9 +1,9 @@ -### Strings +# Strings Rust has two string types, a string slice (`&str`) and an owned string (`String`). We're not going to dictate when you should use which one, but we'll show you how to identify and create them, as well as use them. -#### Book Sections +## Further information - [Strings](https://doc.rust-lang.org/book/ch08-02-strings.html) diff --git a/exercises/strings/strings1.rs b/exercises/strings/strings1.rs index 80902444..0de86a1d 100644 --- a/exercises/strings/strings1.rs +++ b/exercises/strings/strings1.rs @@ -1,6 +1,6 @@ // strings1.rs // Make me compile without changing the function signature! -// Execute `rustlings hint strings1` for hints ;) +// Execute `rustlings hint strings1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/strings/strings2.rs b/exercises/strings/strings2.rs index 5a2ce74a..0c48ec95 100644 --- a/exercises/strings/strings2.rs +++ b/exercises/strings/strings2.rs @@ -1,6 +1,6 @@ // strings2.rs // Make me compile without changing the function signature! -// Execute `rustlings hint strings2` for hints :) +// Execute `rustlings hint strings2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/strings/strings3.rs b/exercises/strings/strings3.rs new file mode 100644 index 00000000..e2353aec --- /dev/null +++ b/exercises/strings/strings3.rs @@ -0,0 +1,43 @@ +// strings3.rs +// Execute `rustlings hint strings3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +fn trim_me(input: &str) -> String { + // TODO: Remove whitespace from both ends of a string! + ??? +} + +fn compose_me(input: &str) -> String { + // TODO: Add " world!" to the string! There's multiple ways to do this! + ??? +} + +fn replace_me(input: &str) -> String { + // TODO: Replace "cars" in the string with "balloons"! + ??? +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn trim_a_string() { + assert_eq!(trim_me("Hello! "), "Hello!"); + assert_eq!(trim_me(" What's up!"), "What's up!"); + assert_eq!(trim_me(" Hola! "), "Hola!"); + } + + #[test] + fn compose_a_string() { + assert_eq!(compose_me("Hello"), "Hello world!"); + assert_eq!(compose_me("Goodbye"), "Goodbye world!"); + } + + #[test] + fn replace_a_string() { + assert_eq!(replace_me("I think cars are cool"), "I think balloons are cool"); + assert_eq!(replace_me("I love to look at cars"), "I love to look at balloons"); + } +} diff --git a/exercises/strings/strings4.rs b/exercises/strings/strings4.rs new file mode 100644 index 00000000..c410b562 --- /dev/null +++ b/exercises/strings/strings4.rs @@ -0,0 +1,29 @@ +// strings4.rs + +// Ok, here are a bunch of values-- some are `String`s, some are `&str`s. Your +// task is to call one of these two functions on each value depending on what +// you think each value is. That is, add either `string_slice` or `string` +// before the parentheses on each line. If you're right, it will compile! +// No hints this time! + +// I AM NOT DONE + +fn string_slice(arg: &str) { + println!("{}", arg); +} +fn string(arg: String) { + println!("{}", arg); +} + +fn main() { + ???("blue"); + ???("red".to_string()); + ???(String::from("hi")); + ???("rust is fun!".to_owned()); + ???("nice weather".into()); + ???(format!("Interpolation {}", "Station")); + ???(&String::from("abc")[0..1]); + ???(" hello there ".trim()); + ???("Happy Monday!".to_string().replace("Mon", "Tues")); + ???("mY sHiFt KeY iS sTiCkY".to_lowercase()); +} diff --git a/exercises/structs/README.md b/exercises/structs/README.md index afbc72c8..3fc1fdc9 100644 --- a/exercises/structs/README.md +++ b/exercises/structs/README.md @@ -1,7 +1,8 @@ -### Structs +# Structs -Rust has three struct types: a classic c struct, a tuple struct, and a unit struct. +Rust has three struct types: a classic C struct, a tuple struct, and a unit struct. -#### Book Sections +## Further information -- [Structures](https://doc.rust-lang.org/rust-by-example/custom_types/structs.html) +- [Structures](https://doc.rust-lang.org/book/ch05-01-defining-structs.html) +- [Method Syntax](https://doc.rust-lang.org/book/ch05-03-method-syntax.html) diff --git a/exercises/structs/structs1.rs b/exercises/structs/structs1.rs index 6d0b2f49..0d91c469 100644 --- a/exercises/structs/structs1.rs +++ b/exercises/structs/structs1.rs @@ -1,5 +1,6 @@ // structs1.rs // Address all the TODOs to make the tests pass! +// Execute `rustlings hint structs1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -10,7 +11,7 @@ struct ColorClassicStruct { struct ColorTupleStruct(/* TODO: Something goes here */); #[derive(Debug)] -struct UnitStruct; +struct UnitLikeStruct; #[cfg(test)] mod tests { @@ -21,8 +22,9 @@ mod tests { // TODO: Instantiate a classic c struct! // let green = - assert_eq!(green.name, "green"); - assert_eq!(green.hex, "#00FF00"); + assert_eq!(green.red, 0); + assert_eq!(green.green, 255); + assert_eq!(green.blue, 0); } #[test] @@ -30,16 +32,17 @@ mod tests { // TODO: Instantiate a tuple struct! // let green = - assert_eq!(green.0, "green"); - assert_eq!(green.1, "#00FF00"); + assert_eq!(green.0, 0); + assert_eq!(green.1, 255); + assert_eq!(green.2, 0); } #[test] fn unit_structs() { - // TODO: Instantiate a unit struct! - // let unit_struct = - let message = format!("{:?}s are fun!", unit_struct); + // TODO: Instantiate a unit-like struct! + // let unit_like_struct = + let message = format!("{:?}s are fun!", unit_like_struct); - assert_eq!(message, "UnitStructs are fun!"); + assert_eq!(message, "UnitLikeStructs are fun!"); } } diff --git a/exercises/structs/structs2.rs b/exercises/structs/structs2.rs index f9c6427d..32e311fa 100644 --- a/exercises/structs/structs2.rs +++ b/exercises/structs/structs2.rs @@ -1,5 +1,6 @@ // structs2.rs // Address all the TODOs to make the tests pass! +// Execute `rustlings hint structs2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/structs/structs3.rs b/exercises/structs/structs3.rs index 06fcaf29..0b3615f4 100644 --- a/exercises/structs/structs3.rs +++ b/exercises/structs/structs3.rs @@ -1,7 +1,8 @@ // structs3.rs -// Structs contain more than simply some data, they can also have logic, in this -// exercise we have defined the Package struct and we want to test some logic attached to it, -// make the code compile and the tests pass! If you have issues execute `rustlings hint structs3` +// Structs contain data, but can also have logic. In this exercise we have +// defined the Package struct and we want to test some logic attached to it. +// Make the code compile and the tests pass! +// Execute `rustlings hint structs3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -15,13 +16,13 @@ struct Package { impl Package { fn new(sender_country: String, recipient_country: String, weight_in_grams: i32) -> Package { if weight_in_grams <= 0 { - // Something goes here... + panic!("Can not ship a weightless package.") } else { - return Package { + Package { sender_country, recipient_country, weight_in_grams, - }; + } } } @@ -30,7 +31,7 @@ impl Package { } fn get_fees(&self, cents_per_gram: i32) -> ??? { - // Something goes here... + // Something goes here... } } @@ -57,12 +58,22 @@ mod tests { assert!(package.is_international()); } + #[test] + fn create_local_package() { + let sender_country = String::from("Canada"); + let recipient_country = sender_country.clone(); + + let package = Package::new(sender_country, recipient_country, 1200); + + assert!(!package.is_international()); + } + #[test] fn calculate_transport_fees() { let sender_country = String::from("Spain"); let recipient_country = String::from("Spain"); - let cents_per_gram = ???; + let cents_per_gram = 3; let package = Package::new(sender_country, recipient_country, 1500); diff --git a/exercises/tests/README.md b/exercises/tests/README.md index dbb14a83..27c6818d 100644 --- a/exercises/tests/README.md +++ b/exercises/tests/README.md @@ -1,7 +1,7 @@ -### Tests +# Tests Going out of order from the book to cover tests -- many of the following exercises will ask you to make tests pass! -#### Book Sections +## Further information - [Writing Tests](https://doc.rust-lang.org/book/ch11-01-writing-tests.html) diff --git a/exercises/tests/tests1.rs b/exercises/tests/tests1.rs index 50586a19..8b6ea374 100644 --- a/exercises/tests/tests1.rs +++ b/exercises/tests/tests1.rs @@ -4,7 +4,8 @@ // rustlings run tests1 // This test has a problem with it -- make the test compile! Make the test -// pass! Make the test fail! Execute `rustlings hint tests1` for hints :) +// pass! Make the test fail! +// Execute `rustlings hint tests1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/tests/tests2.rs b/exercises/tests/tests2.rs index 0d981ad1..a5ac15b1 100644 --- a/exercises/tests/tests2.rs +++ b/exercises/tests/tests2.rs @@ -1,6 +1,7 @@ // tests2.rs // This test has a problem with it -- make the test compile! Make the test -// pass! Make the test fail! Execute `rustlings hint tests2` for hints :) +// pass! Make the test fail! +// Execute `rustlings hint tests2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/tests/tests3.rs b/exercises/tests/tests3.rs index 3424f940..196a81a0 100644 --- a/exercises/tests/tests3.rs +++ b/exercises/tests/tests3.rs @@ -2,7 +2,7 @@ // This test isn't testing our function -- make it do that in such a way that // the test passes. Then write a second test that tests whether we get the result // we expect to get when we call `is_even(5)`. -// Execute `rustlings hint tests3` for hints :) +// Execute `rustlings hint tests3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/threads/README.md b/exercises/threads/README.md index 2024292f..d0866947 100644 --- a/exercises/threads/README.md +++ b/exercises/threads/README.md @@ -1 +1,9 @@ -For this exercise check out the [Dining Philosophers example](https://doc.rust-lang.org/1.4.0/book/dining-philosophers.html) and the chapter [Concurrency](https://doc.rust-lang.org/book/ch16-01-threads.html) of the Rust Book. \ No newline at end of file +# Threads + +In most current operating systems, an executed program’s code is run in a process, and the operating system manages multiple processes at once. +Within your program, you can also have independent parts that run simultaneously. The features that run these independent parts are called threads. + +## Further information + +- [Dining Philosophers example](https://doc.rust-lang.org/1.4.0/book/dining-philosophers.html) +- [Using Threads to Run Code Simultaneously](https://doc.rust-lang.org/book/ch16-01-threads.html) diff --git a/exercises/threads/threads1.rs b/exercises/threads/threads1.rs index 1785e8ce..e59f4ce4 100644 --- a/exercises/threads/threads1.rs +++ b/exercises/threads/threads1.rs @@ -1,31 +1,31 @@ // threads1.rs -// Make this compile! Execute `rustlings hint threads1` for hints :) -// The idea is the thread spawned on line 21 is completing jobs while the main thread is -// monitoring progress until 10 jobs are completed. If you see 6 lines -// of "waiting..." and the program ends without timing out when running, -// you've got it :) +// Execute `rustlings hint threads1` or use the `hint` watch subcommand for a hint. +// This program should wait until all the spawned threads have finished before exiting. // I AM NOT DONE -use std::sync::Arc; use std::thread; use std::time::Duration; -struct JobStatus { - jobs_completed: u32, -} fn main() { - let status = Arc::new(JobStatus { jobs_completed: 0 }); - let status_shared = status.clone(); - thread::spawn(move || { - for _ in 0..10 { + + let mut handles = vec![]; + for i in 0..10 { + thread::spawn(move || { thread::sleep(Duration::from_millis(250)); - status_shared.jobs_completed += 1; - } - }); - while status.jobs_completed < 10 { - println!("waiting... "); - thread::sleep(Duration::from_millis(500)); + println!("thread {} is complete", i); + }); } + + let mut completed_threads = 0; + for handle in handles { + // TODO: a struct is returned from thread::spawn, can you use it? + completed_threads += 1; + } + + if completed_threads != 10 { + panic!("Oh no! All the spawned threads did not finish!"); + } + } diff --git a/exercises/threads/threads2.rs b/exercises/threads/threads2.rs new file mode 100644 index 00000000..d0f8578f --- /dev/null +++ b/exercises/threads/threads2.rs @@ -0,0 +1,34 @@ +// threads2.rs +// Execute `rustlings hint threads2` or use the `hint` watch subcommand for a hint. +// Building on the last exercise, we want all of the threads to complete their work but this time +// the spawned threads need to be in charge of updating a shared value: JobStatus.jobs_completed + +// I AM NOT DONE + +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +struct JobStatus { + jobs_completed: u32, +} + +fn main() { + let status = Arc::new(JobStatus { jobs_completed: 0 }); + let mut handles = vec![]; + for _ in 0..10 { + let status_shared = status.clone(); + let handle = thread::spawn(move || { + thread::sleep(Duration::from_millis(250)); + // TODO: You must take an action before you update a shared value + status_shared.jobs_completed += 1; + }); + handles.push(handle); + } + for handle in handles { + handle.join().unwrap(); + // TODO: Print the value of the JobStatus.jobs_completed. Did you notice anything + // interesting in the output? Do you have to 'join' on all the handles? + println!("jobs completed {}", ???); + } +} diff --git a/exercises/threads/threads3.rs b/exercises/threads/threads3.rs new file mode 100644 index 00000000..27e99088 --- /dev/null +++ b/exercises/threads/threads3.rs @@ -0,0 +1,64 @@ +// threads3.rs +// Execute `rustlings hint threads3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +use std::sync::mpsc; +use std::sync::Arc; +use std::thread; +use std::time::Duration; + +struct Queue { + length: u32, + first_half: Vec, + second_half: Vec, +} + +impl Queue { + fn new() -> Self { + Queue { + length: 10, + first_half: vec![1, 2, 3, 4, 5], + second_half: vec![6, 7, 8, 9, 10], + } + } +} + +fn send_tx(q: Queue, tx: mpsc::Sender) -> () { + let qc = Arc::new(q); + let qc1 = qc.clone(); + let qc2 = qc.clone(); + + thread::spawn(move || { + for val in &qc1.first_half { + println!("sending {:?}", val); + tx.send(*val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); + + thread::spawn(move || { + for val in &qc2.second_half { + println!("sending {:?}", val); + tx.send(*val).unwrap(); + thread::sleep(Duration::from_secs(1)); + } + }); +} + +fn main() { + let (tx, rx) = mpsc::channel(); + let queue = Queue::new(); + let queue_length = queue.length; + + send_tx(queue, tx); + + let mut total_received: u32 = 0; + for received in rx { + println!("Got: {}", received); + total_received += 1; + } + + println!("total numbers received: {}", total_received); + assert_eq!(total_received, queue_length) +} diff --git a/exercises/traits/README.md b/exercises/traits/README.md index 8cd03ec4..de67acd0 100644 --- a/exercises/traits/README.md +++ b/exercises/traits/README.md @@ -1,4 +1,4 @@ -### Traits +# Traits A trait is a collection of methods. @@ -7,14 +7,13 @@ Data types can implement traits. To do so, the methods making up the trait are d In this way, traits are somewhat similar to Java interfaces and C++ abstract classes. Some additional common Rust traits include: - -+ `Clone` (the `clone` method), -+ `Display` (which allows formatted display via `{}`), and -+ `Debug` (which allows formatted display via `{:?}`). +- `Clone` (the `clone` method) +- `Display` (which allows formatted display via `{}`) +- `Debug` (which allows formatted display via `{:?}`) Because traits indicate shared behavior between data types, they are useful when writing generics. -#### Book Sections +## Further information - [Traits](https://doc.rust-lang.org/book/ch10-02-traits.html) diff --git a/exercises/traits/traits1.rs b/exercises/traits/traits1.rs index 2ef9e11b..5b9d8d50 100644 --- a/exercises/traits/traits1.rs +++ b/exercises/traits/traits1.rs @@ -7,6 +7,7 @@ // The trait AppendBar has only one function, // which appends "Bar" to any object // implementing this trait. +// Execute `rustlings hint traits1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -29,12 +30,12 @@ mod tests { use super::*; #[test] - fn is_FooBar() { + fn is_foo_bar() { assert_eq!(String::from("Foo").append_bar(), String::from("FooBar")); } #[test] - fn is_BarBar() { + fn is_bar_bar() { assert_eq!( String::from("").append_bar().append_bar(), String::from("BarBar") diff --git a/exercises/traits/traits2.rs b/exercises/traits/traits2.rs index 916c3c4b..708bb19a 100644 --- a/exercises/traits/traits2.rs +++ b/exercises/traits/traits2.rs @@ -9,6 +9,7 @@ // // No boiler plate code this time, // you can do this! +// Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/traits/traits3.rs b/exercises/traits/traits3.rs new file mode 100644 index 00000000..6d2fd6c3 --- /dev/null +++ b/exercises/traits/traits3.rs @@ -0,0 +1,41 @@ +// traits3.rs +// +// Your task is to implement the Licensed trait for +// both structures and have them return the same +// information without writing the same function twice. +// +// Consider what you can add to the Licensed trait. +// Execute `rustlings hint traits3` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +pub trait Licensed { + fn licensing_info(&self) -> String; +} + +struct SomeSoftware { + version_number: i32, +} + +struct OtherSoftware { + version_number: String, +} + +impl Licensed for SomeSoftware {} // Don't edit this line +impl Licensed for OtherSoftware {} // Don't edit this line + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_licensing_info_the_same() { + let licensing_info = String::from("Some information"); + let some_software = SomeSoftware { version_number: 1 }; + let other_software = OtherSoftware { + version_number: "v2.0.0".to_string(), + }; + assert_eq!(some_software.licensing_info(), licensing_info); + assert_eq!(other_software.licensing_info(), licensing_info); + } +} diff --git a/exercises/traits/traits4.rs b/exercises/traits/traits4.rs new file mode 100644 index 00000000..6b541665 --- /dev/null +++ b/exercises/traits/traits4.rs @@ -0,0 +1,46 @@ +// traits4.rs +// +// Your task is to replace the '??' sections so the code compiles. +// Don't change any line other than the marked one. +// Execute `rustlings hint traits4` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +pub trait Licensed { + fn licensing_info(&self) -> String { + "some information".to_string() + } +} + +struct SomeSoftware {} + +struct OtherSoftware {} + +impl Licensed for SomeSoftware {} +impl Licensed for OtherSoftware {} + +// YOU MAY ONLY CHANGE THE NEXT LINE +fn compare_license_types(software: ??, software_two: ??) -> bool { + software.licensing_info() == software_two.licensing_info() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compare_license_information() { + let some_software = SomeSoftware {}; + let other_software = OtherSoftware {}; + + assert!(compare_license_types(some_software, other_software)); + } + + #[test] + fn compare_license_information_backwards() { + let some_software = SomeSoftware {}; + let other_software = OtherSoftware {}; + + assert!(compare_license_types(other_software, some_software)); + } +} diff --git a/exercises/traits/traits5.rs b/exercises/traits/traits5.rs new file mode 100644 index 00000000..0fbca28a --- /dev/null +++ b/exercises/traits/traits5.rs @@ -0,0 +1,37 @@ +// traits5.rs +// +// Your task is to replace the '??' sections so the code compiles. +// Don't change any line other than the marked one. +// Execute `rustlings hint traits5` or use the `hint` watch subcommand for a hint. + +// I AM NOT DONE + +pub trait SomeTrait { + fn some_function(&self) -> bool { + true + } +} + +pub trait OtherTrait { + fn other_function(&self) -> bool { + true + } +} + +struct SomeStruct {} +struct OtherStruct {} + +impl SomeTrait for SomeStruct {} +impl OtherTrait for SomeStruct {} +impl SomeTrait for OtherStruct {} +impl OtherTrait for OtherStruct {} + +// YOU MAY ONLY CHANGE THE NEXT LINE +fn some_func(item: ??) -> bool { + item.some_function() && item.other_function() +} + +fn main() { + some_func(SomeStruct {}); + some_func(OtherStruct {}); +} diff --git a/exercises/variables/README.md b/exercises/variables/README.md index 1e2eb596..11a7a78a 100644 --- a/exercises/variables/README.md +++ b/exercises/variables/README.md @@ -1,7 +1,9 @@ -### Variables +# Variables -Here you'll learn about simple variables. +In Rust, variables are immutable by default. +When a variable is immutable, once a value is bound to a name, you can’t change that value. +You can make them mutable by adding mut in front of the variable name. -#### Book Sections +## Further information - [Variables and Mutability](https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html) diff --git a/exercises/variables/variables1.rs b/exercises/variables/variables1.rs index 4a3af73c..f4d182ac 100644 --- a/exercises/variables/variables1.rs +++ b/exercises/variables/variables1.rs @@ -1,10 +1,6 @@ // variables1.rs -// Make me compile! Execute the command `rustlings hint variables1` if you want a hint :) - -// About this `I AM NOT DONE` thing: -// We sometimes encourage you to keep trying things on a given exercise, -// even after you already figured it out. If you got everything working and -// feel ready for the next exercise, remove the `I AM NOT DONE` comment below. +// Make me compile! +// Execute `rustlings hint variables1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/variables/variables2.rs b/exercises/variables/variables2.rs index 7774a8fb..641aeb8e 100644 --- a/exercises/variables/variables2.rs +++ b/exercises/variables/variables2.rs @@ -1,13 +1,13 @@ // variables2.rs -// Make me compile! Execute the command `rustlings hint variables2` if you want a hint :) +// Execute `rustlings hint variables2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let x; if x == 10 { - println!("Ten!"); + println!("x is ten!"); } else { - println!("Not ten!"); + println!("x is not ten!"); } } diff --git a/exercises/variables/variables3.rs b/exercises/variables/variables3.rs index 30ec48ff..819b1bc7 100644 --- a/exercises/variables/variables3.rs +++ b/exercises/variables/variables3.rs @@ -1,11 +1,9 @@ // variables3.rs -// Make me compile! Execute the command `rustlings hint variables3` if you want a hint :) +// Execute `rustlings hint variables3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { - let x = 3; - println!("Number {}", x); - x = 5; // don't change this line + let x: i32; println!("Number {}", x); } diff --git a/exercises/variables/variables4.rs b/exercises/variables/variables4.rs index 77f1e9ab..54491b0a 100644 --- a/exercises/variables/variables4.rs +++ b/exercises/variables/variables4.rs @@ -1,9 +1,11 @@ // variables4.rs -// Make me compile! Execute the command `rustlings hint variables4` if you want a hint :) +// Execute `rustlings hint variables4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { - let x: i32; + let x = 3; + println!("Number {}", x); + x = 5; // don't change this line println!("Number {}", x); } diff --git a/exercises/variables/variables5.rs b/exercises/variables/variables5.rs index da37ae99..0e670d2a 100644 --- a/exercises/variables/variables5.rs +++ b/exercises/variables/variables5.rs @@ -1,11 +1,11 @@ // variables5.rs -// Make me compile! Execute the command `rustlings hint variables5` if you want a hint :) +// Execute `rustlings hint variables5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { - let number = "T-H-R-E-E"; + let number = "T-H-R-E-E"; // don't change this line println!("Spell a Number : {}", number); - number = 3; + number = 3; // don't rename this variable println!("Number plus two is : {}", number + 2); } diff --git a/exercises/variables/variables6.rs b/exercises/variables/variables6.rs index 98666914..a8520122 100644 --- a/exercises/variables/variables6.rs +++ b/exercises/variables/variables6.rs @@ -1,5 +1,5 @@ // variables6.rs -// Make me compile! Execute the command `rustlings hint variables6` if you want a hint :) +// Execute `rustlings hint variables6` or use the `hint` watch subcommand for a hint. // I AM NOT DONE diff --git a/exercises/vecs/README.md b/exercises/vecs/README.md new file mode 100644 index 00000000..ebe90bf3 --- /dev/null +++ b/exercises/vecs/README.md @@ -0,0 +1,15 @@ +# Vectors + +Vectors are one of the most-used Rust data structures. In other programming +languages, they'd simply be called Arrays, but since Rust operates on a +bit of a lower level, an array in Rust is stored on the stack (meaning it +can't grow or shrink, and the size needs to be known at compile time), +and a Vector is stored in the heap (where these restrictions do not apply). + +Vectors are a bit of a later chapter in the book, but we think that they're +useful enough to talk about them a bit earlier. We shall be talking about +the other useful data structure, hash maps, later. + +## Further information + +- [Storing Lists of Values with Vectors](https://doc.rust-lang.org/stable/book/ch08-01-vectors.html) diff --git a/exercises/collections/vec1.rs b/exercises/vecs/vecs1.rs similarity index 76% rename from exercises/collections/vec1.rs rename to exercises/vecs/vecs1.rs index ac3d9f1a..4e8c4cbb 100644 --- a/exercises/collections/vec1.rs +++ b/exercises/vecs/vecs1.rs @@ -1,8 +1,8 @@ -// vec1.rs +// vecs1.rs // Your task is to create a `Vec` which holds the exact same elements // as in the array `a`. // Make me compile and pass the test! -// Execute the command `rustlings hint collections1` if you need hints. +// Execute `rustlings hint vecs1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -20,6 +20,6 @@ mod tests { #[test] fn test_array_and_vec_similarity() { let (a, v) = array_and_vec(); - assert!(a.iter().zip(v.iter()).all(|(x, y)| x == y)); + assert_eq!(a, v[..]); } } diff --git a/exercises/collections/vec2.rs b/exercises/vecs/vecs2.rs similarity index 50% rename from exercises/collections/vec2.rs rename to exercises/vecs/vecs2.rs index ec6cfc00..5bea09a2 100644 --- a/exercises/collections/vec2.rs +++ b/exercises/vecs/vecs2.rs @@ -1,11 +1,10 @@ -// vec2.rs +// vecs2.rs // A Vec of even numbers is given. Your task is to complete the loop // so that each number in the Vec is multiplied by 2. // // Make me pass the test! // -// Execute the command `rustlings hint collections2` if you need -// hints. +// Execute `rustlings hint vecs2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE @@ -13,12 +12,21 @@ fn vec_loop(mut v: Vec) -> Vec { for i in v.iter_mut() { // TODO: Fill this up so that each element in the Vec `v` is // multiplied by 2. + ??? } // At this point, `v` should be equal to [4, 8, 12, 16, 20]. v } +fn vec_map(v: &Vec) -> Vec { + v.iter().map(|num| { + // TODO: Do the same thing as above - but instead of mutating the + // Vec, you can just return the new number! + ??? + }).collect() +} + #[cfg(test)] mod tests { use super::*; @@ -28,11 +36,14 @@ mod tests { let v: Vec = (1..).filter(|x| x % 2 == 0).take(5).collect(); let ans = vec_loop(v.clone()); - assert_eq!( - ans, - v.iter() - .map(|x| x * 2) - .collect::>() - ); + assert_eq!(ans, v.iter().map(|x| x * 2).collect::>()); + } + + #[test] + fn test_vec_map() { + let v: Vec = (1..).filter(|x| x % 2 == 0).take(5).collect(); + let ans = vec_map(&v); + + assert_eq!(ans, v.iter().map(|x| x * 2).collect::>()); } } diff --git a/info.toml b/info.toml index cc12c99d..060643d7 100644 --- a/info.toml +++ b/info.toml @@ -1,3 +1,20 @@ +# INTRO + +[[exercises]] +name = "intro1" +path = "exercises/intro/intro1.rs" +mode = "compile" +hint = """ +Remove the I AM NOT DONE comment in the exercises/intro/intro1.rs file +to move on to the next exercise.""" + +[[exercises]] +name = "intro2" +path = "exercises/intro/intro2.rs" +mode = "compile" +hint = """ +Add an argument after the format string.""" + # VARIABLES [[exercises]] @@ -5,7 +22,7 @@ name = "variables1" path = "exercises/variables/variables1.rs" mode = "compile" hint = """ -Hint: The declaration on line 12 is missing a keyword that is needed in Rust +The declaration on line 8 is missing a keyword that is needed in Rust to create a new variable binding.""" [[exercises]] @@ -26,27 +43,27 @@ name = "variables3" path = "exercises/variables/variables3.rs" mode = "compile" hint = """ -In Rust, variable bindings are immutable by default. But here we're trying -to reassign a different value to x! There's a keyword we can use to make -a variable binding mutable instead.""" - -[[exercises]] -name = "variables4" -path = "exercises/variables/variables4.rs" -mode = "compile" -hint = """ Oops! In this exercise, we have a variable binding that we've created on line 7, and we're trying to use it on line 8, but we haven't given it a value. We can't print out something that isn't there; try giving x a value! This is an error that can cause bugs that's very easy to make in any programming language -- thankfully the Rust compiler has caught this for us!""" +[[exercises]] +name = "variables4" +path = "exercises/variables/variables4.rs" +mode = "compile" +hint = """ +In Rust, variable bindings are immutable by default. But here we're trying +to reassign a different value to x! There's a keyword we can use to make +a variable binding mutable instead.""" + [[exercises]] name = "variables5" path = "exercises/variables/variables5.rs" mode = "compile" hint = """ -In variables3 we already learned how to make an immutable variable mutable +In variables4 we already learned how to make an immutable variable mutable using a special keyword. Unfortunately this doesn't help us much in this exercise because we want to assign a different typed value to an existing variable. Sometimes you may also like to reuse existing variable names because you are just converting @@ -62,40 +79,15 @@ path = "exercises/variables/variables6.rs" mode = "compile" hint = """ We know about variables and mutability, but there is another important type of -variable available; constants. +variable available: constants. Constants are always immutable and they are declared with keyword 'const' rather than keyword 'let'. Constants types must also always be annotated. -Read more about constants under 'Differences Between Variables and Constants' in the book's section 'Variables and Mutability': -https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#differences-between-variables-and-constants +Read more about constants and the differences between variables and constants under 'Constants' in the book's section 'Variables and Mutability': +https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html#constants """ -# IF - -[[exercises]] -name = "if1" -path = "exercises/if/if1.rs" -mode = "test" -hint = """ -It's possible to do this in one line if you would like! -Some similar examples from other languages: -- In C(++) this would be: `a > b ? a : b` -- In Python this would be: `a if a > b else b` -Remember in Rust that: -- the `if` condition does not need to be surrounded by parentheses -- `if`/`else` conditionals are expressions -- Each condition is followed by a `{}` block.""" - -[[exercises]] -name = "if2" -path = "exercises/if/if2.rs" -mode = "test" -hint = """ -For that first compiler error, it's important in Rust that each conditional -block return the same type! To get the tests passing, you will need a couple -conditions checking different input values.""" - # FUNCTIONS [[exercises]] @@ -122,16 +114,21 @@ path = "exercises/functions/functions3.rs" mode = "compile" hint = """ This time, the function *declaration* is okay, but there's something wrong -with the place where we're calling the function.""" +with the place where we're calling the function. +As a reminder, you can freely play around with different solutions in Rustlings! +Watch mode will only jump to the next exercise if you remove the I AM NOT DONE comment.""" [[exercises]] name = "functions4" path = "exercises/functions/functions4.rs" mode = "compile" hint = """ -The error message points to line 14 and says it expects a type after the -`->`. This is where the function's return type should be-- take a look at -the `is_even` function for an example!""" +The error message points to line 17 and says it expects a type after the +`->`. This is where the function's return type should be -- take a look at +the `is_even` function for an example! + +Also: Did you figure out that, technically, u32 would be the more fitting type +for the prices here, since they can't be negative? If so, kudos!""" [[exercises]] name = "functions5" @@ -139,14 +136,38 @@ path = "exercises/functions/functions5.rs" mode = "compile" hint = """ This is a really common error that can be fixed by removing one character. -It happens because Rust distinguishes between expressions and statements: expressions return -a value based on its operand, and statements simply return a () type which behaves just like `void` in C/C++ language. +It happens because Rust distinguishes between expressions and statements: expressions return a value based on their operand(s), and statements simply return a () type which behaves just like `void` in C/C++ language. We want to return a value of `i32` type from the `square` function, but it is returning a `()` type... They are not the same. There are two solutions: 1. Add a `return` ahead of `num * num;` 2. remove `;`, make it to be `num * num`""" -# TEST 1 +# IF + +[[exercises]] +name = "if1" +path = "exercises/if/if1.rs" +mode = "test" +hint = """ +It's possible to do this in one line if you would like! +Some similar examples from other languages: +- In C(++) this would be: `a > b ? a : b` +- In Python this would be: `a if a > b else b` +Remember in Rust that: +- the `if` condition does not need to be surrounded by parentheses +- `if`/`else` conditionals are expressions +- Each condition is followed by a `{}` block.""" + +[[exercises]] +name = "if2" +path = "exercises/if/if2.rs" +mode = "test" +hint = """ +For that first compiler error, it's important in Rust that each conditional +block returns the same type! To get the tests passing, you will need a couple +conditions checking different input values.""" + +# QUIZ 1 [[exercises]] name = "quiz1" @@ -193,8 +214,8 @@ that you want to end up in the slice. If you're curious why the first argument of `assert_eq!` does not have an ampersand for a reference since the second argument is a -reference, take a look at the Deref coercions section of the book: -https://doc.rust-lang.org/book/ch15-02-deref.html""" +reference, take a look at the coercion chapter of the nomicon: +https://doc.rust-lang.org/nomicon/coercions.html""" [[exercises]] name = "primitive_types5" @@ -218,6 +239,124 @@ Data Types -> The Tuple Type section of the book: https://doc.rust-lang.org/book/ch03-02-data-types.html#the-tuple-type Now you have another tool in your toolbox!""" +# VECS + +[[exercises]] +name = "vecs1" +path = "exercises/vecs/vecs1.rs" +mode = "test" +hint = """ +In Rust, there are two ways to define a Vector. +1. One way is to use the `Vec::new()` function to create a new vector + and fill it with the `push()` method. +2. The second way, which is simpler is to use the `vec![]` macro and + define your elements inside the square brackets. +Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html +of the Rust book to learn more. +""" + +[[exercises]] +name = "vecs2" +path = "exercises/vecs/vecs2.rs" +mode = "test" +hint = """ +Hint 1: `i` is each element from the Vec as they are being iterated. Can you try +multiplying this? + +Hint 2: For the first function, there's a way to directly access the numbers stored +in the Vec, using the * dereference operator. You can both access and write to the +number that way. + +After you've completed both functions, decide for yourself which approach you like +better. What do you think is the more commonly used pattern under Rust developers? +""" + +# MOVE SEMANTICS + +[[exercises]] +name = "move_semantics1" +path = "exercises/move_semantics/move_semantics1.rs" +mode = "compile" +hint = """ +So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on line 13, +right? The fix for this is going to be adding one keyword, and the addition is NOT on line 13 +where the error is. + +Also: Try accessing `vec0` after having called `fill_vec()`. See what happens!""" + +[[exercises]] +name = "move_semantics2" +path = "exercises/move_semantics/move_semantics2.rs" +mode = "compile" +hint = """ +So, `vec0` is passed into the `fill_vec` function as an argument. In Rust, +when an argument is passed to a function and it's not explicitly returned, +you can't use the original variable anymore. We call this "moving" a variable. +Variables that are moved into a function (or block scope) and aren't explicitly +returned get "dropped" at the end of that function. This is also what happens here. +There's a few ways to fix this, try them all if you want: +1. Make another, separate version of the data that's in `vec0` and pass that + to `fill_vec` instead. +2. Make `fill_vec` borrow its argument instead of taking ownership of it, + and then copy the data within the function in order to return an owned + `Vec` +3. Make `fill_vec` *mutably* borrow a reference to its argument (which will need to be + mutable), modify it directly, then not return anything. Then you can get rid + of `vec1` entirely -- note that this will change what gets printed by the + first `println!`""" + +[[exercises]] +name = "move_semantics3" +path = "exercises/move_semantics/move_semantics3.rs" +mode = "compile" +hint = """ +The difference between this one and the previous ones is that the first line +of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, +instead of adding that line back, add `mut` in one place that will change +an existing binding to be a mutable binding instead of an immutable one :)""" + +[[exercises]] +name = "move_semantics4" +path = "exercises/move_semantics/move_semantics4.rs" +mode = "compile" +hint = """ +Stop reading whenever you feel like you have enough direction :) Or try +doing one step and then fixing the compiler errors that result! +So the end goal is to: + - get rid of the first line in main that creates the new vector + - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` + - we don't want to pass anything to `fill_vec`, so its signature should + reflect that it does not take any arguments + - since we're not creating a new vec in `main` anymore, we need to create + a new vec in `fill_vec`, similarly to the way we did in `main`""" + +[[exercises]] +name = "move_semantics5" +path = "exercises/move_semantics/move_semantics5.rs" +mode = "compile" +hint = """ +Carefully reason about the range in which each mutable reference is in +scope. Does it help to update the value of referent (x) immediately after +the mutable reference is taken? Read more about 'Mutable References' +in the book's section References and Borrowing': +https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#mutable-references. +""" + +[[exercises]] +name = "move_semantics6" +path = "exercises/move_semantics/move_semantics6.rs" +mode = "compile" +hint = """ +To find the answer, you can consult the book section "References and Borrowing": +https://doc.rust-lang.org/stable/book/ch04-02-references-and-borrowing.html +The first problem is that `get_char` is taking ownership of the string. +So `data` is moved and can't be used for `string_uppercase` +`data` is moved to `get_char` first, meaning that `string_uppercase` cannot manipulate the data. +Once you've fixed that, `string_uppercase`'s function signature will also need to be adjusted. +Can you figure out how? + +Another hint: it has to do with the `&` character.""" + # STRUCTS [[exercises]] @@ -228,7 +367,7 @@ hint = """ Rust has more than one type of struct. Three actually, all variants are used to package related data together. There are normal (or classic) structs. These are named collections of related data stored in fields. Tuple structs are basically just named tuples. -Finally, Unit structs. These don't have and fields and are useful for generics. +Finally, Unit-like structs. These don't have any fields and are useful for generics. In this exercise you need to complete and implement one of each kind. Read more about structs in The Book: https://doc.rust-lang.org/book/ch05-01-defining-structs.html""" @@ -239,7 +378,7 @@ path = "exercises/structs/structs2.rs" mode = "test" hint = """ Creating instances of structs is easy, all you need to do is assign some values to its fields. -There is however some shortcuts that can be taken when instantiating structs. +There are however some shortcuts that can be taken when instantiating structs. Have a look in The Book, to find out more: https://doc.rust-lang.org/stable/book/ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struct-update-syntax""" [[exercises]] @@ -255,6 +394,32 @@ For calculate_transport_fees: Bigger is more expensive usually, we don't have si Have a look in The Book, to find out more about method implementations: https://doc.rust-lang.org/book/ch05-03-method-syntax.html""" +# ENUMS + +[[exercises]] +name = "enums1" +path = "exercises/enums/enums1.rs" +mode = "compile" +hint = "No hints this time ;)" + +[[exercises]] +name = "enums2" +path = "exercises/enums/enums2.rs" +mode = "compile" +hint = """ +You can create enumerations that have different variants with different types +such as no data, anonymous structs, a single string, tuples, ...etc""" + +[[exercises]] +name = "enums3" +path = "exercises/enums/enums3.rs" +mode = "test" +hint = """ +As a first step, you can define enums to compile this code without errors. +and then create a match expression in `process()`. +Note that you need to deconstruct some message variants +in the match expression to get value in the variant.""" + # STRINGS [[exercises]] @@ -278,37 +443,311 @@ Yes, it would be really easy to fix this by just changing the value bound to `wo string slice instead of a `String`, wouldn't it?? There is a way to add one character to line 9, though, that will coerce the `String` into a string slice.""" -# TEST 2 +[[exercises]] +name = "strings3" +path = "exercises/strings/strings3.rs" +mode = "test" +hint = """ +There's tons of useful standard library functions for strings. Let's try and use some of +them! + +For the compose_me method: You can either use the `format!` macro, or convert the string +slice into an owned string, which you can then freely extend.""" + +[[exercises]] +name = "strings4" +path = "exercises/strings/strings4.rs" +mode = "compile" +hint = "No hints this time ;)" + +# MODULES + +[[exercises]] +name = "modules1" +path = "exercises/modules/modules1.rs" +mode = "compile" +hint = """ +Everything is private in Rust by default-- but there's a keyword we can use +to make something public! The compiler error should point to the thing that +needs to be public.""" + +[[exercises]] +name = "modules2" +path = "exercises/modules/modules2.rs" +mode = "compile" +hint = """ +The delicious_snacks module is trying to present an external interface that is +different than its internal structure (the `fruits` and `veggies` modules and +associated constants). Complete the `use` statements to fit the uses in main and +find the one keyword missing for both constants.""" + +[[exercises]] +name = "modules3" +path = "exercises/modules/modules3.rs" +mode = "compile" +hint = """ +UNIX_EPOCH and SystemTime are declared in the std::time module. Add a use statement +for these two to bring them into scope. You can use nested paths or the glob +operator to bring these two in using only one line.""" + +# HASHMAPS + +[[exercises]] +name = "hashmaps1" +path = "exercises/hashmaps/hashmaps1.rs" +mode = "test" +hint = """ +Hint 1: Take a look at the return type of the function to figure out + the type for the `basket`. +Hint 2: Number of fruits should be at least 5. And you have to put + at least three different types of fruits. +""" + +[[exercises]] +name = "hashmaps2" +path = "exercises/hashmaps/hashmaps2.rs" +mode = "test" +hint = """ +Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. +Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +""" + +[[exercises]] +name = "hashmaps3" +path = "exercises/hashmaps/hashmaps3.rs" +mode = "test" +hint = """ +Hint 1: Use the `entry()` and `or_insert()` methods of `HashMap` to insert entries corresponding to each team in the scores table. +Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +Hint 2: If there is already an entry for a given key, the value returned by `entry()` can be updated based on the existing value. +Learn more at https://doc.rust-lang.org/book/ch08-03-hash-maps.html#updating-a-value-based-on-the-old-value +""" + +# QUIZ 2 [[exercises]] name = "quiz2" path = "exercises/quiz2.rs" -mode = "compile" -hint = "No hints this time ;)" - -# ENUMS - -[[exercises]] -name = "enums1" -path = "exercises/enums/enums1.rs" -mode = "compile" -hint = """ -Hint: The declaration of the enumeration type has not been defined yet.""" - -[[exercises]] -name = "enums2" -path = "exercises/enums/enums2.rs" -mode = "compile" -hint = """ -Hint: you can create enumerations that have different variants with different types -such as no data, anonymous structs, a single string, tuples, ...etc""" - -[[exercises]] -name = "enums3" -path = "exercises/enums/enums3.rs" mode = "test" hint = "No hints this time ;)" +# OPTIONS + +[[exercises]] +name = "options1" +path = "exercises/options/options1.rs" +mode = "test" +hint = """ +Options can have a Some value, with an inner value, or a None value, without an inner value. +There's multiple ways to get at the inner value, you can use unwrap, or pattern match. Unwrapping +is the easiest, but how do you do it safely so that it doesn't panic in your face later?""" + +[[exercises]] +name = "options2" +path = "exercises/options/options2.rs" +mode = "test" +hint = """ +check out: +https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html +https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html + +Remember that Options can be stacked in if let and while let. +For example: Some(Some(variable)) = variable2 +Also see Option::flatten +""" + +[[exercises]] +name = "options3" +path = "exercises/options/options3.rs" +mode = "compile" +hint = """ +The compiler says a partial move happened in the `match` +statement. How can this be avoided? The compiler shows the correction +needed. After making the correction as suggested by the compiler, do +read: https://doc.rust-lang.org/std/keyword.ref.html""" + +# ERROR HANDLING + +[[exercises]] +name = "errors1" +path = "exercises/error_handling/errors1.rs" +mode = "test" +hint = """ +`Ok` and `Err` are one of the variants of `Result`, so what the tests are saying +is that `generate_nametag_text` should return a `Result` instead of an +`Option`. + +To make this change, you'll need to: + - update the return type in the function signature to be a Result that + could be the variants `Ok(String)` and `Err(String)` + - change the body of the function to return `Ok(stuff)` where it currently + returns `Some(stuff)` + - change the body of the function to return `Err(error message)` where it + currently returns `None`""" + +[[exercises]] +name = "errors2" +path = "exercises/error_handling/errors2.rs" +mode = "test" +hint = """ +One way to handle this is using a `match` statement on +`item_quantity.parse::()` where the cases are `Ok(something)` and +`Err(something)`. This pattern is very common in Rust, though, so there's +a `?` operator that does pretty much what you would make that match statement +do for you! Take a look at this section of the Error Handling chapter: +https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator +and give it a try!""" + +[[exercises]] +name = "errors3" +path = "exercises/error_handling/errors3.rs" +mode = "compile" +hint = """ +If other functions can return a `Result`, why shouldn't `main`? It's a fairly common +convention to return something like Result<(), ErrorType> from your main function. +The unit (`()`) type is there because nothing is really needed in terms of positive +results.""" + +[[exercises]] +name = "errors4" +path = "exercises/error_handling/errors4.rs" +mode = "test" +hint = """ +`PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result. +It should be doing some checking, returning an `Err` result if those checks fail, and only +returning an `Ok` result if those checks determine that everything is... okay :)""" + +[[exercises]] +name = "errors5" +path = "exercises/error_handling/errors5.rs" +mode = "compile" +hint = """ +There are two different possible `Result` types produced within `main()`, which are +propagated using `?` operators. How do we declare a return type from `main()` that allows both? + +Under the hood, the `?` operator calls `From::from` on the error value to convert it to a boxed +trait object, a `Box`. This boxed trait object is polymorphic, and since all +errors implement the `error:Error` trait, we can capture lots of different errors in one "Box" +object. + +Check out this section of the book: +https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator + +Read more about boxing errors: +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/boxing_errors.html + +Read more about using the `?` operator with boxed errors: +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html +""" + +[[exercises]] +name = "errors6" +path = "exercises/error_handling/errors6.rs" +mode = "test" +hint = """ +This exercise uses a completed version of `PositiveNonzeroInteger` from +errors4. + +Below the line that TODO asks you to change, there is an example of using +the `map_err()` method on a `Result` to transform one type of error into +another. Try using something similar on the `Result` from `parse()`. You +might use the `?` operator to return early from the function, or you might +use a `match` expression, or maybe there's another way! + +You can create another function inside `impl ParsePosNonzeroError` to use +with `map_err()`. + +Read more about `map_err()` in the `std::result` documentation: +https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err""" + +# Generics + +[[exercises]] +name = "generics1" +path = "exercises/generics/generics1.rs" +mode = "compile" +hint = """ +Vectors in rust make use of generics to create dynamically sized arrays of any type. +You need to tell the compiler what type we are pushing onto this vector.""" + +[[exercises]] +name = "generics2" +path = "exercises/generics/generics2.rs" +mode = "test" +hint = """ +Currently we are wrapping only values of type 'u32'. +Maybe we could update the explicit references to this data type somehow? + +If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions +""" + +# TRAITS + +[[exercises]] +name = "traits1" +path = "exercises/traits/traits1.rs" +mode = "test" +hint = """ +A discussion about Traits in Rust can be found at: +https://doc.rust-lang.org/book/ch10-02-traits.html +""" + +[[exercises]] +name = "traits2" +path = "exercises/traits/traits2.rs" +mode = "test" +hint = """ +Notice how the trait takes ownership of 'self',and returns `Self'. +Try mutating the incoming string vector. Have a look at the tests to see +what the result should look like! + +Vectors provide suitable methods for adding an element at the end. See +the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" + +[[exercises]] +name = "traits3" +path = "exercises/traits/traits3.rs" +mode = "test" +hint = """ +Traits can have a default implementation for functions. Structs that implement +the trait can then use the default version of these functions if they choose not +implement the function themselves. + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#default-implementations +""" + +[[exercises]] +name = "traits4" +path = "exercises/traits/traits4.rs" +mode = "test" +hint = """ +Instead of using concrete types as parameters you can use traits. Try replacing the +'??' with 'impl ' + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters +""" + +[[exercises]] +name = "traits5" +path = "exercises/traits/traits5.rs" +mode = "compile" +hint = """ +To ensure a parameter implements multiple traits use the '+ syntax'. Try replacing the +'??' with 'impl <> + <>'. + +See the documentation at: https://doc.rust-lang.org/book/ch10-02-traits.html#specifying-multiple-trait-bounds-with-the--syntax +""" + +# QUIZ 3 + +[[exercises]] +name = "quiz3" +path = "exercises/quiz3.rs" +mode = "test" +hint = """ +To find the best solution to this challenge you're going to need to think back to your +knowledge of traits, specifically Trait Bound Syntax - you may also need this: `use std::fmt::Display;`.""" + # TESTS [[exercises]] @@ -341,86 +780,242 @@ You can call a function right where you're passing arguments to `assert!` -- so something like `assert!(having_fun())`. If you want to check that you indeed get false, you can negate the result of what you're doing using `!`, like `assert!(!having_fun())`.""" -# TEST 3 +# LIFETIMES [[exercises]] -name = "quiz3" -path = "exercises/quiz3.rs" -mode = "test" -hint = "No hints this time ;)" - -# MODULES - -[[exercises]] -name = "modules1" -path = "exercises/modules/modules1.rs" +name = "lifetimes1" +path = "exercises/lifetimes/lifetimes1.rs" mode = "compile" hint = """ -Everything is private in Rust by default-- but there's a keyword we can use -to make something public! The compiler error should point to the thing that -needs to be public.""" +Let the compiler guide you. Also take a look at the book if you need help: +https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html""" [[exercises]] -name = "modules2" -path = "exercises/modules/modules2.rs" +name = "lifetimes2" +path = "exercises/lifetimes/lifetimes2.rs" mode = "compile" hint = """ -The delicious_snacks module is trying to present an external -interface (the `fruit` and `veggie` constants) that is different than -its internal structure (the `fruits` and `veggies` modules and -associated constants). It's almost there except for one keyword missing for -each constant.""" - -# COLLECTIONS +Remember that the generic lifetime 'a will get the concrete lifetime that is equal to the smaller of the lifetimes of x and y. +You can take at least two paths to achieve the desired result while keeping the inner block: +1. Move the string2 declaration to make it live as long as string1 (how is result declared?) +2. Move println! into the inner block""" [[exercises]] -name = "collections1" -path = "exercises/collections/vec1.rs" -mode = "test" +name = "lifetimes3" +path = "exercises/lifetimes/lifetimes3.rs" +mode = "compile" hint = """ -In Rust, there are two ways to define a Vector. +If you use a lifetime annotation in a struct's fields, where else does it need to be added?""" -1. One way is to use the `Vec::new()` function to create a new vector - and fill it with the `push()` method. +# STANDARD LIBRARY TYPES -2. The second way, which is simpler is to use the `vec![]` macro and - define your elements inside the square brackets. - -Check this chapter: https://doc.rust-lang.org/stable/book/ch08-01-vectors.html -of the Rust book to learn more. +[[exercises]] +name = "iterators1" +path = "exercises/standard_library_types/iterators1.rs" +mode = "compile" +hint = """ +Step 1: +We need to apply something to the collection `my_fav_fruits` before we start to go through +it. What could that be? Take a look at the struct definition for a vector for inspiration: +https://doc.rust-lang.org/std/vec/struct.Vec.html. +Step 2 & step 2.1: +Very similar to the lines above and below. You've got this! +Step 3: +An iterator goes through all elements in a collection, but what if we've run out of +elements? What should we expect here? If you're stuck, take a look at +https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. """ [[exercises]] -name = "collections2" -path = "exercises/collections/vec2.rs" +name = "iterators2" +path = "exercises/standard_library_types/iterators2.rs" mode = "test" hint = """ -Hint 1: `i` is each element from the Vec as they are being iterated. - Can you try multiplying this? +Step 1 +The variable `first` is a `char`. It needs to be capitalized and added to the +remaining characters in `c` in order to return the correct `String`. +The remaining characters in `c` can be viewed as a string slice using the +`as_str` method. +The documentation for `char` contains many useful methods. +https://doc.rust-lang.org/std/primitive.char.html -Hint 2: Check the suggestion from the compiler error ;) +Step 2 +Create an iterator from the slice. Transform the iterated values by applying +the `capitalize_first` function. Remember to collect the iterator. + +Step 3. +This is surprising similar to the previous solution. Collect is very powerful +and very general. Rust just needs to know the desired type.""" + +[[exercises]] +name = "iterators3" +path = "exercises/standard_library_types/iterators3.rs" +mode = "test" +hint = """ +The divide function needs to return the correct error when even division is not +possible. + +The division_results variable needs to be collected into a collection type. + +The result_with_list function needs to return a single Result where the success +case is a vector of integers and the failure case is a DivisionError. + +The list_of_results function needs to return a vector of results. + +See https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect for how +the `FromIterator` trait is used in `collect()`. This trait is REALLY powerful! It +can make the solution to this exercise infinitely easier.""" + +[[exercises]] +name = "iterators4" +path = "exercises/standard_library_types/iterators4.rs" +mode = "test" +hint = """ +In an imperative language, you might write a for loop that updates +a mutable variable. Or, you might write code utilizing recursion +and a match clause. In Rust you can take another functional +approach, computing the factorial elegantly with ranges and iterators. + +Hint 2: Check out the `fold` and `rfold` methods!""" + +[[exercises]] +name = "iterators5" +path = "exercises/standard_library_types/iterators5.rs" +mode = "test" +hint = """ +The documentation for the std::iter::Iterator trait contains numerous methods +that would be helpful here. + +Return 0 from count_collection_iterator to make the code compile in order to +test count_iterator. + +The collection variable in count_collection_iterator is a slice of HashMaps. It +needs to be converted into an iterator in order to use the iterator methods. + +The fold method can be useful in the count_collection_iterator function. + +For a further challenge, consult the documentation for Iterator to find +a different method that could make your code more compact than using fold.""" + +[[exercises]] +name = "box1" +path = "exercises/standard_library_types/box1.rs" +mode = "test" +hint = """ +Step 1 +The compiler's message should help: since we cannot store the value of the actual type +when working with recursive types, we need to store a reference (pointer) to its value. +We should, therefore, place our `List` inside a `Box`. More details in the book here: +https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes + +Step 2 +Creating an empty list should be fairly straightforward (hint: peek at the assertions). +For a non-empty list keep in mind that we want to use our Cons "list builder". +Although the current list is one of integers (i32), feel free to change the definition +and try other types! """ [[exercises]] -name = "collections3" -path = "exercises/collections/hashmap1.rs" -mode = "test" +name = "arc1" +path = "exercises/standard_library_types/arc1.rs" +mode = "compile" hint = """ -Hint 1: Take a look at the return type of the function to figure out - the type for the `basket`. +Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order +to avoid creating a copy of `numbers`, you'll need to create `child_numbers` +inside the loop but still in the main thread. -Hint 2: Number of fruits should be at least 5. And you have to put - at least three different types of fruits. +`child_numbers` should be a clone of the Arc of the numbers instead of a +thread-local copy of the numbers. + +This is a simple exercise if you understand the underlying concepts, but if this +is too much of a struggle, consider reading through all of Chapter 16 in the book: +https://doc.rust-lang.org/stable/book/ch16-00-concurrency.html """ [[exercises]] -name = "collections4" -path = "exercises/collections/hashmap2.rs" -mode = "test" +name = "rc1" +path = "exercises/standard_library_types/rc1.rs" +mode = "compile" hint = """ -Use the `entry()` and `or_insert()` methods of `HashMap` to achieve this. +This is a straightforward exercise to use the Rc type. Each Planet has +ownership of the Sun, and uses Rc::clone() to increment the reference count of the Sun. +After using drop() to move the Planets out of scope individually, the reference count goes down. +In the end the sun only has one reference again, to itself. See more at: +https://doc.rust-lang.org/book/ch15-04-rc.html -Learn more at https://doc.rust-lang.org/stable/book/ch08-03-hash-maps.html#only-inserting-a-value-if-the-key-has-no-value +* Unforunately Pluto is no longer considered a planet :( +""" + +[[exercises]] +name = "cow1" +path = "exercises/standard_library_types/cow1.rs" +mode = "compile" +hint = """ +Since the vector is already owned, the `Cow` type doesn't need to clone it. + +Checkout https://doc.rust-lang.org/std/borrow/enum.Cow.html for documentation +on the `Cow` type. +""" + +# THREADS + +[[exercises]] +name = "threads1" +path = "exercises/threads/threads1.rs" +mode = "compile" +hint = """ +`JoinHandle` is a struct that is returned from a spawned thread: +https://doc.rust-lang.org/std/thread/fn.spawn.html + +A challenge with multi-threaded applications is that the main thread can +finish before the spawned threads are completed. +https://doc.rust-lang.org/book/ch16-01-threads.html#waiting-for-all-threads-to-finish-using-join-handle + +Collect the JoinHandles and wait for them to finish. +https://doc.rust-lang.org/std/thread/struct.JoinHandle.html +""" + +[[exercises]] +name = "threads2" +path = "exercises/threads/threads2.rs" +mode = "compile" +hint = """ +`Arc` is an Atomic Reference Counted pointer that allows safe, shared access +to **immutable** data. But we want to *change* the number of `jobs_completed` +so we'll need to also use another type that will only allow one thread to +mutate the data at a time. Take a look at this section of the book: +https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct +and keep reading if you'd like more hints :) + + +Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like: +`let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));` +Similar to the code in the example in the book that happens after the text +that says "We can use Arc to fix this.". If not, give that a try! If you +do and would like more hints, keep reading!! + + +Make sure neither of your threads are holding onto the lock of the mutex +while they are sleeping, since this will prevent the other thread from +being allowed to get the lock. Locks are automatically released when +they go out of scope. + +If you've learned from the sample solutions, I encourage you to come +back to this exercise and try it again in a few days to reinforce +what you've learned :)""" + +[[exercises]] +name = "threads3" +path = "exercises/threads/threads3.rs" +mode = "compile" +hint = """ +An alternate way to handle concurrency between threads is to use +a mpsc (multiple producer, single consumer) channel to communicate. +With both a sending end and a receiving end, it's possible to +send values in one thread and receive them in another. +Multiple producers are possible by using clone() to create a duplicate +of the original sending end. +See https://doc.rust-lang.org/book/ch16-02-message-passing.html for more info. """ # MACROS @@ -463,191 +1058,12 @@ mode = "compile" hint = """ You only need to add a single character to make this compile. The way macros are written, it wants to see something between each -"macro arm", so it can separate them.""" -# TEST 4 +"macro arm", so it can separate them. -[[exercises]] -name = "quiz4" -path = "exercises/quiz4.rs" -mode = "test" -hint = "No hints this time ;)" - -# MOVE SEMANTICS - -[[exercises]] -name = "move_semantics1" -path = "exercises/move_semantics/move_semantics1.rs" -mode = "compile" -hint = """ -So you've got the "cannot borrow immutable local variable `vec1` as mutable" error on line 13, -right? The fix for this is going to be adding one keyword, and the addition is NOT on line 13 -where the error is.""" - -[[exercises]] -name = "move_semantics2" -path = "exercises/move_semantics/move_semantics2.rs" -mode = "compile" -hint = """ -So `vec0` is being *moved* into the function `fill_vec` when we call it on -line 10, which means it gets dropped at the end of `fill_vec`, which means we -can't use `vec0` again on line 13 (or anywhere else in `main` after the -`fill_vec` call for that matter). We could fix this in a few ways, try them -all! -1. Make another, separate version of the data that's in `vec0` and pass that - to `fill_vec` instead. -2. Make `fill_vec` borrow its argument instead of taking ownership of it, - and then copy the data within the function in order to return an owned - `Vec` -3. Make `fill_vec` *mutably* borrow its argument (which will need to be - mutable), modify it directly, then not return anything. Then you can get rid - of `vec1` entirely -- note that this will change what gets printed by the - first `println!`""" - -[[exercises]] -name = "move_semantics3" -path = "exercises/move_semantics/move_semantics3.rs" -mode = "compile" -hint = """ -The difference between this one and the previous ones is that the first line -of `fn fill_vec` that had `let mut vec = vec;` is no longer there. You can, -instead of adding that line back, add `mut` in one place that will change -an existing binding to be a mutable binding instead of an immutable one :)""" - -[[exercises]] -name = "move_semantics4" -path = "exercises/move_semantics/move_semantics4.rs" -mode = "compile" -hint = """ -Stop reading whenever you feel like you have enough direction :) Or try -doing one step and then fixing the compiler errors that result! -So the end goal is to: - - get rid of the first line in main that creates the new vector - - so then `vec0` doesn't exist, so we can't pass it to `fill_vec` - - we don't want to pass anything to `fill_vec`, so its signature should - reflect that it does not take any arguments - - since we're not creating a new vec in `main` anymore, we need to create - a new vec in `fill_vec`, similarly to the way we did in `main`""" - -# ERROR HANDLING - -[[exercises]] -name = "errors1" -path = "exercises/error_handling/errors1.rs" -mode = "test" -hint = """ -`Err` is one of the variants of `Result`, so what the 2nd test is saying -is that `generate_nametag_text` should return a `Result` instead of an -`Option`. - -To make this change, you'll need to: - - update the return type in the function signature to be a Result that - could be the variants `Ok(String)` and `Err(String)` - - change the body of the function to return `Ok(stuff)` where it currently - returns `Some(stuff)` - - change the body of the function to return `Err(error message)` where it - currently returns `None` - - change the first test to expect `Ok(stuff)` where it currently expects - `Some(stuff)`.""" - -[[exercises]] -name = "errors2" -path = "exercises/error_handling/errors2.rs" -mode = "test" -hint = """ -One way to handle this is using a `match` statement on -`item_quantity.parse::()` where the cases are `Ok(something)` and -`Err(something)`. This pattern is very common in Rust, though, so there's -a `?` operator that does pretty much what you would make that match statement -do for you! Take a look at this section of the Error Handling chapter: -https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator -and give it a try!""" - -[[exercises]] -name = "errors3" -path = "exercises/error_handling/errors3.rs" -mode = "test" -hint = """ -If other functions can return a `Result`, why shouldn't `main`?""" - -[[exercises]] -name = "errorsn" -path = "exercises/error_handling/errorsn.rs" -mode = "test" -hint = """ -First hint: To figure out what type should go where the ??? is, take a look -at the test helper function `test_with_str`, since it returns whatever -`read_and_validate` returns and `test_with_str` has its signature fully -specified. - - -Next hint: There are three places in `read_and_validate` that we call a -function that returns a `Result` (that is, the functions might fail). -Apply the `?` operator on those calls so that we return immediately from -`read_and_validate` if those function calls fail. - - -Another hint: under the hood, the `?` operator calls `From::from` -on the error value to convert it to a boxed trait object, a Box, -which is polymorphic-- that means that lots of different kinds of errors -can be returned from the same function because all errors act the same -since they all implement the `error::Error` trait. -Check out this section of the book: -https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator - - -Another another hint: Note that because the `?` operator returns -the *unwrapped* value in the `Ok` case, if we want to return a `Result` from -`read_and_validate` for *its* success case, we'll have to rewrap a value -that we got from the return value of a `?`ed call in an `Ok`-- this will -look like `Ok(something)`. - - -Another another another hint: `Result`s must be "used", that is, you'll -get a warning if you don't handle a `Result` that you get in your -function. Read more about that in the `std::result` module docs: -https://doc.rust-lang.org/std/result/#results-must-be-used""" - -# OPTIONS / RESULTS - -[[exercises]] -name = "option1" -path = "exercises/option/option1.rs" -mode = "compile" -hint = """ -Hint 1: Check out some functions of Option: -is_some -is_none -unwrap - -and: -pattern matching - -Hint 2: There are no sensible defaults for the value of an Array; the values need to be filled before use. -""" - -[[exercises]] -name = "option2" -path = "exercises/option/option2.rs" -mode = "compile" -hint = """ -check out: -https://doc.rust-lang.org/rust-by-example/flow_control/if_let.html -https://doc.rust-lang.org/rust-by-example/flow_control/while_let.html - -Remember that Options can be stacked in if let and while let. -For example: Some(Some(variable)) = variable2 - - -""" - -[[exercises]] -name = "result1" -path = "exercises/error_handling/result1.rs" -mode = "test" -hint = """ -`PositiveNonzeroInteger::new` is always creating a new instance and returning an `Ok` result. -It should be doing some checking, returning an `Err` result if those checks fail, and only -returning an `Ok` result if those checks determine that everything is... okay :)""" +That's all the macro exercises we have in here, but it's barely even +scratching the surface of what you can do with Rust's macros. For a more +thorough introduction, you can have a read through the little book of Rust +macros: https://veykril.github.io/tlborm/""" # CLIPPY @@ -656,7 +1072,15 @@ name = "clippy1" path = "exercises/clippy/clippy1.rs" mode = "clippy" hint = """ -Floating point calculations are usually imprecise, so asking if two values are exactly equal is asking for trouble""" +Rust stores the highest precision version of any long or inifinite precision +mathematical constants in the rust standard library. +https://doc.rust-lang.org/stable/std/f32/consts/index.html + +We may be tempted to use our own approximations for certain mathematical constants, +but clippy recognizes those imprecise mathematical constants as a source of +potential error. +See the suggestions of the clippy warning in compile output and use the +appropriate replacement constant from std::f32::consts...""" [[exercises]] name = "clippy2" @@ -665,196 +1089,11 @@ mode = "clippy" hint = """ `for` loops over Option values are more clearly expressed as an `if let`""" -# STANDARD LIBRARY TYPES - [[exercises]] -name = "box1" -path = "exercises/standard_library_types/box1.rs" -mode = "test" -hint = """ -Step 1 -The compiler's message should help: since we cannot store the value of the actual type -when working with recursive types, we need to store a reference (pointer) to its value. -We should, therefore, place our `List` inside a `Box`. More details in the book here: -https://doc.rust-lang.org/book/ch15-01-box.html#enabling-recursive-types-with-boxes - -Step 2 -Creating an empty list should be fairly straightforward (hint: peek at the assertions). -For a non-empty list keep in mind that we want to use our Cons "list builder". -Although the current list is one of integers (i32), feel free to change the definition -and try other types! -""" - -[[exercises]] -name = "arc1" -path = "exercises/standard_library_types/arc1.rs" -mode = "compile" -hint = """ -Make `shared_numbers` be an `Arc` from the numbers vector. Then, in order -to avoid creating a copy of `numbers`, you'll need to create `child_numbers` -inside the loop but still in the main thread. - -`child_numbers` should be a clone of the Arc of the numbers instead of a -thread-local copy of the numbers.""" - -[[exercises]] -name = "iterators1" -path = "exercises/standard_library_types/iterators1.rs" -mode = "compile" -hint = """ -Step 1: -We need to apply something to the collection `my_fav_fruits` before we start to go through -it. What could that be? Take a look at the struct definition for a vector for inspiration: -https://doc.rust-lang.org/std/vec/struct.Vec.html. - - -Step 2 & step 2.1: -Very similar to the lines above and below. You've got this! - - -Step 3: -An iterator goes through all elements in a collection, but what if we've run out of -elements? What should we expect here? If you're stuck, take a look at -https://doc.rust-lang.org/std/iter/trait.Iterator.html for some ideas. -""" - -[[exercises]] -name = "iterators2" -path = "exercises/standard_library_types/iterators2.rs" -mode = "test" -hint = """ -Step 1 -You need to call something on `first` before it can be collected -Currently its type is `char`. Have a look at the methods that are available on that type: -https://doc.rust-lang.org/std/primitive.char.html - - -Step 2 -First you'll need to turn the Vec into an iterator -Then you'll need to apply your function unto each item in the vector -P.s. Don't forget to collect() at the end! - - -Step 3. -This is very similar to the previous test. The only real change is that you will need to -alter the type that collect is coerced into. For a bonus you could try doing this with a -turbofish""" - -[[exercises]] -name = "iterators3" -path = "exercises/standard_library_types/iterators3.rs" -mode = "test" -hint = """ -Minor hint: In each of the two cases in the match in main, you can create x with either -a 'turbofish' or by hinting the type of x to the compiler. You may try both. - -Major hint: Have a look at the Iter trait and at the explanation of its collect function. -Especially the part about Result is interesting.""" - -[[exercises]] -name = "iterators4" -path = "exercises/standard_library_types/iterators4.rs" -mode = "test" -hint = """ -In an imperative language, you might write a for loop that updates -a mutable variable. Or, you might write code utilizing recursion -and a match clause. In Rust you can take another functional -approach, computing the factorial elegantly with ranges and iterators.""" - -# TRAITS - -[[exercises]] -name = "traits1" -path = "exercises/traits/traits1.rs" -mode = "test" -hint = """ -A discussion about Traits in Rust can be found at: -https://doc.rust-lang.org/book/ch10-02-traits.html -""" - -[[exercises]] -name = "traits2" -path = "exercises/traits/traits2.rs" -mode = "test" -hint = """ -Notice how the trait takes ownership of 'self',and returns `Self'. -Try mutating the incoming string vector. - -Vectors provide suitable methods for adding an element at the end. See -the documentation at: https://doc.rust-lang.org/std/vec/struct.Vec.html""" - -# Generics - -[[exercises]] -name = "generics1" -path = "exercises/generics/generics1.rs" -mode = "compile" -hint = """ -Vectors in rust make use of generics to create dynamically sized arrays of any type. -You need to tell the compiler what type we are pushing onto this vector.""" - -[[exercises]] -name = "generics2" -path = "exercises/generics/generics2.rs" -mode = "test" -hint = """ -Currently we are wrapping only values of type 'u32'. -Maybe we could update the explicit references to this data type somehow? - -If you are still stuck https://doc.rust-lang.org/stable/book/ch10-01-syntax.html#in-method-definitions -""" - -[[exercises]] -name = "generics3" -path = "exercises/generics/generics3.rs" -mode = "test" -hint = """ -To find the best solution to this challenge you're going to need to think back to your -knowledge of traits, specifically Trait Bound Syntax - you may also need this: "use std::fmt::Display;" - -This is definitely harder than the last two exercises! You need to think about not only making the -ReportCard struct generic, but also the correct property - you will need to change the implementation -of the struct slightly too...you can do it! -""" - -# THREADS - -[[exercises]] -name = "threads1" -path = "exercises/threads/threads1.rs" -mode = "compile" -hint = """ -`Arc` is an Atomic Reference Counted pointer that allows safe, shared access -to **immutable** data. But we want to *change* the number of `jobs_completed` -so we'll need to also use another type that will only allow one thread to -mutate the data at a time. Take a look at this section of the book: -https://doc.rust-lang.org/book/ch16-03-shared-state.html#atomic-reference-counting-with-arct -and keep reading if you'd like more hints :) - - -Do you now have an `Arc` `Mutex` `JobStatus` at the beginning of main? Like: -`let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));` -Similar to the code in the example in the book that happens after the text -that says "We can use Arc to fix this.". If not, give that a try! If you -do and would like more hints, keep reading!! - - -Make sure neither of your threads are holding onto the lock of the mutex -while they are sleeping, since this will prevent the other thread from -being allowed to get the lock. Locks are automatically released when -they go out of scope. - -Ok, so, real talk, this was actually tricky for *me* to do too. And -I could see a lot of different problems you might run into, so at this -point I'm not sure which one you've hit :) - -Please open an issue if you're still running into a problem that -these hints are not helping you with, or if you've looked at the sample -answers and don't understand why they work and yours doesn't. - -If you've learned from the sample solutions, I encourage you to come -back to this exercise and try it again in a few days to reinforce -what you've learned :)""" +name = "clippy3" +path = "exercises/clippy/clippy3.rs" +mode = "clippy" +hint = "No hints this time!" # TYPE CONVERSIONS @@ -873,13 +1112,48 @@ mode = "test" hint = """ Follow the steps provided right before the `From` implementation""" +[[exercises]] +name = "from_str" +path = "exercises/conversions/from_str.rs" +mode = "test" +hint = """ +The implementation of FromStr should return an Ok with a Person object, +or an Err with an error if the string is not valid. + +This is almost like the `from_into` exercise, but returning errors instead +of falling back to a default value. + +Look at the test cases to see which error variants to return. + +Another hint: You can use the `map_err` method of `Result` with a function +or a closure to wrap the error from `parse::`. + +Yet another hint: If you would like to propagate errors by using the `?` +operator in your solution, you might want to look at +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html +""" + [[exercises]] name = "try_from_into" path = "exercises/conversions/try_from_into.rs" mode = "test" hint = """ Follow the steps provided right before the `TryFrom` implementation. -You can also use the example at https://doc.rust-lang.org/std/convert/trait.TryFrom.html""" +You can also use the example at https://doc.rust-lang.org/std/convert/trait.TryFrom.html + +Is there an implementation of `TryFrom` in the standard library that +can both do the required integer conversion and check the range of the input? + +Another hint: Look at the test cases to see which error variants to return. + +Yet another hint: You can use the `map_err` or `or` methods of `Result` to +convert errors. + +Yet another hint: If you would like to propagate errors by using the `?` +operator in your solution, you might want to look at +https://doc.rust-lang.org/stable/rust-by-example/error/multiple_error_types/reenter_question_mark.html + +Challenge: Can you make the `TryFrom` implementations generic over many integer types?""" [[exercises]] name = "as_ref_mut" @@ -887,12 +1161,3 @@ path = "exercises/conversions/as_ref_mut.rs" mode = "test" hint = """ Add AsRef as a trait bound to the functions.""" - -[[exercises]] -name = "from_str" -path = "exercises/conversions/from_str.rs" -mode = "test" -hint = """ -The implementation of FromStr should return an Ok with a Person object, -or an Err with a string if the string is not valid. -This is almost like the `try_from_into` exercise.""" diff --git a/install.ps1 b/install.ps1 index 32167f06..97980c5b 100644 --- a/install.ps1 +++ b/install.ps1 @@ -53,7 +53,7 @@ function vercomp($v1, $v2) { } $rustVersion = $(rustc --version).Split(" ")[1] -$minRustVersion = "1.39" +$minRustVersion = "1.56" if ((vercomp $rustVersion $minRustVersion) -eq 2) { Write-Host "WARNING: Rust version is too old: $rustVersion - needs at least $minRustVersion" Write-Host "Please update Rust with 'rustup update'" diff --git a/install.sh b/install.sh index e986e741..f3b3f33d 100755 --- a/install.sh +++ b/install.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +set -euo pipefail echo "Let's get you set up with Rustlings!" @@ -12,12 +13,33 @@ else exit 1 fi +if [ -x "$(command -v cc)" ] +then + echo "SUCCESS: cc is installed" +else + echo "ERROR: cc does not seem to be installed." + echo "Please download (g)cc using your package manager." + echo "OSX: xcode-select --install" + echo "Deb: sudo apt install gcc" + echo "Yum: sudo yum -y install gcc" + exit 1 +fi + +if [ -x "$(command -v rustup)" ] +then + echo "SUCCESS: rustup is installed" +else + echo "ERROR: rustup does not seem to be installed." + echo "Please download rustup using https://rustup.rs!" + exit 1 +fi + if [ -x "$(command -v rustc)" ] then echo "SUCCESS: Rust is installed" else echo "ERROR: Rust does not seem to be installed." - echo "Please download Rust using https://rustup.rs!" + echo "Please download Rust using rustup!" exit 1 fi @@ -26,7 +48,7 @@ then echo "SUCCESS: Cargo is installed" else echo "ERROR: Cargo does not seem to be installed." - echo "Please download Rust and Cargo using https://rustup.rs!" + echo "Please download Rust and Cargo using rustup!" exit 1 fi @@ -62,6 +84,21 @@ function vercomp() { then max_len=$len2 fi + + #pad right in short arr + if [[ len1 -gt len2 ]]; + then + for ((i = len2; i < len1; i++)); + do + v2[$i]=0 + done + else + for ((i = len1; i < len2; i++)); + do + v1[$i]=0 + done + fi + for i in `seq 0 $max_len` do # Fill empty fields with zeros in v1 @@ -87,9 +124,9 @@ function vercomp() { } RustVersion=$(rustc --version | cut -d " " -f 2) -MinRustVersion=1.39 -vercomp $RustVersion $MinRustVersion -if [ $? -eq 2 ] +MinRustVersion=1.56 +vercomp "$RustVersion" $MinRustVersion || ec=$? +if [ ${ec:-0} -eq 2 ] then echo "ERROR: Rust version is too old: $RustVersion - needs at least $MinRustVersion" echo "Please update Rust with 'rustup update'" @@ -100,9 +137,9 @@ fi Path=${1:-rustlings/} echo "Cloning Rustlings at $Path..." -git clone -q https://github.com/rust-lang/rustlings $Path +git clone -q https://github.com/rust-lang/rustlings "$Path" -cd $Path +cd "$Path" Version=$(curl -s https://api.github.com/repos/rust-lang/rustlings/releases/latest | ${PY} -c "import json,sys;obj=json.load(sys.stdin);print(obj['tag_name']);") CargoBin="${CARGO_HOME:-$HOME/.cargo}/bin" diff --git a/src/exercise.rs b/src/exercise.rs index 283b2b90..4be3a2cc 100644 --- a/src/exercise.rs +++ b/src/exercise.rs @@ -1,5 +1,6 @@ use regex::Regex; use serde::Deserialize; +use std::env; use std::fmt::{self, Display, Formatter}; use std::fs::{self, remove_file, File}; use std::io::Read; @@ -23,7 +24,7 @@ fn temp_file() -> String { } // The mode of the exercise. -#[derive(Deserialize, Copy, Clone)] +#[derive(Deserialize, Copy, Clone, Debug)] #[serde(rename_all = "lowercase")] pub enum Mode { // Indicates that the exercise should be compiled as a binary @@ -41,7 +42,7 @@ pub struct ExerciseList { // A representation of a rustlings exercise. // This is deserialized from the accompanying info.toml file -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub struct Exercise { // Name of the exercise pub name: String, @@ -120,15 +121,19 @@ impl Exercise { r#"[package] name = "{}" version = "0.0.1" -edition = "2018" +edition = "2021" [[bin]] name = "{}" path = "{}.rs""#, self.name, self.name, self.name ); - fs::write(CLIPPY_CARGO_TOML_PATH, cargo_toml) - .expect("Failed to write πŸ“Ž Clippy πŸ“Ž Cargo.toml file."); - // To support the ability to run the clipy exercises, build + let cargo_toml_error_msg = if env::var("NO_EMOJI").is_ok() { + "Failed to write Clippy Cargo.toml file." + } else { + "Failed to write πŸ“Ž Clippy πŸ“Ž Cargo.toml file." + }; + fs::write(CLIPPY_CARGO_TOML_PATH, cargo_toml).expect(cargo_toml_error_msg); + // To support the ability to run the clippy exercises, build // an executable, in addition to running clippy. With a // compilation failure, this would silently fail. But we expect // clippy to reflect the same failure while compiling later. @@ -149,7 +154,7 @@ path = "{}.rs""#, Command::new("cargo") .args(&["clippy", "--manifest-path", CLIPPY_CARGO_TOML_PATH]) .args(RUSTC_COLOR_ARGS) - .args(&["--", "-D", "warnings"]) + .args(&["--", "-D", "warnings","-D","clippy::float_cmp"]) .output() } } @@ -157,7 +162,7 @@ path = "{}.rs""#, if cmd.status.success() { Ok(CompiledExercise { - exercise: &self, + exercise: self, _handle: FileHandle, }) } else { @@ -212,8 +217,7 @@ path = "{}.rs""#, let matched_line_index = source .lines() .enumerate() - .filter_map(|(i, line)| if re.is_match(line) { Some(i) } else { None }) - .next() + .find_map(|(i, line)| if re.is_match(line) { Some(i) } else { None }) .expect("This should not happen at all"); let min_line = ((matched_line_index as i32) - (CONTEXT as i32)).max(0) as usize; @@ -232,6 +236,16 @@ path = "{}.rs""#, State::Pending(context) } + + // Check that the exercise looks to be solved using self.state() + // This is not the best way to check since + // the user can just remove the "I AM NOT DONE" string from the file + // without actually having solved anything. + // The only other way to truly check this would to compile and run + // the exercise; which would be both costly and counterintuitive + pub fn looks_done(&self) -> bool { + self.state() == State::Done + } } impl Display for Exercise { diff --git a/src/main.rs b/src/main.rs index 35f8df4d..8eebc086 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,18 @@ use crate::exercise::{Exercise, ExerciseList}; -use crate::run::run; +use crate::project::RustAnalyzerProject; +use crate::run::{reset, run}; use crate::verify::verify; -use clap::{crate_version, App, Arg, SubCommand}; +use argh::FromArgs; use console::Emoji; use notify::DebouncedEvent; use notify::{RecommendedWatcher, RecursiveMode, Watcher}; use std::ffi::OsStr; use std::fs; -use std::io; +use std::io::{self, prelude::*}; use std::path::Path; use std::process::{Command, Stdio}; -use std::sync::mpsc::channel; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::{channel, RecvTimeoutError}; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; @@ -19,58 +21,112 @@ use std::time::Duration; mod ui; mod exercise; +mod project; mod run; mod verify; -fn main() { - let matches = App::new("rustlings") - .version(crate_version!()) - .author("Olivia Hugger, Carol Nichols") - .about("Rustlings is a collection of small exercises to get you used to writing and reading Rust code") - .arg( - Arg::with_name("nocapture") - .long("nocapture") - .help("Show outputs from the test exercises") - ) - .subcommand( - SubCommand::with_name("verify") - .alias("v") - .about("Verifies all exercises according to the recommended order") - ) - .subcommand( - SubCommand::with_name("watch") - .alias("w") - .about("Reruns `verify` when files were edited") - ) - .subcommand( - SubCommand::with_name("run") - .alias("r") - .about("Runs/Tests a single exercise") - .arg(Arg::with_name("name").required(true).index(1)), - ) - .subcommand( - SubCommand::with_name("hint") - .alias("h") - .about("Returns a hint for the current exercise") - .arg(Arg::with_name("name").required(true).index(1)), - ) - .subcommand( - SubCommand::with_name("list") - .alias("l") - .about("Lists the exercises available in rustlings") - ) - .get_matches(); +// In sync with crate version +const VERSION: &str = "5.2.0"; - if matches.subcommand_name().is_none() { - println!(); - println!(r#" welcome to... "#); - println!(r#" _ _ _ "#); - println!(r#" _ __ _ _ ___| |_| (_)_ __ __ _ ___ "#); - println!(r#" | '__| | | / __| __| | | '_ \ / _` / __| "#); - println!(r#" | | | |_| \__ \ |_| | | | | | (_| \__ \ "#); - println!(r#" |_| \__,_|___/\__|_|_|_| |_|\__, |___/ "#); - println!(r#" |___/ "#); - println!(); +#[derive(FromArgs, PartialEq, Debug)] +/// Rustlings is a collection of small exercises to get you used to writing and reading Rust code +struct Args { + /// show outputs from the test exercises + #[argh(switch)] + nocapture: bool, + /// show the executable version + #[argh(switch, short = 'v')] + version: bool, + #[argh(subcommand)] + nested: Option, +} + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand)] +enum Subcommands { + Verify(VerifyArgs), + Watch(WatchArgs), + Run(RunArgs), + Reset(ResetArgs), + Hint(HintArgs), + List(ListArgs), + Lsp(LspArgs), +} + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "verify")] +/// Verifies all exercises according to the recommended order +struct VerifyArgs {} + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "watch")] +/// Reruns `verify` when files were edited +struct WatchArgs {} + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "run")] +/// Runs/Tests a single exercise +struct RunArgs { + #[argh(positional)] + /// the name of the exercise + name: String, +} + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "reset")] +/// Resets a single exercise using "git stash -- " +struct ResetArgs { + #[argh(positional)] + /// the name of the exercise + name: String, +} + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "hint")] +/// Returns a hint for the given exercise +struct HintArgs { + #[argh(positional)] + /// the name of the exercise + name: String, +} + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "lsp")] +/// Enable rust-analyzer for exercises +struct LspArgs {} + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "list")] +/// Lists the exercises available in Rustlings +struct ListArgs { + #[argh(switch, short = 'p')] + /// show only the paths of the exercises + paths: bool, + #[argh(switch, short = 'n')] + /// show only the names of the exercises + names: bool, + #[argh(option, short = 'f')] + /// provide a string to match exercise names + /// comma separated patterns are acceptable + filter: Option, + #[argh(switch, short = 'u')] + /// display only exercises not yet solved + unsolved: bool, + #[argh(switch, short = 's')] + /// display only exercises that have been solved + solved: bool, +} + +fn main() { + let args: Args = argh::from_env(); + + if args.version { + println!("v{}", VERSION); + std::process::exit(0); + } + + if args.nested.is_none() { + println!("\n{}\n", WELCOME); } if !Path::new("info.toml").exists() { @@ -91,101 +147,164 @@ fn main() { let toml_str = &fs::read_to_string("info.toml").unwrap(); let exercises = toml::from_str::(toml_str).unwrap().exercises; - let verbose = matches.is_present("nocapture"); + let verbose = args.nocapture; - if matches.subcommand_matches("list").is_some() { - exercises.iter().for_each(|e| println!("{}", e.name)); - } - if let Some(ref matches) = matches.subcommand_matches("run") { - let name = matches.value_of("name").unwrap(); - - let matching_exercise = |e: &&Exercise| name == e.name; - - let exercise = exercises.iter().find(matching_exercise).unwrap_or_else(|| { - println!("No exercise found for your given name!"); - std::process::exit(1) - }); - - run(&exercise, verbose).unwrap_or_else(|_| std::process::exit(1)); - } - - if let Some(ref matches) = matches.subcommand_matches("hint") { - let name = matches.value_of("name").unwrap(); - - let exercise = exercises - .iter() - .find(|e| name == e.name) - .unwrap_or_else(|| { - println!("No exercise found for your given name!"); - std::process::exit(1) + let command = args.nested.unwrap_or_else(|| { + println!("{}\n", DEFAULT_OUT); + std::process::exit(0); + }); + match command { + Subcommands::List(subargs) => { + if !subargs.paths && !subargs.names { + println!("{:<17}\t{:<46}\t{:<7}", "Name", "Path", "Status"); + } + let mut exercises_done: u16 = 0; + let filters = subargs.filter.clone().unwrap_or_default().to_lowercase(); + exercises.iter().for_each(|e| { + let fname = format!("{}", e.path.display()); + let filter_cond = filters + .split(',') + .filter(|f| !f.trim().is_empty()) + .any(|f| e.name.contains(&f) || fname.contains(&f)); + let status = if e.looks_done() { + exercises_done += 1; + "Done" + } else { + "Pending" + }; + let solve_cond = { + (e.looks_done() && subargs.solved) + || (!e.looks_done() && subargs.unsolved) + || (!subargs.solved && !subargs.unsolved) + }; + if solve_cond && (filter_cond || subargs.filter.is_none()) { + let line = if subargs.paths { + format!("{}\n", fname) + } else if subargs.names { + format!("{}\n", e.name) + } else { + format!("{:<17}\t{:<46}\t{:<7}\n", e.name, fname, status) + }; + // Somehow using println! leads to the binary panicking + // when its output is piped. + // So, we're handling a Broken Pipe error and exiting with 0 anyway + let stdout = std::io::stdout(); + { + let mut handle = stdout.lock(); + handle.write_all(line.as_bytes()).unwrap_or_else(|e| { + match e.kind() { + std::io::ErrorKind::BrokenPipe => std::process::exit(0), + _ => std::process::exit(1), + }; + }); + } + } }); - - println!("{}", exercise.hint); - } - - if matches.subcommand_matches("verify").is_some() { - verify(&exercises, verbose).unwrap_or_else(|_| std::process::exit(1)); - } - - if matches.subcommand_matches("watch").is_some() { - if let Err(e) = watch(&exercises, verbose) { - println!("Error: Could not watch your progess. Error message was {:?}.", e); - println!("Most likely you've run out of disk space or your 'inotify limit' has been reached."); - std::process::exit(1); + let percentage_progress = exercises_done as f32 / exercises.len() as f32 * 100.0; + println!( + "Progress: You completed {} / {} exercises ({:.2} %).", + exercises_done, + exercises.len(), + percentage_progress + ); + std::process::exit(0); } - println!( - "{emoji} All exercises completed! {emoji}", - emoji = Emoji("πŸŽ‰", "β˜…") - ); - println!(); - println!("+----------------------------------------------------+"); - println!("| You made it to the Fe-nish line! |"); - println!("+-------------------------- ------------------------+"); - println!(" \\/ "); - println!(" β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ "); - println!(" β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ "); - println!(" β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ "); - println!(" β–‘β–‘β–’β–’β–’β–’β–‘β–‘β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–‘β–‘β–’β–’β–’β–’ "); - println!(" β–“β–“β–“β–“β–“β–“β–“β–“ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–“β–“β–“β–“β–“β–“ "); - println!(" β–’β–’β–’β–’ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–‘β–‘ β–’β–’β–’β–’ "); - println!(" β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’ "); - println!(" β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’ "); - println!(" β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ "); - println!(" β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ "); - println!(" β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ "); - println!(" β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ "); - println!(" β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ "); - println!(" β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ "); - println!(" β–’β–’ β–’β–’ β–’β–’ β–’β–’ "); - println!(); - println!("We hope you enjoyed learning about the various aspects of Rust!"); - println!("If you noticed any issues, please don't hesitate to report them to our repo."); - println!("You can also contribute your own exercises to help the greater community!"); - println!(); - println!("Before reporting an issue or contributing, please read our guidelines:"); - println!("https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"); - } - if matches.subcommand_name().is_none() { - let text = fs::read_to_string("default_out.txt").unwrap(); - println!("{}", text); + Subcommands::Run(subargs) => { + let exercise = find_exercise(&subargs.name, &exercises); + + run(exercise, verbose).unwrap_or_else(|_| std::process::exit(1)); + } + + Subcommands::Reset(subargs) => { + let exercise = find_exercise(&subargs.name, &exercises); + + reset(exercise).unwrap_or_else(|_| std::process::exit(1)); + } + + Subcommands::Hint(subargs) => { + let exercise = find_exercise(&subargs.name, &exercises); + + println!("{}", exercise.hint); + } + + Subcommands::Verify(_subargs) => { + verify(&exercises, (0, exercises.len()), verbose) + .unwrap_or_else(|_| std::process::exit(1)); + } + + Subcommands::Lsp(_subargs) => { + let mut project = RustAnalyzerProject::new(); + project + .get_sysroot_src() + .expect("Couldn't find toolchain path, do you have `rustc` installed?"); + project + .exercies_to_json() + .expect("Couldn't parse rustlings exercises files"); + + if project.crates.is_empty() { + println!("Failed find any exercises, make sure you're in the `rustlings` folder"); + } else if project.write_to_disk().is_err() { + println!("Failed to write rust-project.json to disk for rust-analyzer"); + } else { + println!("Successfully generated rust-project.json"); + println!("rust-analyzer will now parse exercises, restart your language server or editor") + } + } + + Subcommands::Watch(_subargs) => match watch(&exercises, verbose) { + Err(e) => { + println!( + "Error: Could not watch your progress. Error message was {:?}.", + e + ); + println!("Most likely you've run out of disk space or your 'inotify limit' has been reached."); + std::process::exit(1); + } + Ok(WatchStatus::Finished) => { + println!( + "{emoji} All exercises completed! {emoji}", + emoji = Emoji("πŸŽ‰", "β˜…") + ); + println!("\n{}\n", FENISH_LINE); + } + Ok(WatchStatus::Unfinished) => { + println!("We hope you're enjoying learning about Rust!"); + println!("If you want to continue working on the exercises at a later point, you can simply run `rustlings watch` again"); + } + }, } } -fn spawn_watch_shell(failed_exercise_hint: &Arc>>) { +fn spawn_watch_shell( + failed_exercise_hint: &Arc>>, + should_quit: Arc, +) { let failed_exercise_hint = Arc::clone(failed_exercise_hint); - println!("Type 'hint' to get help or 'clear' to clear the screen"); + println!("Welcome to watch mode! You can type 'help' to get an overview of the commands you can use here."); thread::spawn(move || loop { let mut input = String::new(); match io::stdin().read_line(&mut input) { Ok(_) => { let input = input.trim(); - if input.eq("hint") { + if input == "hint" { if let Some(hint) = &*failed_exercise_hint.lock().unwrap() { println!("{}", hint); } - } else if input.eq("clear") { + } else if input == "clear" { println!("\x1B[2J\x1B[1;1H"); + } else if input.eq("quit") { + should_quit.store(true, Ordering::SeqCst); + println!("Bye!"); + } else if input.eq("help") { + println!("Commands available to you in watch mode:"); + println!(" hint - prints the current exercise's hint"); + println!(" clear - clears the screen"); + println!(" quit - quits watch mode"); + println!(" help - displays this help message"); + println!(); + println!("Watch mode automatically re-evaluates the current exercise"); + println!("when you edit a file's contents.") } else { println!("unknown command: {}", input); } @@ -195,7 +314,33 @@ fn spawn_watch_shell(failed_exercise_hint: &Arc>>) { }); } -fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> { +fn find_exercise<'a>(name: &str, exercises: &'a [Exercise]) -> &'a Exercise { + if name.eq("next") { + exercises + .iter() + .find(|e| !e.looks_done()) + .unwrap_or_else(|| { + println!("πŸŽ‰ Congratulations! You have done all the exercises!"); + println!("πŸ”š There are no more exercises to do next!"); + std::process::exit(1) + }) + } else { + exercises + .iter() + .find(|e| e.name == name) + .unwrap_or_else(|| { + println!("No exercise found for '{}'!", name); + std::process::exit(1) + }) + } +} + +enum WatchStatus { + Finished, + Unfinished, +} + +fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result { /* Clears the terminal with an ANSI escape code. Works in UNIX and newer Windows terminals. */ fn clear_screen() { @@ -203,6 +348,7 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> { } let (tx, rx) = channel(); + let should_quit = Arc::new(AtomicBool::new(false)); let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?; watcher.watch(Path::new("./exercises"), RecursiveMode::Recursive)?; @@ -210,23 +356,30 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> { clear_screen(); let to_owned_hint = |t: &Exercise| t.hint.to_owned(); - let failed_exercise_hint = match verify(exercises.iter(), verbose) { - Ok(_) => return Ok(()), + let failed_exercise_hint = match verify(exercises.iter(), (0, exercises.len()), verbose) { + Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => Arc::new(Mutex::new(Some(to_owned_hint(exercise)))), }; - spawn_watch_shell(&failed_exercise_hint); + spawn_watch_shell(&failed_exercise_hint, Arc::clone(&should_quit)); loop { - match rx.recv() { + match rx.recv_timeout(Duration::from_secs(1)) { Ok(event) => match event { DebouncedEvent::Create(b) | DebouncedEvent::Chmod(b) | DebouncedEvent::Write(b) => { if b.extension() == Some(OsStr::new("rs")) && b.exists() { let filepath = b.as_path().canonicalize().unwrap(); let pending_exercises = exercises .iter() - .skip_while(|e| !filepath.ends_with(&e.path)); + .find(|e| filepath.ends_with(&e.path)) + .into_iter() + .chain( + exercises + .iter() + .filter(|e| !e.looks_done() && !filepath.ends_with(&e.path)), + ); + let num_done = exercises.iter().filter(|e| e.looks_done()).count(); clear_screen(); - match verify(pending_exercises, verbose) { - Ok(_) => return Ok(()), + match verify(pending_exercises, (num_done, exercises.len()), verbose) { + Ok(_) => return Ok(WatchStatus::Finished), Err(exercise) => { let mut failed_exercise_hint = failed_exercise_hint.lock().unwrap(); *failed_exercise_hint = Some(to_owned_hint(exercise)); @@ -236,8 +389,15 @@ fn watch(exercises: &[Exercise], verbose: bool) -> notify::Result<()> { } _ => {} }, + Err(RecvTimeoutError::Timeout) => { + // the timeout expired, just check the `should_quit` variable below then loop again + } Err(e) => println!("watch error: {:?}", e), } + // Check if we need to exit + if should_quit.load(Ordering::SeqCst) { + return Ok(WatchStatus::Unfinished); + } } } @@ -250,3 +410,66 @@ fn rustc_exists() -> bool { .map(|status| status.success()) .unwrap_or(false) } + +const DEFAULT_OUT: &str = r#"Thanks for installing Rustlings! + +Is this your first time? Don't worry, Rustlings was made for beginners! We are +going to teach you a lot of things about Rust, but before we can get +started, here's a couple of notes about how Rustlings operates: + +1. The central concept behind Rustlings is that you solve exercises. These + exercises usually have some sort of syntax error in them, which will cause + them to fail compilation or testing. Sometimes there's a logic error instead + of a syntax error. No matter what error, it's your job to find it and fix it! + You'll know when you fixed it because then, the exercise will compile and + Rustlings will be able to move on to the next exercise. +2. If you run Rustlings in watch mode (which we recommend), it'll automatically + start with the first exercise. Don't get confused by an error message popping + up as soon as you run Rustlings! This is part of the exercise that you're + supposed to solve, so open the exercise file in an editor and start your + detective work! +3. If you're stuck on an exercise, there is a helpful hint you can view by typing + 'hint' (in watch mode), or running `rustlings hint exercise_name`. +4. If an exercise doesn't make sense to you, feel free to open an issue on GitHub! + (https://github.com/rust-lang/rustlings/issues/new). We look at every issue, + and sometimes, other learners do too so you can help each other out! +5. If you want to use `rust-analyzer` with exercises, which provides features like + autocompletion, run the command `rustlings lsp`. + +Got all that? Great! To get started, run `rustlings watch` in order to get the first +exercise. Make sure to have your editor open!"#; + +const FENISH_LINE: &str = r#"+----------------------------------------------------+ +| You made it to the Fe-nish line! | ++-------------------------- ------------------------+ + \\/ + β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ + β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ + β–’β–’β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–’β–’ + β–‘β–‘β–’β–’β–’β–’β–‘β–‘β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’β–‘β–‘β–’β–’β–’β–’ + β–“β–“β–“β–“β–“β–“β–“β–“ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–ˆβ–ˆ β–“β–“ β–“β–“β–“β–“β–“β–“β–“β–“ + β–’β–’β–’β–’ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’ β–ˆβ–ˆβ–ˆβ–ˆ β–’β–’β–‘β–‘ β–’β–’β–’β–’ + β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’β–’β–’β–’β–’ β–’β–’ + β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–“β–“β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’β–“β–“β–’β–’β–“β–“β–’β–’β–’β–’β–’β–’β–’β–’ + β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ + β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ + β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ + β–’β–’ β–’β–’ β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’β–’ β–’β–’ β–’β–’ + β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ β–’β–’ + β–’β–’ β–’β–’ β–’β–’ β–’β–’ + +We hope you enjoyed learning about the various aspects of Rust! +If you noticed any issues, please don't hesitate to report them to our repo. +You can also contribute your own exercises to help the greater community! + +Before reporting an issue or contributing, please read our guidelines: +https://github.com/rust-lang/rustlings/blob/main/CONTRIBUTING.md"#; + +const WELCOME: &str = r#" welcome to... + _ _ _ + _ __ _ _ ___| |_| (_)_ __ __ _ ___ + | '__| | | / __| __| | | '_ \ / _` / __| + | | | |_| \__ \ |_| | | | | | (_| \__ \ + |_| \__,_|___/\__|_|_|_| |_|\__, |___/ + |___/"#; diff --git a/src/project.rs b/src/project.rs new file mode 100644 index 00000000..0df00b9a --- /dev/null +++ b/src/project.rs @@ -0,0 +1,90 @@ +use glob::glob; +use serde::{Deserialize, Serialize}; +use std::error::Error; +use std::process::Command; + +/// Contains the structure of resulting rust-project.json file +/// and functions to build the data required to create the file +#[derive(Serialize, Deserialize)] +pub struct RustAnalyzerProject { + sysroot_src: String, + pub crates: Vec, +} + +#[derive(Serialize, Deserialize)] +pub struct Crate { + root_module: String, + edition: String, + deps: Vec, + cfg: Vec, +} + +impl RustAnalyzerProject { + pub fn new() -> RustAnalyzerProject { + RustAnalyzerProject { + sysroot_src: String::new(), + crates: Vec::new(), + } + } + + /// Write rust-project.json to disk + pub fn write_to_disk(&self) -> Result<(), std::io::Error> { + std::fs::write( + "./rust-project.json", + serde_json::to_vec(&self).expect("Failed to serialize to JSON"), + )?; + Ok(()) + } + + /// If path contains .rs extension, add a crate to `rust-project.json` + fn path_to_json(&mut self, path: String) { + if let Some((_, ext)) = path.split_once('.') { + if ext == "rs" { + self.crates.push(Crate { + root_module: path, + edition: "2021".to_string(), + deps: Vec::new(), + // This allows rust_analyzer to work inside #[test] blocks + cfg: vec!["test".to_string()], + }) + } + } + } + + /// Parse the exercises folder for .rs files, any matches will create + /// a new `crate` in rust-project.json which allows rust-analyzer to + /// treat it like a normal binary + pub fn exercies_to_json(&mut self) -> Result<(), Box> { + for e in glob("./exercises/**/*")? { + let path = e?.to_string_lossy().to_string(); + self.path_to_json(path); + } + Ok(()) + } + + /// Use `rustc` to determine the default toolchain + pub fn get_sysroot_src(&mut self) -> Result<(), Box> { + let toolchain = Command::new("rustc") + .arg("--print") + .arg("sysroot") + .output()? + .stdout; + + let toolchain = String::from_utf8_lossy(&toolchain); + let mut whitespace_iter = toolchain.split_whitespace(); + + let toolchain = whitespace_iter.next().unwrap_or(&toolchain); + + println!("Determined toolchain: {}\n", &toolchain); + + self.sysroot_src = (std::path::Path::new(&*toolchain) + .join("lib") + .join("rustlib") + .join("src") + .join("rust") + .join("library") + .to_string_lossy()) + .to_string(); + Ok(()) + } +} diff --git a/src/run.rs b/src/run.rs index fdabb3e0..826f00a6 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,3 +1,5 @@ +use std::process::Command; + use crate::exercise::{Exercise, Mode}; use crate::verify::test; use indicatif::ProgressBar; @@ -15,12 +17,25 @@ pub fn run(exercise: &Exercise, verbose: bool) -> Result<(), ()> { Ok(()) } +// Resets the exercise by stashing the changes. +pub fn reset(exercise: &Exercise) -> Result<(), ()> { + let command = Command::new("git") + .args(["stash", "--"]) + .arg(&exercise.path) + .spawn(); + + match command { + Ok(_) => Ok(()), + Err(_) => Err(()), + } +} + // Invoke the rust compiler on the path of the given exercise // and run the ensuing binary. // This is strictly for non-test binaries, so output is displayed fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {}...", exercise).as_str()); + progress_bar.set_message(format!("Compiling {}...", exercise)); progress_bar.enable_steady_tick(100); let compilation_result = exercise.compile(); @@ -37,7 +52,7 @@ fn compile_and_run(exercise: &Exercise) -> Result<(), ()> { } }; - progress_bar.set_message(format!("Running {}...", exercise).as_str()); + progress_bar.set_message(format!("Running {}...", exercise)); let result = compilation.run(); progress_bar.finish_and_clear(); diff --git a/src/ui.rs b/src/ui.rs index 38cbaa40..1ee46316 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,23 +1,33 @@ macro_rules! warn { ($fmt:literal, $ex:expr) => {{ use console::{style, Emoji}; + use std::env; let formatstr = format!($fmt, $ex); - println!( - "{} {}", - style(Emoji("⚠️ ", "!")).red(), - style(formatstr).red() - ); + if env::var("NO_EMOJI").is_ok() { + println!("{} {}", style("!").red(), style(formatstr).red()); + } else { + println!( + "{} {}", + style(Emoji("⚠️ ", "!")).red(), + style(formatstr).red() + ); + } }}; } macro_rules! success { ($fmt:literal, $ex:expr) => {{ use console::{style, Emoji}; + use std::env; let formatstr = format!($fmt, $ex); - println!( - "{} {}", - style(Emoji("βœ…", "βœ“")).green(), - style(formatstr).green() - ); + if env::var("NO_EMOJI").is_ok() { + println!("{} {}", style("βœ“").green(), style(formatstr).green()); + } else { + println!( + "{} {}", + style(Emoji("βœ…", "βœ“")).green(), + style(formatstr).green() + ); + } }}; } diff --git a/src/verify.rs b/src/verify.rs index 00e45c8c..6f877831 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,6 +1,7 @@ use crate::exercise::{CompiledExercise, Exercise, Mode, State}; use console::style; -use indicatif::ProgressBar; +use indicatif::{ProgressBar, ProgressStyle}; +use std::env; // Verify that the provided container of Exercise objects // can be compiled and run without any failures. @@ -8,18 +9,27 @@ use indicatif::ProgressBar; // If the Exercise being verified is a test, the verbose boolean // determines whether or not the test harness outputs are displayed. pub fn verify<'a>( - start_at: impl IntoIterator, + exercises: impl IntoIterator, + progress: (usize, usize), verbose: bool, ) -> Result<(), &'a Exercise> { - for exercise in start_at { + let (num_done, total) = progress; + let bar = ProgressBar::new(total as u64); + bar.set_style(ProgressStyle::default_bar() + .template("Progress: [{bar:60.green/red}] {pos}/{len}") + .progress_chars("#>-") + ); + bar.set_position(num_done as u64); + for exercise in exercises { let compile_result = match exercise.mode { - Mode::Test => compile_and_test(&exercise, RunMode::Interactive, verbose), - Mode::Compile => compile_and_run_interactively(&exercise), - Mode::Clippy => compile_only(&exercise), + Mode::Test => compile_and_test(exercise, RunMode::Interactive, verbose), + Mode::Compile => compile_and_run_interactively(exercise), + Mode::Clippy => compile_only(exercise), }; if !compile_result.unwrap_or(false) { return Err(exercise); } + bar.inc(1); } Ok(()) } @@ -38,25 +48,24 @@ pub fn test(exercise: &Exercise, verbose: bool) -> Result<(), ()> { // Invoke the rust compiler without running the resulting binary fn compile_only(exercise: &Exercise) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {}...", exercise).as_str()); + progress_bar.set_message(format!("Compiling {}...", exercise)); progress_bar.enable_steady_tick(100); - let _ = compile(&exercise, &progress_bar)?; + let _ = compile(exercise, &progress_bar)?; progress_bar.finish_and_clear(); - success!("Successfully compiled {}!", exercise); - Ok(prompt_for_completion(&exercise, None)) + Ok(prompt_for_completion(exercise, None)) } // Compile the given Exercise and run the resulting binary in an interactive mode fn compile_and_run_interactively(exercise: &Exercise) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Compiling {}...", exercise).as_str()); + progress_bar.set_message(format!("Compiling {}...", exercise)); progress_bar.enable_steady_tick(100); - let compilation = compile(&exercise, &progress_bar)?; + let compilation = compile(exercise, &progress_bar)?; - progress_bar.set_message(format!("Running {}...", exercise).as_str()); + progress_bar.set_message(format!("Running {}...", exercise)); let result = compilation.run(); progress_bar.finish_and_clear(); @@ -70,16 +79,14 @@ fn compile_and_run_interactively(exercise: &Exercise) -> Result { } }; - success!("Successfully ran {}!", exercise); - - Ok(prompt_for_completion(&exercise, Some(output.stdout))) + Ok(prompt_for_completion(exercise, Some(output.stdout))) } // Compile the given Exercise as a test harness and display // the output if verbose is set to true fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Result { let progress_bar = ProgressBar::new_spinner(); - progress_bar.set_message(format!("Testing {}...", exercise).as_str()); + progress_bar.set_message(format!("Testing {}...", exercise)); progress_bar.enable_steady_tick(100); let compilation = compile(exercise, &progress_bar)?; @@ -91,9 +98,8 @@ fn compile_and_test(exercise: &Exercise, run_mode: RunMode, verbose: bool) -> Re if verbose { println!("{}", output.stdout); } - success!("Successfully tested {}", &exercise); if let RunMode::Interactive = run_mode { - Ok(prompt_for_completion(&exercise, None)) + Ok(prompt_for_completion(exercise, None)) } else { Ok(true) } @@ -137,14 +143,32 @@ fn prompt_for_completion(exercise: &Exercise, prompt_output: Option) -> State::Pending(context) => context, }; + match exercise.mode { + Mode::Compile => success!("Successfully ran {}!", exercise), + Mode::Test => success!("Successfully tested {}!", exercise), + Mode::Clippy => success!("Successfully compiled {}!", exercise), + } + + let no_emoji = env::var("NO_EMOJI").is_ok(); + + let clippy_success_msg = if no_emoji { + "The code is compiling, and Clippy is happy!" + } else { + "The code is compiling, and πŸ“Ž Clippy πŸ“Ž is happy!" + }; + let success_msg = match exercise.mode { Mode::Compile => "The code is compiling!", Mode::Test => "The code is compiling, and the tests pass!", - Mode::Clippy => "The code is compiling, and πŸ“Ž Clippy πŸ“Ž is happy!", + Mode::Clippy => clippy_success_msg, }; println!(); - println!("πŸŽ‰ πŸŽ‰ {} πŸŽ‰ πŸŽ‰", success_msg); + if no_emoji { + println!("~*~ {} ~*~", success_msg) + } else { + println!("πŸŽ‰ πŸŽ‰ {} πŸŽ‰ πŸŽ‰", success_msg) + } println!(); if let Some(output) = prompt_output { diff --git a/tests/fixture/state/info.toml b/tests/fixture/state/info.toml index 7bfc697e..547b3a48 100644 --- a/tests/fixture/state/info.toml +++ b/tests/fixture/state/info.toml @@ -9,3 +9,10 @@ name = "pending_test_exercise" path = "pending_test_exercise.rs" mode = "test" hint = """""" + +[[exercises]] +name = "finished_exercise" +path = "finished_exercise.rs" +mode = "compile" +hint = """""" + diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 2baf9b86..1a729232 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -24,7 +24,7 @@ fn fails_when_in_wrong_dir() { fn verify_all_success() { Command::cargo_bin("rustlings") .unwrap() - .arg("v") + .arg("verify") .current_dir("tests/fixture/success") .assert() .success(); @@ -34,7 +34,7 @@ fn verify_all_success() { fn verify_fails_if_some_fails() { Command::cargo_bin("rustlings") .unwrap() - .arg("v") + .arg("verify") .current_dir("tests/fixture/failure") .assert() .code(1); @@ -44,7 +44,7 @@ fn verify_fails_if_some_fails() { fn run_single_compile_success() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "compSuccess"]) + .args(&["run", "compSuccess"]) .current_dir("tests/fixture/success/") .assert() .success(); @@ -54,7 +54,7 @@ fn run_single_compile_success() { fn run_single_compile_failure() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "compFailure"]) + .args(&["run", "compFailure"]) .current_dir("tests/fixture/failure/") .assert() .code(1); @@ -64,7 +64,7 @@ fn run_single_compile_failure() { fn run_single_test_success() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "testSuccess"]) + .args(&["run", "testSuccess"]) .current_dir("tests/fixture/success/") .assert() .success(); @@ -74,7 +74,7 @@ fn run_single_test_success() { fn run_single_test_failure() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "testFailure"]) + .args(&["run", "testFailure"]) .current_dir("tests/fixture/failure/") .assert() .code(1); @@ -84,7 +84,7 @@ fn run_single_test_failure() { fn run_single_test_not_passed() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "testNotPassed.rs"]) + .args(&["run", "testNotPassed.rs"]) .current_dir("tests/fixture/failure/") .assert() .code(1); @@ -94,7 +94,7 @@ fn run_single_test_not_passed() { fn run_single_test_no_filename() { Command::cargo_bin("rustlings") .unwrap() - .arg("r") + .arg("run") .current_dir("tests/fixture/") .assert() .code(1); @@ -104,17 +104,38 @@ fn run_single_test_no_filename() { fn run_single_test_no_exercise() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "compNoExercise.rs"]) + .args(&["run", "compNoExercise.rs"]) .current_dir("tests/fixture/failure") .assert() .code(1); } +#[test] +fn reset_single_exercise() { + Command::cargo_bin("rustlings") + .unwrap() + .args(&["reset", "intro1"]) + .assert() + .code(0); +} + +#[test] +fn reset_no_exercise() { + Command::cargo_bin("rustlings") + .unwrap() + .arg("reset") + .assert() + .code(1) + .stderr(predicates::str::contains( + "positional arguments not provided", + )); +} + #[test] fn get_hint_for_single_test() { Command::cargo_bin("rustlings") .unwrap() - .args(&["h", "testFailure"]) + .args(&["hint", "testFailure"]) .current_dir("tests/fixture/failure") .assert() .code(0) @@ -125,16 +146,24 @@ fn get_hint_for_single_test() { fn all_exercises_require_confirmation() { for exercise in glob("exercises/**/*.rs").unwrap() { let path = exercise.unwrap(); + if path.file_name().unwrap() == "mod.rs" { + continue; + } let source = { let mut file = File::open(&path).unwrap(); let mut s = String::new(); file.read_to_string(&mut s).unwrap(); s }; - source.matches("// I AM NOT DONE").next().expect(&format!( - "There should be an `I AM NOT DONE` annotation in {:?}", - path - )); + source + .matches("// I AM NOT DONE") + .next() + .unwrap_or_else(|| { + panic!( + "There should be an `I AM NOT DONE` annotation in {:?}", + path + ) + }); } } @@ -142,7 +171,7 @@ fn all_exercises_require_confirmation() { fn run_compile_exercise_does_not_prompt() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "pending_exercise"]) + .args(&["run", "pending_exercise"]) .current_dir("tests/fixture/state") .assert() .code(0) @@ -153,7 +182,7 @@ fn run_compile_exercise_does_not_prompt() { fn run_test_exercise_does_not_prompt() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "pending_test_exercise"]) + .args(&["run", "pending_test_exercise"]) .current_dir("tests/fixture/state") .assert() .code(0) @@ -164,20 +193,74 @@ fn run_test_exercise_does_not_prompt() { fn run_single_test_success_with_output() { Command::cargo_bin("rustlings") .unwrap() - .args(&["--nocapture", "r", "testSuccess"]) + .args(&["--nocapture", "run", "testSuccess"]) .current_dir("tests/fixture/success/") .assert() .code(0) - .stdout(predicates::str::contains("THIS TEST TOO SHALL PAS")); + .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS")); } #[test] fn run_single_test_success_without_output() { Command::cargo_bin("rustlings") .unwrap() - .args(&["r", "testSuccess"]) + .args(&["run", "testSuccess"]) .current_dir("tests/fixture/success/") .assert() .code(0) - .stdout(predicates::str::contains("THIS TEST TOO SHALL PAS").not()); + .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS").not()); +} + +#[test] +fn run_rustlings_list() { + Command::cargo_bin("rustlings") + .unwrap() + .args(&["list"]) + .current_dir("tests/fixture/success") + .assert() + .success(); +} + +#[test] +fn run_rustlings_list_no_pending() { + Command::cargo_bin("rustlings") + .unwrap() + .args(&["list"]) + .current_dir("tests/fixture/success") + .assert() + .success() + .stdout(predicates::str::contains("Pending").not()); +} + +#[test] +fn run_rustlings_list_both_done_and_pending() { + Command::cargo_bin("rustlings") + .unwrap() + .args(&["list"]) + .current_dir("tests/fixture/state") + .assert() + .success() + .stdout(predicates::str::contains("Done").and(predicates::str::contains("Pending"))); +} + +#[test] +fn run_rustlings_list_without_pending() { + Command::cargo_bin("rustlings") + .unwrap() + .args(&["list", "--solved"]) + .current_dir("tests/fixture/state") + .assert() + .success() + .stdout(predicates::str::contains("Pending").not()); +} + +#[test] +fn run_rustlings_list_without_done() { + Command::cargo_bin("rustlings") + .unwrap() + .args(&["list", "--unsolved"]) + .current_dir("tests/fixture/state") + .assert() + .success() + .stdout(predicates::str::contains("Done").not()); }