<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-3e0c23f0f191.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/global-c309e0470d2c.css" />
<link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/github-d23884c8cce9.css" />
<meta name="optimizely-datafile" content="{"groups": [], "environmentKey": "production", "rollouts": [], "typedAudiences": [], "projectId": "16737760170", "variables": [], "featureFlags": [], "experiments": [], "version": "4", "audiences": [{"conditions": "[\"or\", {\"match\": \"exact\", \"name\": \"$opt_dummy_attribute\", \"type\": \"custom_attribute\", \"value\": \"$opt_dummy_value\"}]", "id": "$opt_dummy_audience", "name": "Optimizely-Generated Audience for Backwards Compatibility"}], "anonymizeIP": true, "sdkKey": "WTc6awnGuYDdG98CYRban", "attributes": [{"id": "16822470375", "key": "user_id"}, {"id": "17143601254", "key": "spammy"}, {"id": "18175660309", "key": "organization_plan"}, {"id": "18813001570", "key": "is_logged_in"}, {"id": "19073851829", "key": "geo"}, {"id": "20175462351", "key": "requestedCurrency"}, {"id": "20785470195", "key": "country_code"}, {"id": "21656311196", "key": "opened_downgrade_dialog"}], "botFiltering": false, "accountId": "16737760170", "events": [{"experimentIds": [], "id": "17911811441", "key": "hydro_click.dashboard.teacher_toolbox_cta"}, {"experimentIds": [], "id": "18124116703", "key": "submit.organizations.complete_sign_up"}, {"experimentIds": [], "id": "18145892387", "key": "no_metric.tracked_outside_of_optimizely"}, {"experimentIds": [], "id": "18178755568", "key": "click.org_onboarding_checklist.add_repo"}, {"experimentIds": [], "id": "18180553241", "key": "submit.repository_imports.create"}, {"experimentIds": [], "id": "18186103728", "key": "click.help.learn_more_about_repository_creation"}, {"experimentIds": [], "id": "18188530140", "key": "test_event"}, {"experimentIds": [], "id": "18191963644", "key": "click.empty_org_repo_cta.transfer_repository"}, {"experimentIds": [], "id": "18195612788", "key": "click.empty_org_repo_cta.import_repository"}, {"experimentIds": [], "id": "18210945499", "key": "click.org_onboarding_checklist.invite_members"}, {"experimentIds": [], "id": "18211063248", "key": "click.empty_org_repo_cta.create_repository"}, {"experimentIds": [], "id": "18215721889", "key": "click.org_onboarding_checklist.update_profile"}, {"experimentIds": [], "id": "18224360785", "key": "click.org_onboarding_checklist.dismiss"}, {"experimentIds": [], "id": "18234832286", "key": "submit.organization_activation.complete"}, {"experimentIds": [], "id": "18252392383", "key": "submit.org_repository.create"}, {"experimentIds": [], "id": "18257551537", "key": "submit.org_member_invitation.create"}, {"experimentIds": [], "id": "18259522260", "key": "submit.organization_profile.update"}, {"experimentIds": [], "id": "18564603625", "key": "view.classroom_select_organization"}, {"experimentIds": [], "id": "18568612016", "key": "click.classroom_sign_in_click"}, {"experimentIds": [], "id": "18572592540", "key": "view.classroom_name"}, {"experimentIds": [], "id": "18574203855", "key": "click.classroom_create_organization"}, {"experimentIds": [], "id": "18582053415", "key": "click.classroom_select_organization"}, {"experimentIds": [], "id": "18589463420", "key": "click.classroom_create_classroom"}, {"experimentIds": [], "id": "18591323364", "key": "click.classroom_create_first_classroom"}, {"experimentIds": [], "id": "18591652321", "key": "click.classroom_grant_access"}, {"experimentIds": [], "id": "18607131425", "key": "view.classroom_creation"}, {"experimentIds": [], "id": "18831680583", "key": "upgrade_account_plan"}, {"experimentIds": [], "id": "19064064515", "key": "click.signup"}, {"experimentIds": [], "id": "19075373687", "key": "click.view_account_billing_page"}, {"experimentIds": [], "id": "19077355841", "key": "click.dismiss_signup_prompt"}, {"experimentIds": [], "id": "19079713938", "key": "click.contact_sales"}, {"experimentIds": [], "id": "19120963070", "key": "click.compare_account_plans"}, {"experimentIds": [], "id": "19151690317", "key": "click.upgrade_account_cta"}, {"experimentIds": [], "id": "19424193129", "key": "click.open_account_switcher"}, {"experimentIds": [], "id": "19520330825", "key": "click.visit_account_profile"}, {"experimentIds": [], "id": "19540970635", "key": "click.switch_account_context"}, {"experimentIds": [], "id": "19730198868", "key": "submit.homepage_signup"}, {"experimentIds": [], "id": "19820830627", "key": "click.homepage_signup"}, {"experimentIds": [], "id": "19988571001", "key": "click.create_enterprise_trial"}, {"experimentIds": [], "id": "20036538294", "key": "click.create_organization_team"}, {"experimentIds": [], "id": "20040653299", "key": "click.input_enterprise_trial_form"}, {"experimentIds": [], "id": "20062030003", "key": "click.continue_with_team"}, {"experimentIds": [], "id": "20068947153", "key": "click.create_organization_free"}, {"experimentIds": [], "id": "20086636658", "key": "click.signup_continue.username"}, {"experimentIds": [], "id": "20091648988", "key": "click.signup_continue.create_account"}, {"experimentIds": [], "id": "20103637615", "key": "click.signup_continue.email"}, {"experimentIds": [], "id": "20111574253", "key": "click.signup_continue.password"}, {"experimentIds": [], "id": "20120044111", "key": "view.pricing_page"}, {"experimentIds": [], "id": "20152062109", "key": "submit.create_account"}, {"experimentIds": [], "id": "20165800992", "key": "submit.upgrade_payment_form"}, {"experimentIds": [], "id": "20171520319", "key": "submit.create_organization"}, {"experimentIds": [], "id": "20222645674", "key": "click.recommended_plan_in_signup.discuss_your_needs"}, {"experimentIds": [], "id": "20227443657", "key": "submit.verify_primary_user_email"}, {"experimentIds": [], "id": "20234607160", "key": "click.recommended_plan_in_signup.try_enterprise"}, {"experimentIds": [], "id": "20238175784", "key": "click.recommended_plan_in_signup.team"}, {"experimentIds": [], "id": "20239847212", "key": "click.recommended_plan_in_signup.continue_free"}, {"experimentIds": [], "id": "20251097193", "key": "recommended_plan"}, {"experimentIds": [], "id": "20438619534", "key": "click.pricing_calculator.1_member"}, {"experimentIds": [], "id": "20456699683", "key": "click.pricing_calculator.15_members"}, {"experimentIds": [], "id": "20467868331", "key": "click.pricing_calculator.10_members"}, {"experimentIds": [], "id": "20476267432", "key": "click.trial_days_remaining"}, {"experimentIds": [], "id": "20476357660", "key": "click.discover_feature"}, {"experimentIds": [], "id": "20479287901", "key": "click.pricing_calculator.custom_members"}, {"experimentIds": [], "id": "20481107083", "key": "click.recommended_plan_in_signup.apply_teacher_benefits"}, {"experimentIds": [], "id": "20483089392", "key": "click.pricing_calculator.5_members"}, {"experimentIds": [], "id": "20484283944", "key": "click.onboarding_task"}, {"experimentIds": [], "id": "20484996281", "key": "click.recommended_plan_in_signup.apply_student_benefits"}, {"experimentIds": [], "id": "20486713726", "key": "click.onboarding_task_breadcrumb"}, {"experimentIds": [], "id": "20490791319", "key": "click.upgrade_to_enterprise"}, {"experimentIds": [], "id": "20491786766", "key": "click.talk_to_us"}, {"experimentIds": [], "id": "20494144087", "key": "click.dismiss_enterprise_trial"}, {"experimentIds": [], "id": "20499722759", "key": "completed_all_tasks"}, {"experimentIds": [], "id": "20500710104", "key": "completed_onboarding_tasks"}, {"experimentIds": [], "id": "20513160672", "key": "click.read_doc"}, {"experimentIds": [], "id": "20516196762", "key": "actions_enabled"}, {"experimentIds": [], "id": "20518980986", "key": "click.dismiss_trial_banner"}, {"experimentIds": [], "id": "20535446721", "key": "click.issue_actions_prompt.dismiss_prompt"}, {"experimentIds": [], "id": "20557002247", "key": "click.issue_actions_prompt.setup_workflow"}, {"experimentIds": [], "id": "20595070227", "key": "click.pull_request_setup_workflow"}, {"experimentIds": [], "id": "20626600314", "key": "click.seats_input"}, {"experimentIds": [], "id": "20642310305", "key": "click.decrease_seats_number"}, {"experimentIds": [], "id": "20662990045", "key": "click.increase_seats_number"}, {"experimentIds": [], "id": "20679620969", "key": "click.public_product_roadmap"}, {"experimentIds": [], "id": "20761240940", "key": "click.dismiss_survey_banner"}, {"experimentIds": [], "id": "20767210721", "key": "click.take_survey"}, {"experimentIds": [], "id": "20795281201", "key": "click.archive_list"}, {"experimentIds": [], "id": "20966790249", "key": "contact_sales.submit"}, {"experimentIds": [], "id": "20996500333", "key": "contact_sales.existing_customer"}, {"experimentIds": [], "id": "20996890162", "key": "contact_sales.blank_message_field"}, {"experimentIds": [], "id": "21000470317", "key": "contact_sales.personal_email"}, {"experimentIds": [], "id": "21002790172", "key": "contact_sales.blank_phone_field"}, {"experimentIds": [], "id": "21354412592", "key": "click.dismiss_create_readme"}, {"experimentIds": [], "id": "21366102546", "key": "click.dismiss_zero_user_content"}, {"experimentIds": [], "id": "21370252505", "key": "account_did_downgrade"}, {"experimentIds": [], "id": "21370840408", "key": "click.cta_create_readme"}, {"experimentIds": [], "id": "21375451068", "key": "click.cta_create_new_repository"}, {"experimentIds": [], "id": "21385390948", "key": "click.zero_user_content"}, {"experimentIds": [], "id": "21467712175", "key": "click.downgrade_keep"}, {"experimentIds": [], "id": "21484112202", "key": "click.downgrade"}, {"experimentIds": [], "id": "21495292213", "key": "click.downgrade_survey_exit"}, {"experimentIds": [], "id": "21508241468", "key": "click.downgrade_survey_submit"}, {"experimentIds": [], "id": "21512030356", "key": "click.downgrade_support"}, {"experimentIds": [], "id": "21539090022", "key": "click.downgrade_exit"}, {"experimentIds": [], "id": "21543640644", "key": "click_fetch_upstream"}, {"experimentIds": [], "id": "21646510300", "key": "click.move_your_work"}, {"experimentIds": [], "id": "21656151116", "key": "click.add_branch_protection_rule"}, {"experimentIds": [], "id": "21663860599", "key": "click.downgrade_dialog_open"}, {"experimentIds": [], "id": "21687860483", "key": "click.learn_about_protected_branches"}, {"experimentIds": [], "id": "21689050333", "key": "click.dismiss_protect_this_branch"}, {"experimentIds": [], "id": "21864370109", "key": "click.sign_in"}], "revision": "1367"}" />
<meta name="hovercard-subject-tag" content="repository:234530887" data-turbo-transient>
<meta name="google-site-verification" content="c1kuD-K2HIVF635lypcsWPoD4kilo5-jA_wBFyT4uMY">
<meta name="user-login" content="">
<meta name="viewport" content="width=device-width">
<meta name="description" content="Bee is a Swarm client implemented in Go. It’s the basic building block for the Swarm network: a private; decentralized; and self-sustaining network for permissionless publishing and access to your (application) data. - bee/CODINGSTYLE.md at master · ethersphere/bee">
<link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub">
<link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub">
<meta property="fb:app_id" content="1401488693436528">
<meta name="apple-itunes-app" content="app-id=1477376905" />
<meta name="twitter:image:src" content="https://opengraph.githubassets.com/dfc49d584f2ccfcd28049a4903e0dc4e251f37663d82b2fe5bf78f29f47afc48/ethersphere/bee" /><meta name="twitter:site" content="@github" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:title" content="bee/CODINGSTYLE.md at master · ethersphere/bee" /><meta name="twitter:description" content="Bee is a Swarm client implemented in Go. It’s the basic building block for the Swarm network: a private; decentralized; and self-sustaining network for permissionless publishing and access to your ..." />
<meta property="og:image" content="https://opengraph.githubassets.com/dfc49d584f2ccfcd28049a4903e0dc4e251f37663d82b2fe5bf78f29f47afc48/ethersphere/bee" /><meta property="og:image:alt" content="Bee is a Swarm client implemented in Go. It’s the basic building block for the Swarm network: a private; decentralized; and self-sustaining network for permissionless publishing and access to your ..." /><meta property="og:image:width" content="1200" /><meta property="og:image:height" content="600" /><meta property="og:site_name" content="GitHub" /><meta property="og:type" content="object" /><meta property="og:title" content="bee/CODINGSTYLE.md at master · ethersphere/bee" /><meta property="og:url" content="https://github.com/ethersphere/bee" /><meta property="og:description" content="Bee is a Swarm client implemented in Go. It’s the basic building block for the Swarm network: a private; decentralized; and self-sustaining network for permissionless publishing and access to your ..." />
<link rel="assets" href="https://github.githubassets.com/">
<meta name="hostname" content="github.com">
<meta name="expected-hostname" content="github.com">
<meta name="enabled-features" content="TURBO_EXPERIMENT_RISKY,IMAGE_METRIC_TRACKING,GEOJSON_AZURE_MAPS">
<meta data-hydrostats="publish">
<link rel="canonical" href="https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md" data-turbo-transient>
<div class="position-relative js-header-wrapper ">
<a href="#start-of-content" class="px-2 py-4 color-bg-accent-emphasis color-fg-on-emphasis show-on-focus js-skip-to-content">Skip to content</a>
<span data-view-component="true" class="progress-pjax-loader Progress position-fixed width-full">
<span style="width: 0%;" data-view-component="true" class="Progress-item progress-pjax-loader-bar left-0 top-0 color-bg-accent-emphasis"></span>
<script crossorigin="anonymous" defer="defer" type="application/javascript" src="https://github.githubassets.com/assets/sessions-b62ade0f2b09.js"></script>
<div class="flex-1">
<a href="/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&source=header-repo"
class="d-inline-block d-lg-none flex-order-1 f5 no-underline border color-border-default rounded-2 px-2 py-1 color-fg-inherit"
data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="58eb063cef97ff35cfa3191a34e9bcce96148863833093232879c5c8b704c9c9"
>
Sign up
</a>
</div>
<div class="flex-1 flex-order-2 text-right">
<button aria-label="Toggle navigation" aria-expanded="false" type="button" data-view-component="true" class="js-details-target Button--link Button--medium Button d-lg-none color-fg-inherit p-1"> <span class="Button-content">
<span class="Button-label"><div class="HeaderMenu-toggle-bar rounded my-1"></div>
<div class="HeaderMenu-toggle-bar rounded my-1"></div>
<div class="HeaderMenu-toggle-bar rounded my-1"></div></span>
</span>
<div class="HeaderMenu--logged-out p-responsive height-fit position-lg-relative d-lg-flex flex-column flex-auto pt-7 pb-4 top-0">
<div class="header-menu-wrapper d-flex flex-column flex-self-end flex-lg-row flex-justify-between flex-auto p-3 p-lg-0 rounded rounded-lg-0 mt-3 mt-lg-0">
<nav class="mt-0 px-3 px-lg-0 mb-3 mb-lg-0" aria-label="Global">
<ul class="d-lg-flex list-style-none">
<li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item">
<button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-3 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false">
Product
<svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1">
<path fill-rule="evenodd" d="M12.78 6.22a.75.75 0 010 1.06l-4.25 4.25a.75.75 0 01-1.06 0L3.22 7.28a.75.75 0 011.06-1.06L8 9.94l3.72-3.72a.75.75 0 011.06 0z"></path>
- For
- No suggested jump to results
-
<img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28"> <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target"> </div> <div class="border rounded-2 flex-shrink-0 color-bg-subtle px-1 color-fg-muted ml-1 f6 d-none js-jump-to-badge-search"> <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository"> In this repository </span> <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub"> All GitHub </span> <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span> </div> <div aria-hidden="true" class="border rounded-2 flex-shrink-0 color-bg-subtle px-1 color-fg-muted ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump"> Jump to <span class="d-inline-block ml-1 v-align-middle">↵</span> </div>
-
<img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28"> <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target"> </div> <div class="border rounded-2 flex-shrink-0 color-bg-subtle px-1 color-fg-muted ml-1 f6 d-none js-jump-to-badge-search"> <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this organization"> In this organization </span> <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub"> All GitHub </span> <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span> </div> <div aria-hidden="true" class="border rounded-2 flex-shrink-0 color-bg-subtle px-1 color-fg-muted ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump"> Jump to <span class="d-inline-block ml-1 v-align-middle">↵</span> </div>
-
<img class="avatar mr-2 flex-shrink-0 js-jump-to-suggestion-avatar d-none" alt="" aria-label="Team" src="" width="28" height="28"> <div class="jump-to-suggestion-name js-jump-to-suggestion-name flex-auto overflow-hidden text-left no-wrap css-truncate css-truncate-target"> </div> <div class="border rounded-2 flex-shrink-0 color-bg-subtle px-1 color-fg-muted ml-1 f6 d-none js-jump-to-badge-search"> <span class="js-jump-to-badge-search-text-default d-none" aria-label="in this repository"> In this repository </span> <span class="js-jump-to-badge-search-text-global d-none" aria-label="in all of GitHub"> All GitHub </span> <span aria-hidden="true" class="d-inline-block ml-1 v-align-middle">↵</span> </div> <div aria-hidden="true" class="border rounded-2 flex-shrink-0 color-bg-subtle px-1 color-fg-muted ml-1 f6 d-none d-on-nav-focus js-jump-to-badge-jump"> Jump to <span class="d-inline-block ml-1 v-align-middle">↵</span> </div>
<li>
<li>
<li>
<li>
<li>
<li>
<li>
<li>
</ul>
<ul class="list-style-none f5 px-lg-4">
<li class="h4 color-fg-default my-1">Explore</li>
<li>
<li>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle">
<path fill-rule="evenodd" d="M10.604 1h4.146a.25.25 0 01.25.25v4.146a.25.25 0 01-.427.177L13.03 4.03 9.28 7.78a.75.75 0 01-1.06-1.06l3.75-3.75-1.543-1.543A.25.25 0 0110.604 1zM3.75 2A1.75 1.75 0 002 3.75v8.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 12.25v-3.5a.75.75 0 00-1.5 0v3.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25h3.5a.75.75 0 000-1.5h-3.5z"></path>
<li>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle">
<path fill-rule="evenodd" d="M10.604 1h4.146a.25.25 0 01.25.25v4.146a.25.25 0 01-.427.177L13.03 4.03 9.28 7.78a.75.75 0 01-1.06-1.06l3.75-3.75-1.543-1.543A.25.25 0 0110.604 1zM3.75 2A1.75 1.75 0 002 3.75v8.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 12.25v-3.5a.75.75 0 00-1.5 0v3.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25h3.5a.75.75 0 000-1.5h-3.5z"></path>
<li>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle">
<path fill-rule="evenodd" d="M10.604 1h4.146a.25.25 0 01.25.25v4.146a.25.25 0 01-.427.177L13.03 4.03 9.28 7.78a.75.75 0 01-1.06-1.06l3.75-3.75-1.543-1.543A.25.25 0 0110.604 1zM3.75 2A1.75 1.75 0 002 3.75v8.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 12.25v-3.5a.75.75 0 00-1.5 0v3.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25h3.5a.75.75 0 000-1.5h-3.5z"></path>
</ul>
</div>
<li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item">
<button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-3 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false">
Solutions
<svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1">
<path fill-rule="evenodd" d="M12.78 6.22a.75.75 0 010 1.06l-4.25 4.25a.75.75 0 01-1.06 0L3.22 7.28a.75.75 0 011.06-1.06L8 9.94l3.72-3.72a.75.75 0 011.06 0z"></path>
<li>
<li>
<li>
<li>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle">
<path fill-rule="evenodd" d="M10.604 1h4.146a.25.25 0 01.25.25v4.146a.25.25 0 01-.427.177L13.03 4.03 9.28 7.78a.75.75 0 01-1.06-1.06l3.75-3.75-1.543-1.543A.25.25 0 0110.604 1zM3.75 2A1.75 1.75 0 002 3.75v8.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 12.25v-3.5a.75.75 0 00-1.5 0v3.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25h3.5a.75.75 0 000-1.5h-3.5z"></path>
</ul>
<ul class="list-style-none f5 border-bottom pb-3 mb-3">
<li class="h4 color-fg-default my-1">By Solution</li>
<li>
<li>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle">
<path fill-rule="evenodd" d="M10.604 1h4.146a.25.25 0 01.25.25v4.146a.25.25 0 01-.427.177L13.03 4.03 9.28 7.78a.75.75 0 01-1.06-1.06l3.75-3.75-1.543-1.543A.25.25 0 0110.604 1zM3.75 2A1.75 1.75 0 002 3.75v8.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 12.25v-3.5a.75.75 0 00-1.5 0v3.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25h3.5a.75.75 0 000-1.5h-3.5z"></path>
<li>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle">
<path fill-rule="evenodd" d="M10.604 1h4.146a.25.25 0 01.25.25v4.146a.25.25 0 01-.427.177L13.03 4.03 9.28 7.78a.75.75 0 01-1.06-1.06l3.75-3.75-1.543-1.543A.25.25 0 0110.604 1zM3.75 2A1.75 1.75 0 002 3.75v8.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 12.25v-3.5a.75.75 0 00-1.5 0v3.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25h3.5a.75.75 0 000-1.5h-3.5z"></path>
</ul>
<ul class="list-style-none f5 ">
<li class="h4 color-fg-default my-1">Case Studies</li>
<li>
<li>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-link-external HeaderMenu-external-icon color-fg-subtle">
<path fill-rule="evenodd" d="M10.604 1h4.146a.25.25 0 01.25.25v4.146a.25.25 0 01-.427.177L13.03 4.03 9.28 7.78a.75.75 0 01-1.06-1.06l3.75-3.75-1.543-1.543A.25.25 0 0110.604 1zM3.75 2A1.75 1.75 0 002 3.75v8.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 12.25v-3.5a.75.75 0 00-1.5 0v3.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-8.5a.25.25 0 01.25-.25h3.5a.75.75 0 000-1.5h-3.5z"></path>
</ul>
</div>
<li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item">
<button type="button" class="HeaderMenu-link border-0 width-full width-lg-auto px-0 px-lg-2 py-3 py-lg-2 no-wrap d-flex flex-items-center flex-justify-between js-details-target" aria-expanded="false">
Open Source
<svg opacity="0.5" aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-chevron-down HeaderMenu-icon ml-1">
<path fill-rule="evenodd" d="M12.78 6.22a.75.75 0 010 1.06l-4.25 4.25a.75.75 0 01-1.06 0L3.22 7.28a.75.75 0 011.06-1.06L8 9.94l3.72-3.72a.75.75 0 011.06 0z"></path>
<li>
<div>
<div class="color-fg-default h4">GitHub Sponsors</div>
Fund open source developers
</div>
</ul>
<ul class="list-style-none f5 border-bottom pb-3 mb-3">
<li>
<div>
<div class="color-fg-default h4">The ReadME Project</div>
GitHub community articles
</div>
</ul>
<ul class="list-style-none f5 ">
<li class="h4 color-fg-default my-1">Repositories</li>
<li>
<li>
<li>
</ul>
</div>
<li class="HeaderMenu-item position-relative flex-wrap flex-justify-between flex-items-center d-block d-lg-flex flex-lg-nowrap flex-lg-items-center js-details-container js-header-menu-item">
<a class="HeaderMenu-link no-underline px-0 px-lg-2 py-3 py-lg-2 d-block d-lg-inline-block" data-analytics-event="{"category":"Header menu top item (logged out)","action":"click to go to Pricing","label":"ref_cta:Pricing;"}" href="/pricing">Pricing</a>
</ul>
</nav>
<div class="d-lg-flex flex-items-center px-3 px-lg-0 mb-3 mb-lg-0 text-center text-lg-left">
<div class="d-lg-flex min-width-0 mb-2 mb-lg-0">
placeholder="Search"
data-unscoped-placeholder="Search GitHub"
data-scoped-placeholder="Search"
autocapitalize="off"
role="combobox"
aria-haspopup="listbox"
aria-expanded="false"
aria-autocomplete="list"
aria-controls="jump-to-results"
aria-label="Search"
data-jump-to-suggestions-path="/_graphql/GetSuggestedNavigationDestinations"
spellcheck="false"
autocomplete="off"
>
<input type="hidden" data-csrf="true" class="js-data-jump-to-suggestions-path-csrf" value="0dWPOyMY+YqTq1LfdU0fOI0qq4re8qYECola0M2/IwUWIaI3Mk0Nggm84odeXjJs0AsrbPub7EINoDmvvUbv3Q==" />
<input type="hidden" class="js-site-search-type-field" name="type" >
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="20" aria-hidden="true" class="mr-1 header-search-key-slash"><path fill="none" stroke="#979A9C" opacity=".4" d="M3.5.5h12c1.7 0 3 1.3 3 3v13c0 1.7-1.3 3-3 3h-12c-1.7 0-3-1.3-3-3v-13c0-1.7 1.3-3 3-3z"></path><path fill="#979A9C" d="M11.8 6L8 15.1h-.9L10.8 6h1z"></path></svg>
<div class="Box position-absolute overflow-hidden d-none jump-to-suggestions js-jump-to-suggestions-container">
</div>
</label>
</div>
<div class="position-relative mr-lg-3 d-lg-inline-block">
<a href="/login?return_to=https%3A%2F%2Fgh.neting.cc%2Fethersphere%2Fbee%2Fblob%2Fmaster%2FCODINGSTYLE.md"
class="HeaderMenu-link HeaderMenu-link--sign-in flex-shrink-0 no-underline d-block d-lg-inline-block border border-lg-0 rounded rounded-lg-0 p-2 p-lg-0"
data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="83a395da31699cc4c3fb3afcd0c4a0ecc7930ddc3b26add30e6329cb0aa9425c"
data-ga-click="(Logged out) Header, clicked Sign in, text:sign-in">
Sign in
</a>
</div>
<a href="/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fblob%2Fshow&source=header-repo&source_repo=ethersphere%2Fbee"
class="HeaderMenu-link HeaderMenu-link--sign-up flex-shrink-0 d-none d-lg-inline-block no-underline border color-border-default rounded px-2 py-1"
data-hydro-click="{"event_type":"authentication.click","payload":{"location_in_page":"site header menu","repository_id":null,"auth_type":"SIGN_UP","originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="83a395da31699cc4c3fb3afcd0c4a0ecc7930ddc3b26add30e6329cb0aa9425c"
data-analytics-event="{"category":"Sign up","action":"click to sign up for account","label":"ref_page:/<user-name>/<repo-name>/blob/show;ref_cta:Sign up;ref_loc:header logged out"}"
>
Sign up
</a>
</div>
</div>
</div>
</div>
<div id="js-flash-container" data-turbo-replace>
<div>{{ message }}</div>
</div>
<div class="d-flex flex-wrap flex-justify-end mb-3 px-3 px-md-4 px-lg-5" style="gap: 1rem;">
<div class="flex-auto min-width-0 width-fit mr-3">
<span class="author flex-self-stretch" itemprop="author">
<a class="url fn" rel="author" data-hovercard-type="organization" data-hovercard-url="/orgs/ethersphere/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="/ethersphere">ethersphere</a>
</span>
<span class="mx-1 flex-self-stretch color-fg-muted">/</span>
<strong itemprop="name" class="mr-2 flex-self-stretch">
<a data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" href="/ethersphere/bee">bee</a>
</strong>
<span></span><span class="Label Label--secondary v-align-middle mr-1">Public</span>
</div>
<ul class="pagehead-actions flex-shrink-0 d-none d-md-inline" style="padding: 2px 0;">
</div>
<div id="responsive-meta-container" data-turbo-replace>
<nav data-pjax="#js-repo-pjax-container" aria-label="Repository" data-view-component="true" class="js-repo-nav js-sidenav-container-pjax js-responsive-underlinenav overflow-hidden UnderlineNav px-3 px-md-4 px-lg-5">
-
Code
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-code UnderlineNav-octicon d-none d-sm-inline"> <path fill-rule="evenodd" d="M4.72 3.22a.75.75 0 011.06 1.06L2.06 8l3.72 3.72a.75.75 0 11-1.06 1.06L.47 8.53a.75.75 0 010-1.06l4.25-4.25zm6.56 0a.75.75 0 10-1.06 1.06L13.94 8l-3.72 3.72a.75.75 0 101.06 1.06l4.25-4.25a.75.75 0 000-1.06l-4.25-4.25z"></path>
-
Issues 120
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-issue-opened UnderlineNav-octicon d-none d-sm-inline"> <path d="M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path><path fill-rule="evenodd" d="M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z"></path>
-
Pull requests 14
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-git-pull-request UnderlineNav-octicon d-none d-sm-inline"> <path fill-rule="evenodd" d="M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z"></path>
-
Discussions
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-comment-discussion UnderlineNav-octicon d-none d-sm-inline"> <path fill-rule="evenodd" d="M1.5 2.75a.25.25 0 01.25-.25h8.5a.25.25 0 01.25.25v5.5a.25.25 0 01-.25.25h-3.5a.75.75 0 00-.53.22L3.5 11.44V9.25a.75.75 0 00-.75-.75h-1a.25.25 0 01-.25-.25v-5.5zM1.75 1A1.75 1.75 0 000 2.75v5.5C0 9.216.784 10 1.75 10H2v1.543a1.457 1.457 0 002.487 1.03L7.061 10h3.189A1.75 1.75 0 0012 8.25v-5.5A1.75 1.75 0 0010.25 1h-8.5zM14.5 4.75a.25.25 0 00-.25-.25h-.5a.75.75 0 110-1.5h.5c.966 0 1.75.784 1.75 1.75v5.5A1.75 1.75 0 0114.25 12H14v1.543a1.457 1.457 0 01-2.487 1.03L9.22 12.28a.75.75 0 111.06-1.06l2.22 2.22v-2.19a.75.75 0 01.75-.75h1a.25.25 0 00.25-.25v-5.5z"></path>
-
Actions
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-play UnderlineNav-octicon d-none d-sm-inline"> <path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zM6.379 5.227A.25.25 0 006 5.442v5.117a.25.25 0 00.379.214l4.264-2.559a.25.25 0 000-.428L6.379 5.227z"></path>
-
Projects 0
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-table UnderlineNav-octicon d-none d-sm-inline"> <path fill-rule="evenodd" d="M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v12.5A1.75 1.75 0 0114.25 16H1.75A1.75 1.75 0 010 14.25V1.75zM1.5 6.5v7.75c0 .138.112.25.25.25H5v-8H1.5zM5 5H1.5V1.75a.25.25 0 01.25-.25H5V5zm1.5 1.5v8h7.75a.25.25 0 00.25-.25V6.5h-8zm8-1.5h-8V1.5h7.75a.25.25 0 01.25.25V5z"></path>
-
Security
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-shield UnderlineNav-octicon d-none d-sm-inline"> <path fill-rule="evenodd" d="M7.467.133a1.75 1.75 0 011.066 0l5.25 1.68A1.75 1.75 0 0115 3.48V7c0 1.566-.32 3.182-1.303 4.682-.983 1.498-2.585 2.813-5.032 3.855a1.7 1.7 0 01-1.33 0c-2.447-1.042-4.049-2.357-5.032-3.855C1.32 10.182 1 8.566 1 7V3.48a1.75 1.75 0 011.217-1.667l5.25-1.68zm.61 1.429a.25.25 0 00-.153 0l-5.25 1.68a.25.25 0 00-.174.238V7c0 1.358.275 2.666 1.057 3.86.784 1.194 2.121 2.34 4.366 3.297a.2.2 0 00.154 0c2.245-.956 3.582-2.104 4.366-3.298C13.225 9.666 13.5 8.36 13.5 7V3.48a.25.25 0 00-.174-.237l-5.25-1.68zM9 10.5a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.75a.75.75 0 10-1.5 0v3a.75.75 0 001.5 0v-3z"></path>
-
Insights
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-graph UnderlineNav-octicon d-none d-sm-inline"> <path fill-rule="evenodd" d="M1.5 1.75a.75.75 0 00-1.5 0v12.5c0 .414.336.75.75.75h14.5a.75.75 0 000-1.5H1.5V1.75zm14.28 2.53a.75.75 0 00-1.06-1.06L10 7.94 7.53 5.47a.75.75 0 00-1.06 0L3.22 8.72a.75.75 0 001.06 1.06L7 7.06l2.47 2.47a.75.75 0 001.06 0l5.25-5.25z"></path>
<div data-view-component="true"> A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
</div>
<footer class="Overlay-footer Overlay-footer--alignEnd">
<button data-close-dialog-id="warn-tag-match-create-branch-dialog" type="button" data-view-component="true" class="btn"> Cancel
bee/CODINGSTYLE.md
- Go to file T
- Go to line L
-
Copy path
-
<span class="d-flex flex-items-baseline"> <span class="flex-auto">Copy permalink</span> </span>
<div id="spoof-warning" class="mt-0 pb-3" hidden aria-hidden>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert float-left mt-1">
<path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path>
<div class="overflow-hidden">This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.</div>
<include-fragment src="/ethersphere/bee/spoofed_commit_check/e1674285bc17ca684b332d682d1993a6726a4d20" data-test-selector="spoofed-commit-check"></include-fragment>
<div class="Box d-flex flex-column flex-shrink-0 mb-3">
<div class="Box-body d-flex flex-items-center" >
<div class="Skeleton Skeleton--text col-1"> </div>
<span class="color-fg-danger h6 loader-error">Cannot retrieve contributors at this time</span>
</div>
<readme-toc>
<div data-target="readme-toc.content" class="Box mt-3 position-relative">
<details
data-target="readme-toc.trigger" data-menu-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"trigger","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-menu-hydro-click-hmac="747ddc9f7965eda6e289b17ef9b6cba502bad3072e7c013efd4f24a4af175511" class="dropdown details-reset details-overlay"
<div class="SelectMenu-filter">
<input
class="SelectMenu-input form-control js-filterable-field"
id="toc-filter-field"
type="text"
autocomplete="off"
spellcheck="false"
autofocus
placeholder="Filter headings"
aria-label="Filter headings">
</div>
<div class="SelectMenu-list SelectMenu-list--borderless p-2" style="overscroll-behavior: contain;" data-filterable-for="toc-filter-field" data-filterable-type="substring">
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 text-emphasized" style="-webkit-box-orient: vertical; padding-left: 12px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#go-style-guide">Go Style Guide</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#consistent-spelling-and-naming">Consistent Spelling and Naming</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#code-formatting">Code Formatting</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#unused-names">Unused Names</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#naked-returns-and-named-parameters">Naked returns and Named Parameters</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#testing">Testing</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#parallel-test-execution">Parallel Test Execution</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#naming-tests">Naming Tests</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#make-zero-value-useful">Make Zero-value Useful</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#beware-of-copying-mutexes-in-go">Beware of Copying Mutexes in Go</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#copy-slices-and-maps-at-boundaries">Copy Slices and Maps at Boundaries</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#receiving-slices-and-maps">Receiving Slices and Maps</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#returning-slices-and-maps">Returning Slices and Maps</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#filtering-in-place">Filtering in place</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#pointers-to-interfaces">Pointers to Interfaces</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#verify-interface-compliance">Verify Interface Compliance</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#receivers-and-interfaces">Receivers and Interfaces</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#defer-to-clean-up">Defer to Clean Up</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#channel-size-is-one-or-none">Channel Size is One or None</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#start-enums-at-one">Start Enums at One</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#use-time-to-handle-time">Use "time" to handle time</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#use-timetime-for-instants-of-time">Use time.Time for instants of time</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#use-timeduration-for-periods-of-time">Use time.Duration for periods of time</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#use-timetime-and-timeduration-with-external-systems">Use time.Time and time.Duration with external systems</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#error-types">Error Types</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#error-wrapping">Error Wrapping</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#handle-type-assertion-failures">Handle Type Assertion Failures</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#panic-with-care">Panic with care</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#avoid-mutable-globals">Avoid Mutable Globals</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#avoid-embedding-types-in-public-structs">Avoid Embedding Types in Public Structs</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#avoid-using-built-in-names">Avoid Using Built-In Names</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#avoid-init">Avoid init()</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#exit-in-main">Exit in Main</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#exit-once">Exit Once</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 24px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#performance">Performance</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#prefer-strconv-over-fmt">Prefer strconv over fmt</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#avoid-string-to-byte-conversion">Avoid string-to-byte conversion</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#prefer-specifying-container-capacity">Prefer Specifying Container Capacity</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#specifying-map-capacity-hints">Specifying Map Capacity Hints</a>
<a role="menuitem" class="filter-item SelectMenu-item ws-normal wb-break-word line-clamp-2 py-1 " style="-webkit-box-orient: vertical; padding-left: 36px;" data-action="click:readme-toc#blur" data-targets="readme-toc.entries" data-hydro-click="{"event_type":"repository_toc_menu.click","payload":{"target":"entry","repository_id":234530887,"originating_url":"https://github.com/ethersphere/bee/blob/master/CODINGSTYLE.md","user_id":null}}" data-hydro-click-hmac="55090eaa7418d0d092f33235fc22c367bcbb279f7319eb6519633b511a62bf7e" href="#specifying-slice-capacity">Specifying Slice Capacity</a>
</div>
</div>
2134 lines (1524 sloc)
<span class="file-info-divider"></span>
44.6 KB
<div class="BtnGroup">
<a data-permalink-href="/ethersphere/bee/raw/e1674285bc17ca684b332d682d1993a6726a4d20/CODINGSTYLE.md" href="/ethersphere/bee/raw/master/CODINGSTYLE.md" id="raw-url" data-view-component="true" class="js-permalink-replaceable-link btn-sm btn BtnGroup-item"> Raw
<div class="d-flex">
<a data-platforms="windows,mac" aria-label="Open this file in GitHub Desktop" href="https://desktop.github.com" data-view-component="true" class="SelectMenu-item no-wrap js-remove-unless-platform width-full text-normal color-fg-default f5">
Open in GitHub Desktop
<button class="btn-octicon btn-octicon-danger disabled tooltipped tooltipped-nw" disabled
aria-label="You must be signed in to make or propose changes" type="button">
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-trash">
<path fill-rule="evenodd" d="M6.5 1.75a.25.25 0 01.25-.25h2.5a.25.25 0 01.25.25V3h-3V1.75zm4.5 0V3h2.25a.75.75 0 010 1.5H2.75a.75.75 0 010-1.5H5V1.75C5 .784 5.784 0 6.75 0h2.5C10.216 0 11 .784 11 1.75zM4.496 6.675a.75.75 0 10-1.492.15l.66 6.6A1.75 1.75 0 005.405 15h5.19c.9 0 1.652-.681 1.741-1.576l.66-6.6a.75.75 0 00-1.492-.149l-.66 6.6a.25.25 0 01-.249.225h-5.19a.25.25 0 01-.249-.225l-.66-6.6z"></path>
<div class="d-flex hide-lg hide-xl flex-order-2 flex-grow-0">
<details class="dropdown details-reset details-overlay d-inline-block">
<summary
class="js-blob-dropdown-click btn-octicon p-2"
aria-haspopup="true"
aria-label="possible actions"
data-dropdown-tracking="{"type":"blob_edit_dropdown.more_options_click","context":{"repository_id":234530887,"actor_id":null,"github_dev_enabled":false,"edit_enabled":false,"small_screen":true}}"
>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-kebab-horizontal">
<path d="M8 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zM1.5 9a1.5 1.5 0 100-3 1.5 1.5 0 000 3zm13 0a1.5 1.5 0 100-3 1.5 1.5 0 000 3z"></path>
<ul class="dropdown-menu dropdown-menu-sw" style="width: 175px">
<li>
<a class="dropdown-item tooltipped tooltipped-nw js-remove-unless-platform"
data-platforms="windows,mac"
href="https://desktop.github.com">
Open with Desktop
</a>
</li>
<li>
<a class="dropdown-item" href="/ethersphere/bee/raw/master/CODINGSTYLE.md">
View raw
</a>
</li>
<li>
</li>
<li>
<a class="dropdown-item" href="/ethersphere/bee/blame/master/CODINGSTYLE.md">
View blame
</a>
</li>
</ul>
</details>
</div>
<div id="readme" class="Box-body readme blob js-code-block-container p-5 p-xl-6 gist-border-0">
<article class="markdown-body entry-content container-lg" itemprop="text"><h1 dir="auto"><a id="user-content-go-style-guide" class="anchor" aria-hidden="true" href="#go-style-guide"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Go Style Guide</h1>
- Go Style Guide
- Consistent Spelling and Naming
- Code Formatting
- Unused Names
- Naked returns and Named Parameters
- Testing
- Group Declarations by Meaning
- Make Zero-value Useful
- Beware of Copying Mutexes in Go
- Copy Slices and Maps at Boundaries
- Pointers to Interfaces
- Receivers and Interfaces
- Defer to Clean Up
- Channel Size is One or None
- Start Enums at One
- Use
"time"
to handle time - Error Types
- Avoid Mutable Globals
- Avoid Embedding Types in Public Structs
- Avoid Using Built-In Names
- Avoid
init()
- Exit in Main
- Performance
Naming is difficult, but prefer the consistency of naming throughout the code base.
If a naming pattern is already established in the codebase, follow it. If you are unsure, look in the Golang standard library for inspiration.
It's similar to the gofmt
tool, the formatting isn't to everyone's liking, but it is consistent.
Prefer american spellings over British spellings, avoid Latin abbreviations.
Note: misspell
linter should take care of these, but it's good to keep in mind.
Bad | Good |
---|---|
// marshalling // unmarshalling // cancelling // cancelled // cancelation |
// marshaling // unmarshaling // canceling // canceled // cancellation |
- Keep line length, argument count and function size reasonable.
- Run
make lint-style
command to lint the codebase using a set of style linters. - Run
make format FOLDER=pkg/mypackage
to format the code usinggci
andgfumpt
formatters.
Avoid unused method receiver names.
Bad | Good |
---|---|
func (f foo) method() {
// no references to f
}
func method(_ string) { ... } |
func (foo) method() {
...
}
func method(string) { ... } |
Note: there might be a linter to take care of this
Don't name result parameters just to avoid declaring a var inside the function; that trades off a minor implementation brevity at the cost of unnecessary API verbosity.
Naked returns are okay if the function is a handful of lines. Once it's a medium sized function, be explicit with your return values.
return }
Corollary: it's not worth it to name result parameters just because it enables you to use naked returns. Clarity of docs is always more important than saving a line or two in your function.
Finally, in some cases you need to name a result parameter in order to change it in a deferred closure. That is always OK.
id, err = /* call to db */
return }
Use the Golang testing package from the standard library for writing tests.
Run tests in parallel where possible but don't forget about variable scope gotchas.
for tc := range tt { tc := tc // must not forget this t.Run(tc.name, func(t *testing.T) { t.Parallel() //execute // ... // assert if got != tt.want { // useful for human comparable values t.Errorf("wrong output\ngot: %q\nwant: %q", got, want) // alternatively t.Errorf("Foo(%q) = %d; want %d", tt.in, got, tt.want) } }) }
Name tests with a compact name that reflects their scenario. Don't try to specify the scenario in the test name, that is not what it's for. Use the accompanying godoc to describe the test scenario.
Bad | Good |
---|---|
```go func TestSomethingBySettingVarToFive(t *testing.T) { ... } ``` | ```go // TestSomething tests that something works correctly by doing this and that. func TestSomething(t *testing.T) { ... } ``` |
If needed, use an underscore to disambiguate tests that are hard to name:
func TestScenario_CornerCase(t *testing.T) { ... }
Ideally, try to use nested tests that would cause the test runner to automatically assemble the different test cases in separate entries:
const (
limitDays = 90
warningDays = 0.9 * limitDays
sleepFor = 30 * time.Minute
)
var (
_ Interface = (*Accounting)(nil)
balancesPrefix = "accounting_balance_"
someOtherPrefix = "some_other_balance_"
ErrLimitExceeded = errors.New("limit exeeded")
)
"><pre><span class="pl-k">func</span> <span class="pl-en">TestSomething</span>(<span class="pl-s1">t</span> <span class="pl-c1">*</span>testing.<span class="pl-smi">T</span>) {
<span class="pl-c1">...</span>
<span class="pl-s1">t</span>.<span class="pl-en">Run</span>(<span class="pl-s">"edge case"</span>, <span class="pl-k">func</span>(<span class="pl-s1">t</span> <span class="pl-c1">*</span>testing.<span class="pl-smi">T</span>) { <span class="pl-c1">...</span> })
}
<span class="pl-s1">Lastly</span>, <span class="pl-s1">please</span>, <span class="pl-s1">goodness</span>, <span class="pl-s1">don</span>'t <span class="pl-s1">use</span> <span class="pl-s1">the</span> <span class="pl-s1">word</span> <span class="pl-s">"fail"</span> <span class="pl-s1">when</span> <span class="pl-s1">naming</span> tests. <span class="pl-smi">Since</span> <span class="pl-s1">the</span> <span class="pl-k">go</span> <span class="pl-s1">test</span> <span class="pl-s1">runner</span> <span class="pl-s1">uses</span> <span class="pl-s1">the</span> <span class="pl-s1">same</span> <span class="pl-s1">keyword</span> <span class="pl-s1">to</span> <span class="pl-s1">denote</span> <span class="pl-s1">failed</span> <span class="pl-s1">tests</span>, <span class="pl-s1">this</span> <span class="pl-s1">just</span> <span class="pl-s1">prolongs</span> <span class="pl-s1">the</span> <span class="pl-s1">search</span> <span class="pl-s1">for</span> <span class="pl-s1">relevant</span> <span class="pl-s1">information</span> <span class="pl-s1">when</span> <span class="pl-s1">inspecting</span> <span class="pl-s1">build</span> artifacts.
## <span class="pl-smi">Group</span> <span class="pl-s1">Declarations</span> <span class="pl-s1">by</span> <span class="pl-s1">Meaning</span>
<span class="pl-s1">Where</span> <span class="pl-s1">possible</span>, <span class="pl-s1">group</span> <span class="pl-s1">declarations</span> <span class="pl-s1">by</span> <span class="pl-s1">their</span> <span class="pl-s1">purpose</span>.
<span class="pl-c1"><</span><span class="pl-s1">table</span><span class="pl-c1">></span>
<span class="pl-c1"><</span><span class="pl-s1">thead</span><span class="pl-c1">></span><span class="pl-c1"><</span><span class="pl-s1">tr</span><span class="pl-c1">></span><span class="pl-c1"><</span><span class="pl-s1">th</span><span class="pl-c1">></span><span class="pl-s1">Bad</span><span class="pl-c1"><</span><span class="pl-c1">/</span><span class="pl-s1">th</span><span class="pl-c1">></span><span class="pl-c1"><</span><span class="pl-s1">th</span><span class="pl-c1">></span><span class="pl-s1">Good</span><span class="pl-c1"><</span><span class="pl-c1">/</span><span class="pl-s1">th</span><span class="pl-c1">></span><span class="pl-c1"><</span><span class="pl-c1">/</span><span class="pl-s1">tr</span><span class="pl-c1">></span><span class="pl-c1"><</span><span class="pl-c1">/</span><span class="pl-s1">thead</span><span class="pl-c1">></span>
<span class="pl-c1"><</span><span class="pl-s1">tbody</span><span class="pl-c1">></span>
<span class="pl-c1"><</span><span class="pl-s1">tr</span><span class="pl-c1">></span><span class="pl-c1"><</span><span class="pl-s1">td</span><span class="pl-c1">></span>
<span class="pl-s">``</span>`go
const (
limitDays = 90
warningDays = 0.9 * limitDays
sleepFor = 30 * time.Minute
)
var (
_ Interface = (*Accounting)(nil)
balancesPrefix = "accounting_balance_"
someOtherPrefix = "some_other_balance_"
ErrLimitExceeded = errors.New("limit exeeded")
)
</pre></div>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="const (
limitDays = 90
warningDays = 0.9 * limitDays
)
const sleepFor = 30 * time.Minute
var _ Interface = (*Accounting)(nil)
var ( // or const
balancesPrefix = "accounting_balance_"
someOtherPrefix = "some_other_balance_"
)
var ErrLimitExceeded = errors.New("limit exeeded)"><pre><span class="pl-k">const</span> (
<span class="pl-s1">limitDays</span> <span class="pl-c1">=</span> <span class="pl-c1">90</span>
<span class="pl-s1">warningDays</span> <span class="pl-c1">=</span> <span class="pl-c1">0.9</span> <span class="pl-c1">*</span> <span class="pl-s1">limitDays</span>
)
<span class="pl-k">const</span> <span class="pl-s1">sleepFor</span> <span class="pl-c1">=</span> <span class="pl-c1">30</span> <span class="pl-c1">*</span> <span class="pl-s1">time</span>.<span class="pl-c1">Minute</span>
<span class="pl-k">var</span> <span class="pl-s1">_</span> <span class="pl-smi">Interface</span> <span class="pl-c1">=</span> (<span class="pl-c1">*</span><span class="pl-s1">Accounting</span>)(<span class="pl-c1">nil</span>)
<span class="pl-k">var</span> ( <span class="pl-c">// or const</span>
<span class="pl-s1">balancesPrefix</span> <span class="pl-c1">=</span> <span class="pl-s">"accounting_balance_"</span>
<span class="pl-s1">someOtherPrefix</span> <span class="pl-c1">=</span> <span class="pl-s">"some_other_balance_"</span>
)
<span class="pl-k">var</span> <span class="pl-s1">ErrLimitExceeded</span> <span class="pl-c1">=</span> errors.<span class="pl-smi">New</span>("limit exeeded)</pre></div>
<h2 dir="auto"><a id="user-content-make-zero-value-useful" class="anchor" aria-hidden="true" href="#make-zero-value-useful"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Make Zero-value Useful</h2>
<ul dir="auto">
<li>
<p dir="auto">The zero-value of <code>sync.Mutex</code> and <code>sync.RWMutex</code> is valid, so you almost never need a pointer to a mutex.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="mu := new(sync.Mutex)
mu.Lock()"><pre><span class="pl-s1">mu</span> <span class="pl-c1">:=</span> <span class="pl-en">new</span>(sync.<span class="pl-smi">Mutex</span>)
<span class="pl-s1">mu</span>.<span class="pl-en">Lock</span>()</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var mu sync.Mutex
mu.Lock()"><pre><span class="pl-k">var</span> <span class="pl-s1">mu</span> sync.<span class="pl-smi">Mutex</span>
<span class="pl-s1">mu</span>.<span class="pl-en">Lock</span>()</pre></div>
</td></tr>
</tbody></table>
</li>
<li>
<p dir="auto">For the same reason, a struct field of <code>sync.Mutex</code> does not require explicit initialization.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Store struct {
mu sync.Mutex
}
s := Store{
mu: sync.Mutex{},
}
// use s"><pre><span class="pl-k">type</span> <span class="pl-smi">Store</span> <span class="pl-k">struct</span> {
<span class="pl-c1">mu</span> sync.<span class="pl-smi">Mutex</span>
}
<span class="pl-s1">s</span> <span class="pl-c1">:=</span> <span class="pl-smi">Store</span>{
<span class="pl-c1">mu</span>: sync.<span class="pl-smi">Mutex</span>{},
}
<span class="pl-c">// use s</span></pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Store struct {
mu sync.Mutext
}
var s Store
// use s"><pre><span class="pl-k">type</span> <span class="pl-smi">Store</span> <span class="pl-k">struct</span> {
<span class="pl-c1">mu</span> sync.<span class="pl-smi">Mutext</span>
}
<span class="pl-k">var</span> <span class="pl-s1">s</span> <span class="pl-smi">Store</span>
<span class="pl-c">// use s</span></pre></div>
</td></tr>
</tbody></table>
</li>
<li>
<p dir="auto">The zero value (a slice declared with <code>var</code>) is usable immediately without <code>make()</code>.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="nums := []int{}
// or, nums := make([]int)
if add1 {
nums = append(nums, 1)
}
if add2 {
nums = append(nums, 2)
}"><pre><span class="pl-s1">nums</span> <span class="pl-c1">:=</span> []<span class="pl-smi">int</span>{}
<span class="pl-c">// or, nums := make([]int)</span>
<span class="pl-k">if</span> <span class="pl-s1">add1</span> {
<span class="pl-s1">nums</span> <span class="pl-c1">=</span> <span class="pl-en">append</span>(<span class="pl-s1">nums</span>, <span class="pl-c1">1</span>)
}
<span class="pl-k">if</span> <span class="pl-s1">add2</span> {
<span class="pl-s1">nums</span> <span class="pl-c1">=</span> <span class="pl-en">append</span>(<span class="pl-s1">nums</span>, <span class="pl-c1">2</span>)
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var nums []int
if add1 {
nums = append(nums, 1)
}
if add2 {
nums = append(nums, 2)
}
"><pre><span class="pl-k">var</span> <span class="pl-s1">nums</span> []<span class="pl-smi">int</span>
<span class="pl-k">if</span> <span class="pl-s1">add1</span> {
<span class="pl-s1">nums</span> <span class="pl-c1">=</span> <span class="pl-en">append</span>(<span class="pl-s1">nums</span>, <span class="pl-c1">1</span>)
}
<span class="pl-k">if</span> <span class="pl-s1">add2</span> {
<span class="pl-s1">nums</span> <span class="pl-c1">=</span> <span class="pl-en">append</span>(<span class="pl-s1">nums</span>, <span class="pl-c1">2</span>)
}</pre></div>
</td></tr>
</tbody></table>
</li>
</ul>
<p dir="auto"><code>nil</code> is a valid slice of length 0. This means that,</p>
<ul dir="auto">
<li>
<p dir="auto">You should not return a slice of length zero explicitly. Return <code>nil</code> instead.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="if x == "" {
return []int{}
}"><pre><span class="pl-k">if</span> <span class="pl-s1">x</span> <span class="pl-c1">==</span> <span class="pl-s">""</span> {
<span class="pl-k">return</span> []<span class="pl-smi">int</span>{}
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="if x == "" {
return nil
}"><pre><span class="pl-k">if</span> <span class="pl-s1">x</span> <span class="pl-c1">==</span> <span class="pl-s">""</span> {
<span class="pl-k">return</span> <span class="pl-c1">nil</span>
}</pre></div>
</td></tr>
</tbody></table>
</li>
</ul>
<p dir="auto"><em>Note:</em> in the case of serialization it might make sense to use a zero length initialized slice. For instance the JSON representation of a <em>nil</em> slice is <code>null</code>, however a zero length allocated slice would be translated to <code>[]</code>.</p>
<ul dir="auto">
<li>
<p dir="auto">To check if a slice is empty, always use <code>len(s) == 0</code>. Do not check for <code>nil</code>.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func isEmpty(s []string) bool {
return s == nil
}"><pre><span class="pl-k">func</span> <span class="pl-en">isEmpty</span>(<span class="pl-s1">s</span> []<span class="pl-smi">string</span>) <span class="pl-smi">bool</span> {
<span class="pl-k">return</span> <span class="pl-s1">s</span> <span class="pl-c1">==</span> <span class="pl-c1">nil</span>
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func isEmpty(s []string) bool {
return len(s) == 0
}"><pre><span class="pl-k">func</span> <span class="pl-en">isEmpty</span>(<span class="pl-s1">s</span> []<span class="pl-smi">string</span>) <span class="pl-smi">bool</span> {
<span class="pl-k">return</span> <span class="pl-en">len</span>(<span class="pl-s1">s</span>) <span class="pl-c1">==</span> <span class="pl-c1">0</span>
}</pre></div>
</td></tr>
</tbody></table>
</li>
</ul>
<p dir="auto">Remember that, while it is a valid slice, a <code>nil</code> slice is not equivalent to an allocated slice of length <code>0</code> - one is <code>nil</code> and the other is not - and the two may be treated differently in different situations (such as serialization and comparison).</p>
<h2 dir="auto"><a id="user-content-beware-of-copying-mutexes-in-go" class="anchor" aria-hidden="true" href="#beware-of-copying-mutexes-in-go"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Beware of Copying Mutexes in Go</h2>
<p dir="auto">The <code>sync.Mutex</code> is a value type so copying it is wrong. We're just creating a different mutex, so obviously the exclusion no longer works.</p>
<table>
<thead><tr><th>Bad</th> <th>Good</th></tr></thead>
<tbody>
<tr>
<td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Container struct {
mu sync.Mutex
counters map[string]int
}
func (c Container) inc(name string) { // the value receiver will make a copy of the mutex
c.mu.Lock()
defer c.mu.Unlock()
c.counters[name]++
}"><pre><span class="pl-k">type</span> <span class="pl-smi">Container</span> <span class="pl-k">struct</span> {
<span class="pl-c1">mu</span> sync.<span class="pl-smi">Mutex</span>
<span class="pl-c1">counters</span> <span class="pl-k">map</span>[<span class="pl-smi">string</span>]<span class="pl-smi">int</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">c</span> <span class="pl-smi">Container</span>) <span class="pl-en">inc</span>(<span class="pl-s1">name</span> <span class="pl-smi">string</span>) { <span class="pl-c">// the value receiver will make a copy of the mutex</span>
<span class="pl-s1">c</span>.<span class="pl-c1">mu</span>.<span class="pl-en">Lock</span>()
<span class="pl-k">defer</span> <span class="pl-s1">c</span>.<span class="pl-c1">mu</span>.<span class="pl-en">Unlock</span>()
<span class="pl-s1">c</span>.<span class="pl-c1">counters</span>[<span class="pl-s1">name</span>]<span class="pl-c1">++</span>
}</pre></div>
</td>
<td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Container struct {
my.sync.Mutex
counters map[string]int
}
func (c *Container) inc(name string) {
c.mu.Lock()
defer c.mu.Unlock()
c.counters[name]++
}"><pre><span class="pl-k">type</span> <span class="pl-smi">Container</span> <span class="pl-k">struct</span> {
my.<span class="pl-smi">sync</span>.<span class="pl-smi">Mutex</span>
<span class="pl-c1">counters</span> <span class="pl-k">map</span>[<span class="pl-smi">string</span>]<span class="pl-smi">int</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">c</span> <span class="pl-c1">*</span><span class="pl-smi">Container</span>) <span class="pl-en">inc</span>(<span class="pl-s1">name</span> <span class="pl-smi">string</span>) {
<span class="pl-s1">c</span>.<span class="pl-c1">mu</span>.<span class="pl-en">Lock</span>()
<span class="pl-k">defer</span> <span class="pl-s1">c</span>.<span class="pl-c1">mu</span>.<span class="pl-en">Unlock</span>()
<span class="pl-s1">c</span>.<span class="pl-c1">counters</span>[<span class="pl-s1">name</span>]<span class="pl-c1">++</span>
}</pre></div>
</td>
</tr>
</tbody>
</table>
<h2 dir="auto"><a id="user-content-copy-slices-and-maps-at-boundaries" class="anchor" aria-hidden="true" href="#copy-slices-and-maps-at-boundaries"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Copy Slices and Maps at Boundaries</h2>
<p dir="auto">Slices and maps contain pointers to the underlying data so be wary of scenarios when they need to be copied.</p>
<h3 dir="auto"><a id="user-content-receiving-slices-and-maps" class="anchor" aria-hidden="true" href="#receiving-slices-and-maps"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Receiving Slices and Maps</h3>
<p dir="auto">Keep in mind that users can modify a map or slice you received as an argument if you store a reference to it.</p>
<table>
<thead><tr><th>Bad</th> <th>Good</th></tr></thead>
<tbody>
<tr>
<td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func (d *Driver) SetTrips(trips []Trip) {
d.trips = trips
}
trips := ...
d1.SetTrips(trips)
// Did you mean to modify d1.trips?
trips[0] = ..."><pre><span class="pl-k">func</span> (<span class="pl-s1">d</span> <span class="pl-c1">*</span><span class="pl-smi">Driver</span>) <span class="pl-en">SetTrips</span>(<span class="pl-s1">trips</span> []<span class="pl-smi">Trip</span>) {
<span class="pl-s1">d</span>.<span class="pl-c1">trips</span> <span class="pl-c1">=</span> <span class="pl-s1">trips</span>
}
<span class="pl-s1">trips</span> <span class="pl-c1">:=</span> <span class="pl-c1">...</span>
<span class="pl-s1">d1</span>.<span class="pl-en">SetTrips</span>(<span class="pl-s1">trips</span>)
<span class="pl-c">// Did you mean to modify d1.trips?</span>
<span class="pl-s1">trips</span>[<span class="pl-c1">0</span>] <span class="pl-c1">=</span> <span class="pl-c1">...</span></pre></div>
</td>
<td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func (d *Driver) SetTrips(trips []Trip) {
d.trips = make([]Trip, len(trips))
copy(d.trips, trips)
}
trips := ...
d1.SetTrips(trips)
// We can now modify trips[0] without affecting d1.trips.
trips[0] = ..."><pre><span class="pl-k">func</span> (<span class="pl-s1">d</span> <span class="pl-c1">*</span><span class="pl-smi">Driver</span>) <span class="pl-en">SetTrips</span>(<span class="pl-s1">trips</span> []<span class="pl-smi">Trip</span>) {
<span class="pl-s1">d</span>.<span class="pl-c1">trips</span> <span class="pl-c1">=</span> <span class="pl-en">make</span>([]<span class="pl-smi">Trip</span>, <span class="pl-en">len</span>(<span class="pl-s1">trips</span>))
<span class="pl-en">copy</span>(<span class="pl-s1">d</span>.<span class="pl-c1">trips</span>, <span class="pl-s1">trips</span>)
}
<span class="pl-s1">trips</span> <span class="pl-c1">:=</span> <span class="pl-c1">...</span>
<span class="pl-s1">d1</span>.<span class="pl-en">SetTrips</span>(<span class="pl-s1">trips</span>)
<span class="pl-c">// We can now modify trips[0] without affecting d1.trips.</span>
<span class="pl-s1">trips</span>[<span class="pl-c1">0</span>] <span class="pl-c1">=</span> <span class="pl-c1">...</span></pre></div>
</td>
</tr>
</tbody>
</table>
<h3 dir="auto"><a id="user-content-returning-slices-and-maps" class="anchor" aria-hidden="true" href="#returning-slices-and-maps"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Returning Slices and Maps</h3>
<p dir="auto">Similarly, be wary of user modifications to maps or slices exposing internal state.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Stats struct {
mu sync.Mutex
counters map[string]int
}
// Snapshot returns the current stats.
func (s *Stats) Snapshot() map[string]int {
s.mu.Lock()
defer s.mu.Unlock()
return s.counters
}
// snapshot is no longer protected by the mutex, so any
// access to the snapshot is subject to data races.
snapshot := stats.Snapshot()"><pre><span class="pl-k">type</span> <span class="pl-smi">Stats</span> <span class="pl-k">struct</span> {
<span class="pl-c1">mu</span> sync.<span class="pl-smi">Mutex</span>
<span class="pl-c1">counters</span> <span class="pl-k">map</span>[<span class="pl-smi">string</span>]<span class="pl-smi">int</span>
}
<span class="pl-c">// Snapshot returns the current stats.</span>
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">Stats</span>) <span class="pl-en">Snapshot</span>() <span class="pl-k">map</span>[<span class="pl-smi">string</span>]<span class="pl-smi">int</span> {
<span class="pl-s1">s</span>.<span class="pl-c1">mu</span>.<span class="pl-en">Lock</span>()
<span class="pl-k">defer</span> <span class="pl-s1">s</span>.<span class="pl-c1">mu</span>.<span class="pl-en">Unlock</span>()
<span class="pl-k">return</span> <span class="pl-s1">s</span>.<span class="pl-c1">counters</span>
}
<span class="pl-c">// snapshot is no longer protected by the mutex, so any</span>
<span class="pl-c">// access to the snapshot is subject to data races.</span>
<span class="pl-s1">snapshot</span> <span class="pl-c1">:=</span> <span class="pl-s1">stats</span>.<span class="pl-en">Snapshot</span>()</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Stats struct {
mu sync.Mutex
counters map[string]int
}
func (s *Stats) Snapshot() map[string]int {
s.mu.Lock()
defer s.mu.Unlock()
result := make(map[string]int, len(s.counters))
for k, v := range s.counters {
result[k] = v
}
return result
}
// Snapshot is now a copy.
snapshot := stats.Snapshot()"><pre><span class="pl-k">type</span> <span class="pl-smi">Stats</span> <span class="pl-k">struct</span> {
<span class="pl-c1">mu</span> sync.<span class="pl-smi">Mutex</span>
<span class="pl-c1">counters</span> <span class="pl-k">map</span>[<span class="pl-smi">string</span>]<span class="pl-smi">int</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">Stats</span>) <span class="pl-en">Snapshot</span>() <span class="pl-k">map</span>[<span class="pl-smi">string</span>]<span class="pl-smi">int</span> {
<span class="pl-s1">s</span>.<span class="pl-c1">mu</span>.<span class="pl-en">Lock</span>()
<span class="pl-k">defer</span> <span class="pl-s1">s</span>.<span class="pl-c1">mu</span>.<span class="pl-en">Unlock</span>()
<span class="pl-s1">result</span> <span class="pl-c1">:=</span> <span class="pl-en">make</span>(<span class="pl-k">map</span>[<span class="pl-smi">string</span>]<span class="pl-smi">int</span>, <span class="pl-en">len</span>(<span class="pl-s1">s</span>.<span class="pl-c1">counters</span>))
<span class="pl-k">for</span> <span class="pl-s1">k</span>, <span class="pl-s1">v</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">s</span>.<span class="pl-c1">counters</span> {
<span class="pl-s1">result</span>[<span class="pl-s1">k</span>] <span class="pl-c1">=</span> <span class="pl-s1">v</span>
}
<span class="pl-k">return</span> <span class="pl-s1">result</span>
}
<span class="pl-c">// Snapshot is now a copy.</span>
<span class="pl-s1">snapshot</span> <span class="pl-c1">:=</span> <span class="pl-s1">stats</span>.<span class="pl-en">Snapshot</span>()</pre></div>
</td></tr>
</tbody></table>
<h3 dir="auto"><a id="user-content-filtering-in-place" class="anchor" aria-hidden="true" href="#filtering-in-place"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Filtering in place</h3>
<p dir="auto">This trick uses the fact that a slice shares the same backing array and capacity as the original, so the storage is reused for the filtered slice. Of course, the original contents are modified, so be mindful. It is useful for the code in the 'hot path' where we want to minimize allocation.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var b []rune
for _, x := range a {
if f(x) {
b = append(b, x) // will cause new allocations
}
}
"><pre><span class="pl-k">var</span> <span class="pl-s1">b</span> []<span class="pl-smi">rune</span>
<span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">x</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">a</span> {
<span class="pl-k">if</span> <span class="pl-en">f</span>(<span class="pl-s1">x</span>) {
<span class="pl-s1">b</span> <span class="pl-c1">=</span> <span class="pl-en">append</span>(<span class="pl-s1">b</span>, <span class="pl-s1">x</span>) <span class="pl-c">// will cause new allocations</span>
}
}
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="b := a[:0]
for _, x := range a {
if f(x) {
b = append(b, x) // will reuse the backing array
}
}
// alternatively using index, a bit more verbose
n := 0
for _, x := range a {
if f(x) {
a[n] = x
n++
}
}
a = a[:n]"><pre><span class="pl-s1">b</span> <span class="pl-c1">:=</span> <span class="pl-s1">a</span>[:<span class="pl-c1">0</span>]
<span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">x</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">a</span> {
<span class="pl-k">if</span> <span class="pl-en">f</span>(<span class="pl-s1">x</span>) {
<span class="pl-s1">b</span> <span class="pl-c1">=</span> <span class="pl-en">append</span>(<span class="pl-s1">b</span>, <span class="pl-s1">x</span>) <span class="pl-c">// will reuse the backing array</span>
}
}
<span class="pl-c">// alternatively using index, a bit more verbose</span>
<span class="pl-s1">n</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>
<span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">x</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">a</span> {
<span class="pl-k">if</span> <span class="pl-en">f</span>(<span class="pl-s1">x</span>) {
<span class="pl-s1">a</span>[<span class="pl-s1">n</span>] <span class="pl-c1">=</span> <span class="pl-s1">x</span>
<span class="pl-s1">n</span><span class="pl-c1">++</span>
}
}
<span class="pl-s1">a</span> <span class="pl-c1">=</span> <span class="pl-s1">a</span>[:<span class="pl-s1">n</span>]</pre></div>
</td></tr>
</tbody></table>
<h2 dir="auto"><a id="user-content-pointers-to-interfaces" class="anchor" aria-hidden="true" href="#pointers-to-interfaces"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Pointers to Interfaces</h2>
<p dir="auto">You almost never need a pointer to an interface. You should be passing interfaces as values—the underlying data can still be a pointer.</p>
<h3 dir="auto"><a id="user-content-verify-interface-compliance" class="anchor" aria-hidden="true" href="#verify-interface-compliance"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Verify Interface Compliance</h3>
<p dir="auto">Verify interface compliance at compile time <em>where appropriate</em>. This includes:</p>
<ul dir="auto">
<li>Exported types that are required to implement specific interfaces as part of their API contract</li>
<li>Exported or unexported types that are part of a collection of types implementing the same interface</li>
<li>Other cases where violating an interface would break users</li>
</ul>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Handler struct {
// ...
}
func (h *Handler) ServeHTTP(
w http.ResponseWriter,
r *http.Request,
) {
...
}"><pre><span class="pl-k">type</span> <span class="pl-smi">Handler</span> <span class="pl-k">struct</span> {
<span class="pl-c">// ...</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">h</span> <span class="pl-c1">*</span><span class="pl-smi">Handler</span>) <span class="pl-en">ServeHTTP</span>(
<span class="pl-s1">w</span> http.<span class="pl-smi">ResponseWriter</span>,
<span class="pl-s1">r</span> <span class="pl-c1">*</span>http.<span class="pl-smi">Request</span>,
) {
<span class="pl-c1">...</span>
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Handler struct {
// ...
}
var _ http.Handler = (*Handler)(nil)
func (h *Handler) ServeHTTP(
w http.ResponseWriter,
r *http.Request,
) {
// ...
}"><pre><span class="pl-k">type</span> <span class="pl-smi">Handler</span> <span class="pl-k">struct</span> {
<span class="pl-c">// ...</span>
}
<span class="pl-k">var</span> <span class="pl-s1">_</span> http.<span class="pl-smi">Handler</span> <span class="pl-c1">=</span> (<span class="pl-c1">*</span><span class="pl-s1">Handler</span>)(<span class="pl-c1">nil</span>)
<span class="pl-k">func</span> (<span class="pl-s1">h</span> <span class="pl-c1">*</span><span class="pl-smi">Handler</span>) <span class="pl-en">ServeHTTP</span>(
<span class="pl-s1">w</span> http.<span class="pl-smi">ResponseWriter</span>,
<span class="pl-s1">r</span> <span class="pl-c1">*</span>http.<span class="pl-smi">Request</span>,
) {
<span class="pl-c">// ...</span>
}</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">The statement <code>var _ http.Handler = (*Handler)(nil)</code> will fail to compile if <code>*Handler</code> ever stops matching the <code>http.Handler</code> interface.</p>
<p dir="auto">The right hand side of the assignment should be the zero value of the asserted type. This is <code>nil</code> for pointer types (like <code>*Handler</code>), slices, and maps, and an empty struct for struct types.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type LogHandler struct {
h http.Handler
log *zap.Logger
}
var _ http.Handler = LogHandler{}
func (h LogHandler) ServeHTTP(
w http.ResponseWriter,
r *http.Request,
) {
// ...
}"><pre><span class="pl-k">type</span> <span class="pl-smi">LogHandler</span> <span class="pl-k">struct</span> {
<span class="pl-c1">h</span> http.<span class="pl-smi">Handler</span>
<span class="pl-c1">log</span> <span class="pl-c1">*</span>zap.<span class="pl-smi">Logger</span>
}
<span class="pl-k">var</span> <span class="pl-s1">_</span> http.<span class="pl-smi">Handler</span> <span class="pl-c1">=</span> <span class="pl-smi">LogHandler</span>{}
<span class="pl-k">func</span> (<span class="pl-s1">h</span> <span class="pl-smi">LogHandler</span>) <span class="pl-en">ServeHTTP</span>(
<span class="pl-s1">w</span> http.<span class="pl-smi">ResponseWriter</span>,
<span class="pl-s1">r</span> <span class="pl-c1">*</span>http.<span class="pl-smi">Request</span>,
) {
<span class="pl-c">// ...</span>
}</pre></div>
<h2 dir="auto"><a id="user-content-receivers-and-interfaces" class="anchor" aria-hidden="true" href="#receivers-and-interfaces"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Receivers and Interfaces</h2>
<p dir="auto">Methods with value receivers can be called on pointers as well as values.
Methods with pointer receivers can only be called on pointers or <a href="https://golang.org/ref/spec#Method_values" rel="nofollow">addressable values</a>.</p>
<p dir="auto">For example,</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type S struct {
data string
}
func (s S) Read() string {
return s.data
}
func (s *S) Write(str string) {
s.data = str
}
sVals := map[int]S{1: {"A"}}
// You can only call Read using a value
sVals[1].Read()
// This will not compile:
// sVals[1].Write("test")
sPtrs := map[int]*S{1: {"A"}}
// You can call both Read and Write using a pointer
sPtrs[1].Read()
sPtrs[1].Write("test")"><pre><span class="pl-k">type</span> <span class="pl-smi">S</span> <span class="pl-k">struct</span> {
<span class="pl-c1">data</span> <span class="pl-smi">string</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-smi">S</span>) <span class="pl-en">Read</span>() <span class="pl-smi">string</span> {
<span class="pl-k">return</span> <span class="pl-s1">s</span>.<span class="pl-c1">data</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">S</span>) <span class="pl-en">Write</span>(<span class="pl-s1">str</span> <span class="pl-smi">string</span>) {
<span class="pl-s1">s</span>.<span class="pl-c1">data</span> <span class="pl-c1">=</span> <span class="pl-s1">str</span>
}
<span class="pl-s1">sVals</span> <span class="pl-c1">:=</span> <span class="pl-k">map</span>[<span class="pl-smi">int</span>]<span class="pl-smi">S</span>{<span class="pl-c1">1</span>: {<span class="pl-s">"A"</span>}}
<span class="pl-c">// You can only call Read using a value</span>
<span class="pl-s1">sVals</span>[<span class="pl-c1">1</span>].<span class="pl-en">Read</span>()
<span class="pl-c">// This will not compile:</span>
<span class="pl-c">// sVals[1].Write("test")</span>
<span class="pl-s1">sPtrs</span> <span class="pl-c1">:=</span> <span class="pl-k">map</span>[<span class="pl-smi">int</span>]<span class="pl-c1">*</span><span class="pl-smi">S</span>{<span class="pl-c1">1</span>: {<span class="pl-s">"A"</span>}}
<span class="pl-c">// You can call both Read and Write using a pointer</span>
<span class="pl-s1">sPtrs</span>[<span class="pl-c1">1</span>].<span class="pl-en">Read</span>()
<span class="pl-s1">sPtrs</span>[<span class="pl-c1">1</span>].<span class="pl-en">Write</span>(<span class="pl-s">"test"</span>)</pre></div>
<p dir="auto">Similarly, an interface can be satisfied by a pointer, even if the method has a value receiver.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type F interface {
f()
}
type S1 struct{}
func (s S1) f() {}
type S2 struct{}
func (s *S2) f() {}
s1Val := S1{}
s1Ptr := &S1{}
s2Val := S2{}
s2Ptr := &S2{}
var i F
i = s1Val
i = s1Ptr
i = s2Ptr
// The following doesn't compile, since s2Val is a value, and there is no value receiver for f.
// i = s2Val"><pre><span class="pl-k">type</span> <span class="pl-smi">F</span> <span class="pl-k">interface</span> {
<span class="pl-c1">f</span>()
}
<span class="pl-k">type</span> <span class="pl-smi">S1</span> <span class="pl-k">struct</span>{}
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-smi">S1</span>) <span class="pl-en">f</span>() {}
<span class="pl-k">type</span> <span class="pl-smi">S2</span> <span class="pl-k">struct</span>{}
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">S2</span>) <span class="pl-en">f</span>() {}
<span class="pl-s1">s1Val</span> <span class="pl-c1">:=</span> <span class="pl-smi">S1</span>{}
<span class="pl-s1">s1Ptr</span> <span class="pl-c1">:=</span> <span class="pl-c1">&</span><span class="pl-smi">S1</span>{}
<span class="pl-s1">s2Val</span> <span class="pl-c1">:=</span> <span class="pl-smi">S2</span>{}
<span class="pl-s1">s2Ptr</span> <span class="pl-c1">:=</span> <span class="pl-c1">&</span><span class="pl-smi">S2</span>{}
<span class="pl-k">var</span> <span class="pl-s1">i</span> <span class="pl-smi">F</span>
<span class="pl-s1">i</span> <span class="pl-c1">=</span> <span class="pl-s1">s1Val</span>
<span class="pl-s1">i</span> <span class="pl-c1">=</span> <span class="pl-s1">s1Ptr</span>
<span class="pl-s1">i</span> <span class="pl-c1">=</span> <span class="pl-s1">s2Ptr</span>
<span class="pl-c">// The following doesn't compile, since s2Val is a value, and there is no value receiver for f.</span>
<span class="pl-c">// i = s2Val</span></pre></div>
<p dir="auto">Effective Go has a good write up on <a href="https://golang.org/doc/effective_go.html#pointers_vs_values" rel="nofollow">Pointers vs. Values</a>.</p>
<h2 dir="auto"><a id="user-content-defer-to-clean-up" class="anchor" aria-hidden="true" href="#defer-to-clean-up"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Defer to Clean Up</h2>
<p dir="auto">Use defer to clean up resources such as files and locks.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="p.Lock()
if p.count < 10 {
p.Unlock()
return p.count
}
p.count++
newCount := p.count
p.Unlock()
return newCount
// easy to miss unlocks due to multiple returns"><pre><span class="pl-s1">p</span>.<span class="pl-en">Lock</span>()
<span class="pl-k">if</span> <span class="pl-s1">p</span>.<span class="pl-c1">count</span> <span class="pl-c1"><</span> <span class="pl-c1">10</span> {
<span class="pl-s1">p</span>.<span class="pl-en">Unlock</span>()
<span class="pl-k">return</span> <span class="pl-s1">p</span>.<span class="pl-c1">count</span>
}
<span class="pl-s1">p</span>.<span class="pl-c1">count</span><span class="pl-c1">++</span>
<span class="pl-s1">newCount</span> <span class="pl-c1">:=</span> <span class="pl-s1">p</span>.<span class="pl-c1">count</span>
<span class="pl-s1">p</span>.<span class="pl-en">Unlock</span>()
<span class="pl-k">return</span> <span class="pl-s1">newCount</span>
<span class="pl-c">// easy to miss unlocks due to multiple returns</span></pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="p.Lock()
defer p.Unlock()
if p.count < 10 {
return p.count
}
p.count++
return p.count
// more readable"><pre><span class="pl-s1">p</span>.<span class="pl-en">Lock</span>()
<span class="pl-k">defer</span> <span class="pl-s1">p</span>.<span class="pl-en">Unlock</span>()
<span class="pl-k">if</span> <span class="pl-s1">p</span>.<span class="pl-c1">count</span> <span class="pl-c1"><</span> <span class="pl-c1">10</span> {
<span class="pl-k">return</span> <span class="pl-s1">p</span>.<span class="pl-c1">count</span>
}
<span class="pl-s1">p</span>.<span class="pl-c1">count</span><span class="pl-c1">++</span>
<span class="pl-k">return</span> <span class="pl-s1">p</span>.<span class="pl-c1">count</span>
<span class="pl-c">// more readable</span></pre></div>
</td></tr>
</tbody></table>
<p dir="auto">Defer has an extremely small overhead and should be avoided only if you can prove that your function execution time is in the order of nanoseconds. The readability win of using defers is worth the miniscule cost of using them. This is especially true for larger methods that have more than simple memory accesses, where the other computations are more significant than the <code>defer</code>.</p>
<h2 dir="auto"><a id="user-content-channel-size-is-one-or-none" class="anchor" aria-hidden="true" href="#channel-size-is-one-or-none"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Channel Size is One or None</h2>
<p dir="auto">Channels should usually have a size of one or be unbuffered. By default, channels are unbuffered and have a size of zero (blocking behaviour). Any other size must be subject to a high level of scrutiny. Consider how the size is determined, what prevents the channel from filling up under load and blocking writers, and what happens when this occurs.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// Ought to be enough for anybody!
c := make(chan int, 64)
"><pre><span class="pl-c">// Ought to be enough for anybody!</span>
<span class="pl-s1">c</span> <span class="pl-c1">:=</span> <span class="pl-en">make</span>(<span class="pl-k">chan</span> <span class="pl-smi">int</span>, <span class="pl-c1">64</span>)
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// Size of one
c := make(chan int, 1) // or
// Unbuffered channel, size of zero
c := make(chan int)"><pre><span class="pl-c">// Size of one</span>
<span class="pl-s1">c</span> <span class="pl-c1">:=</span> <span class="pl-en">make</span>(<span class="pl-k">chan</span> <span class="pl-smi">int</span>, <span class="pl-c1">1</span>) <span class="pl-c">// or</span>
<span class="pl-c">// Unbuffered channel, size of zero</span>
<span class="pl-s1">c</span> <span class="pl-c1">:=</span> <span class="pl-en">make</span>(<span class="pl-k">chan</span> <span class="pl-smi">int</span>)</pre></div>
</td></tr>
</tbody></table>
<p dir="auto"><em>Note:</em> in some places we use buffered channels to implement a semaphore as described <a href="https://robreid.io/stupid-channel-tricks-p2-semaphores/" rel="nofollow">here</a></p>
<h2 dir="auto"><a id="user-content-start-enums-at-one" class="anchor" aria-hidden="true" href="#start-enums-at-one"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Start Enums at One</h2>
<p dir="auto">The standard way of introducing enumerations in Go is to declare a custom type and a <code>const</code> group with <code>iota</code>. Since variables have a 0 default value, you should usually start your enums on a non-zero value.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Operation int
const (
Add Operation = iota
Subtract
Multiply
)
// Add=0, Subtract=1, Multiply=2"><pre><span class="pl-k">type</span> <span class="pl-smi">Operation</span> <span class="pl-smi">int</span>
<span class="pl-k">const</span> (
<span class="pl-s1">Add</span> <span class="pl-smi">Operation</span> <span class="pl-c1">=</span> <span class="pl-s1">iota</span>
<span class="pl-s1">Subtract</span>
<span class="pl-s1">Multiply</span>
)
<span class="pl-c">// Add=0, Subtract=1, Multiply=2</span></pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Operation int
const (
Add Operation = iota + 1
Subtract
Multiply
)
// Add=1, Subtract=2, Multiply=3"><pre><span class="pl-k">type</span> <span class="pl-smi">Operation</span> <span class="pl-smi">int</span>
<span class="pl-k">const</span> (
<span class="pl-s1">Add</span> <span class="pl-smi">Operation</span> <span class="pl-c1">=</span> <span class="pl-s1">iota</span> <span class="pl-c1">+</span> <span class="pl-c1">1</span>
<span class="pl-s1">Subtract</span>
<span class="pl-s1">Multiply</span>
)
<span class="pl-c">// Add=1, Subtract=2, Multiply=3</span></pre></div>
</td></tr>
</tbody></table>
<p dir="auto"><em>Note:</em> There are cases where using the zero value makes sense, for example when the zero value case is the desirable default behavior.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type LogOutput int
const (
LogToStdout LogOutput = iota
LogToFile
LogToRemote
)
// LogToStdout=0, LogToFile=1, LogToRemote=2"><pre><span class="pl-k">type</span> <span class="pl-smi">LogOutput</span> <span class="pl-smi">int</span>
<span class="pl-k">const</span> (
<span class="pl-s1">LogToStdout</span> <span class="pl-smi">LogOutput</span> <span class="pl-c1">=</span> <span class="pl-s1">iota</span>
<span class="pl-s1">LogToFile</span>
<span class="pl-s1">LogToRemote</span>
)
<span class="pl-c">// LogToStdout=0, LogToFile=1, LogToRemote=2</span></pre></div>
<h2 dir="auto"><a id="user-content-use-time-to-handle-time" class="anchor" aria-hidden="true" href="#use-time-to-handle-time"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Use <code>"time"</code> to handle time</h2>
<p dir="auto">Time is complicated. Incorrect assumptions often made about time include the following.</p>
<ol dir="auto">
<li>A day has 24 hours</li>
<li>An hour has 60 minutes</li>
<li>A week has 7 days</li>
<li>A year has 365 days</li>
<li><a href="https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time" rel="nofollow">And a lot more</a></li>
</ol>
<p dir="auto">For example, <em>1</em> means that adding 24 hours to a time instant will not always yield a new calendar day.</p>
<p dir="auto">Therefore, always use the <a href="https://golang.org/pkg/time/" rel="nofollow"><code>"time"</code></a> package when dealing with time because it helps deal with these incorrect assumptions in a safer, more accurate manner.</p>
<h3 dir="auto"><a id="user-content-use-timetime-for-instants-of-time" class="anchor" aria-hidden="true" href="#use-timetime-for-instants-of-time"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Use <code>time.Time</code> for instants of time</h3>
<p dir="auto">Use <a href="https://golang.org/pkg/time/#Time" rel="nofollow"><code>time.Time</code></a> when dealing with instants of time, and the methods on <code>time.Time</code> when comparing, adding, or subtracting time.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func isActive(now, start, stop int) bool {
return start <= now && now < stop
}"><pre><span class="pl-k">func</span> <span class="pl-en">isActive</span>(<span class="pl-s1">now</span>, <span class="pl-s1">start</span>, <span class="pl-s1">stop</span> <span class="pl-smi">int</span>) <span class="pl-smi">bool</span> {
<span class="pl-k">return</span> <span class="pl-s1">start</span> <span class="pl-c1"><=</span> <span class="pl-s1">now</span> <span class="pl-c1">&&</span> <span class="pl-s1">now</span> <span class="pl-c1"><</span> <span class="pl-s1">stop</span>
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func isActive(now, start, stop time.Time) bool {
return (start.Before(now) || start.Equal(now)) && now.Before(stop)
}"><pre><span class="pl-k">func</span> <span class="pl-en">isActive</span>(<span class="pl-s1">now</span>, <span class="pl-s1">start</span>, <span class="pl-s1">stop</span> time.<span class="pl-smi">Time</span>) <span class="pl-smi">bool</span> {
<span class="pl-k">return</span> (<span class="pl-s1">start</span>.<span class="pl-en">Before</span>(<span class="pl-s1">now</span>) <span class="pl-c1">||</span> <span class="pl-s1">start</span>.<span class="pl-en">Equal</span>(<span class="pl-s1">now</span>)) <span class="pl-c1">&&</span> <span class="pl-s1">now</span>.<span class="pl-en">Before</span>(<span class="pl-s1">stop</span>)
}</pre></div>
</td></tr>
</tbody></table>
<h3 dir="auto"><a id="user-content-use-timeduration-for-periods-of-time" class="anchor" aria-hidden="true" href="#use-timeduration-for-periods-of-time"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Use <code>time.Duration</code> for periods of time</h3>
<p dir="auto">Use <a href="https://golang.org/pkg/time/#Duration" rel="nofollow"><code>time.Duration</code></a> when dealing with periods of time.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func poll(delay int) {
for {
// ...
time.Sleep(time.Duration(delay) * time.Millisecond)
}
}
poll(10) // was it seconds or milliseconds?"><pre><span class="pl-k">func</span> <span class="pl-en">poll</span>(<span class="pl-s1">delay</span> <span class="pl-smi">int</span>) {
<span class="pl-k">for</span> {
<span class="pl-c">// ...</span>
<span class="pl-s1">time</span>.<span class="pl-en">Sleep</span>(<span class="pl-s1">time</span>.<span class="pl-en">Duration</span>(<span class="pl-s1">delay</span>) <span class="pl-c1">*</span> <span class="pl-s1">time</span>.<span class="pl-c1">Millisecond</span>)
}
}
<span class="pl-en">poll</span>(<span class="pl-c1">10</span>) <span class="pl-c">// was it seconds or milliseconds?</span></pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func poll(delay time.Duration) {
for {
// ...
time.Sleep(delay)
}
}
poll(10*time.Second)"><pre><span class="pl-k">func</span> <span class="pl-en">poll</span>(<span class="pl-s1">delay</span> time.<span class="pl-smi">Duration</span>) {
<span class="pl-k">for</span> {
<span class="pl-c">// ...</span>
<span class="pl-s1">time</span>.<span class="pl-en">Sleep</span>(<span class="pl-s1">delay</span>)
}
}
<span class="pl-en">poll</span>(<span class="pl-c1">10</span><span class="pl-c1">*</span><span class="pl-s1">time</span>.<span class="pl-c1">Second</span>)</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">Going back to the example of adding 24 hours to a time instant, the method we use to add time depends on intent. If we want the same time of the day, but on the next calendar day, we should use <a href="https://golang.org/pkg/time/#Time.AddDate" rel="nofollow"><code>Time.AddDate</code></a>. However, if we want an instant of time guaranteed to be 24 hours after the previous time, we should use <a href="https://golang.org/pkg/time/#Time.Add" rel="nofollow"><code>Time.Add</code></a>.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="newDay := t.AddDate(0 /* years */, 0 /* months */, 1 /* days */)
maybeNewDay := t.Add(24 * time.Hour)"><pre><span class="pl-s1">newDay</span> <span class="pl-c1">:=</span> <span class="pl-s1">t</span>.<span class="pl-en">AddDate</span>(<span class="pl-c1">0</span> <span class="pl-c">/* years */</span>, <span class="pl-c1">0</span> <span class="pl-c">/* months */</span>, <span class="pl-c1">1</span> <span class="pl-c">/* days */</span>)
<span class="pl-s1">maybeNewDay</span> <span class="pl-c1">:=</span> <span class="pl-s1">t</span>.<span class="pl-en">Add</span>(<span class="pl-c1">24</span> <span class="pl-c1">*</span> <span class="pl-s1">time</span>.<span class="pl-c1">Hour</span>)</pre></div>
<h3 dir="auto"><a id="user-content-use-timetime-and-timeduration-with-external-systems" class="anchor" aria-hidden="true" href="#use-timetime-and-timeduration-with-external-systems"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Use <code>time.Time</code> and <code>time.Duration</code> with external systems</h3>
<p dir="auto">Use <code>time.Duration</code> and <code>time.Time</code> in interactions with external systems when possible. For example:</p>
<ul dir="auto">
<li>Command-line flags: <a href="https://golang.org/pkg/flag/" rel="nofollow"><code>flag</code></a> supports <code>time.Duration</code> via <a href="https://golang.org/pkg/time/#ParseDuration" rel="nofollow"><code>time.ParseDuration</code></a></li>
<li>JSON: <a href="https://golang.org/pkg/encoding/json/" rel="nofollow"><code>encoding/json</code></a> supports encoding <code>time.Time</code> as an <a href="https://tools.ietf.org/html/rfc3339" rel="nofollow">RFC 3339</a> string via its <a href="https://golang.org/pkg/time/#Time.UnmarshalJSON" rel="nofollow"><code>UnmarshalJSON</code> method</a></li>
<li>SQL: <a href="https://golang.org/pkg/database/sql/" rel="nofollow"><code>database/sql</code></a> supports converting <code>DATETIME</code> or <code>TIMESTAMP</code> columns into <code>time.Time</code> and back if the underlying driver supports it</li>
<li>YAML: <a href="https://godoc.org/gopkg.in/yaml.v2" rel="nofollow"><code>gopkg.in/yaml.v2</code></a> supports <code>time.Time</code> as an <a href="https://tools.ietf.org/html/rfc3339" rel="nofollow">RFC 3339</a> string, and <code>time.Duration</code> via <a href="https://golang.org/pkg/time/#ParseDuration" rel="nofollow"><code>time.ParseDuration</code></a>.</li>
</ul>
<p dir="auto">When it is not possible to use <code>time.Duration</code> in these interactions, use <code>int</code> or <code>float64</code> and include the unit in the name of the field. For example, since <code>encoding/json</code> does not support <code>time.Duration</code>, the unit is included in the name of the field.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// {"interval": 2}
type Config struct {
Interval int `json:"interval"`
}"><pre><span class="pl-c">// {"interval": 2}</span>
<span class="pl-k">type</span> <span class="pl-smi">Config</span> <span class="pl-k">struct</span> {
<span class="pl-c1">Interval</span> <span class="pl-smi">int</span> <span class="pl-s">`json:"interval"`</span>
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// {"intervalMillis": 2000}
type Config struct {
IntervalMillis int `json:"intervalMillis"`
}"><pre><span class="pl-c">// {"intervalMillis": 2000}</span>
<span class="pl-k">type</span> <span class="pl-smi">Config</span> <span class="pl-k">struct</span> {
<span class="pl-c1">IntervalMillis</span> <span class="pl-smi">int</span> <span class="pl-s">`json:"intervalMillis"`</span>
}</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">When it is not possible to use <code>time.Time</code> in these interactions, unless an alternative is agreed upon, use <code>string</code> and format timestamps as defined in <a href="https://tools.ietf.org/html/rfc3339" rel="nofollow">RFC 3339</a>. This format is used by default by [<code>Time.nmarshalText</code>] and is available for use in <code>Time.Format</code> and <code>time.Parse</code> via <a href="https://golang.org/pkg/time/#RFC3339" rel="nofollow"><code>time.RFC3339</code></a>.</p>
<p dir="auto">Although this tends to not be a problem in practice, keep in mind that the <code>"time"</code> package does not support parsing timestamps with leap seconds (<a href="https://github.com/golang/go/issues/8728" data-hovercard-type="issue" data-hovercard-url="/golang/go/issues/8728/hovercard">8728</a>), nor does it account for leap seconds in calculations (<a href="https://github.com/golang/go/issues/15190" data-hovercard-type="issue" data-hovercard-url="/golang/go/issues/15190/hovercard">15190</a>). If you compare two instants of time, the difference will not include the leap seconds that may have occurred between those two instants.</p>
<h2 dir="auto"><a id="user-content-error-types" class="anchor" aria-hidden="true" href="#error-types"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Error Types</h2>
<p dir="auto">There are various options for declaring errors:</p>
<ul dir="auto">
<li><a href="https://golang.org/pkg/errors/#New" rel="nofollow"><code>errors.New</code></a> for errors with simple static strings</li>
<li><a href="https://golang.org/pkg/fmt/#Errorf" rel="nofollow"><code>fmt.Errorf</code></a> for formatted error strings</li>
<li>Custom types that implement an <code>Error()</code> method</li>
<li>Wrapped errors using <a href="https://godoc.org/github.com/pkg/errors#Wrap" rel="nofollow"><code>"pkg/errors".Wrap</code></a></li>
</ul>
<p dir="auto">When returning errors, consider the following to determine the best choice:</p>
<ul dir="auto">
<li>Is this a simple error that needs no extra information? If so, <a href="https://golang.org/pkg/errors/#New" rel="nofollow"><code>errors.New</code></a> should suffice.</li>
<li>Do the clients need to detect and handle this error? If so, you should use a custom type, and implement the <code>Error()</code> method.</li>
<li>Are you propagating an error returned by a downstream function? If so, check the <a href="#error-wrapping">section on error wrapping</a>.</li>
<li>Otherwise, <a href="https://golang.org/pkg/fmt/#Errorf" rel="nofollow"><code>fmt.Errorf</code></a> is okay.</li>
</ul>
<p dir="auto">If the client needs to detect the error, and you have created a simple error
using <a href="https://golang.org/pkg/errors/#New" rel="nofollow"><code>errors.New</code></a>, use a var for the error.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// package foo
func Open() error {
return errors.New("could not open")
}
// package bar
func use() {
if err := foo.Open(); err != nil {
if err.Error() == "could not open" {
// handle
} else {
panic("unknown error")
}
}
}"><pre><span class="pl-c">// package foo</span>
<span class="pl-k">func</span> <span class="pl-en">Open</span>() <span class="pl-smi">error</span> {
<span class="pl-k">return</span> <span class="pl-s1">errors</span>.<span class="pl-en">New</span>(<span class="pl-s">"could not open"</span>)
}
<span class="pl-c">// package bar</span>
<span class="pl-k">func</span> <span class="pl-en">use</span>() {
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">foo</span>.<span class="pl-en">Open</span>(); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">if</span> <span class="pl-s1">err</span>.<span class="pl-en">Error</span>() <span class="pl-c1">==</span> <span class="pl-s">"could not open"</span> {
<span class="pl-c">// handle</span>
} <span class="pl-k">else</span> {
<span class="pl-en">panic</span>(<span class="pl-s">"unknown error"</span>)
}
}
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// package foo
var ErrCouldNotOpen = errors.New("could not open")
func Open() error {
return ErrCouldNotOpen
}
// package bar
if err := foo.Open(); err != nil {
if errors.Is(err, foo.ErrCouldNotOpen) {
// handle
} else {
panic("unknown error")
}
}"><pre><span class="pl-c">// package foo</span>
<span class="pl-k">var</span> <span class="pl-s1">ErrCouldNotOpen</span> <span class="pl-c1">=</span> <span class="pl-s1">errors</span>.<span class="pl-en">New</span>(<span class="pl-s">"could not open"</span>)
<span class="pl-k">func</span> <span class="pl-en">Open</span>() <span class="pl-smi">error</span> {
<span class="pl-k">return</span> <span class="pl-s1">ErrCouldNotOpen</span>
}
<span class="pl-c">// package bar</span>
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">foo</span>.<span class="pl-en">Open</span>(); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">if</span> <span class="pl-s1">errors</span>.<span class="pl-en">Is</span>(<span class="pl-s1">err</span>, <span class="pl-s1">foo</span>.<span class="pl-c1">ErrCouldNotOpen</span>) {
<span class="pl-c">// handle</span>
} <span class="pl-k">else</span> {
<span class="pl-en">panic</span>(<span class="pl-s">"unknown error"</span>)
}
}</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">If you have an error that clients may need to detect, and you would like to add more information to it (e.g., it is not a static string), then you should use a custom type.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func open(file string) error {
return fmt.Errorf("file %q not found", file)
}
func use() {
if err := open("testfile.txt"); err != nil {
if strings.Contains(err.Error(), "not found") {
// handle
} else {
panic("unknown error")
}
}
}
"><pre><span class="pl-k">func</span> <span class="pl-en">open</span>(<span class="pl-s1">file</span> <span class="pl-smi">string</span>) <span class="pl-smi">error</span> {
<span class="pl-k">return</span> <span class="pl-s1">fmt</span>.<span class="pl-en">Errorf</span>(<span class="pl-s">"file %q not found"</span>, <span class="pl-s1">file</span>)
}
<span class="pl-k">func</span> <span class="pl-en">use</span>() {
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-en">open</span>(<span class="pl-s">"testfile.txt"</span>); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">if</span> <span class="pl-s1">strings</span>.<span class="pl-en">Contains</span>(<span class="pl-s1">err</span>.<span class="pl-en">Error</span>(), <span class="pl-s">"not found"</span>) {
<span class="pl-c">// handle</span>
} <span class="pl-k">else</span> {
<span class="pl-en">panic</span>(<span class="pl-s">"unknown error"</span>)
}
}
}
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type errNotFound struct {
file string
}
func (e errNotFound) Error() string {
return fmt.Sprintf("file %q not found", e.file)
}
func open(file string) error {
return errNotFound{file: file}
}
func use() {
if err := open("testfile.txt"); err != nil {
if _, ok := err.(errNotFound); ok {
// handle
} else {
panic("unknown error")
}
}
}"><pre><span class="pl-k">type</span> <span class="pl-smi">errNotFound</span> <span class="pl-k">struct</span> {
<span class="pl-c1">file</span> <span class="pl-smi">string</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">e</span> <span class="pl-smi">errNotFound</span>) <span class="pl-en">Error</span>() <span class="pl-smi">string</span> {
<span class="pl-k">return</span> <span class="pl-s1">fmt</span>.<span class="pl-en">Sprintf</span>(<span class="pl-s">"file %q not found"</span>, <span class="pl-s1">e</span>.<span class="pl-c1">file</span>)
}
<span class="pl-k">func</span> <span class="pl-en">open</span>(<span class="pl-s1">file</span> <span class="pl-smi">string</span>) <span class="pl-smi">error</span> {
<span class="pl-k">return</span> <span class="pl-smi">errNotFound</span>{<span class="pl-c1">file</span>: <span class="pl-s1">file</span>}
}
<span class="pl-k">func</span> <span class="pl-en">use</span>() {
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-en">open</span>(<span class="pl-s">"testfile.txt"</span>); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">if</span> <span class="pl-s1">_</span>, <span class="pl-s1">ok</span> <span class="pl-c1">:=</span> <span class="pl-s1">err</span>.(<span class="pl-smi">errNotFound</span>); <span class="pl-s1">ok</span> {
<span class="pl-c">// handle</span>
} <span class="pl-k">else</span> {
<span class="pl-en">panic</span>(<span class="pl-s">"unknown error"</span>)
}
}
}</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">Be careful with exporting custom error types directly since they become part of the public API of the package. It is preferable to expose matcher functions to check the error instead.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// package foo
type errNotFound struct {
file string
}
func (e errNotFound) Error() string {
return fmt.Sprintf("file %q not found", e.file)
}
func IsNotFoundError(err error) bool {
return errors.Is(err, errNotFound)
}
func Open(file string) error {
return errNotFound{file: file}
}
// package bar
if err := foo.Open("foo"); err != nil {
if foo.IsNotFoundError(err) {
// handle
} else {
panic("unknown error")
}
}"><pre><span class="pl-c">// package foo</span>
<span class="pl-k">type</span> <span class="pl-smi">errNotFound</span> <span class="pl-k">struct</span> {
<span class="pl-c1">file</span> <span class="pl-smi">string</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">e</span> <span class="pl-smi">errNotFound</span>) <span class="pl-en">Error</span>() <span class="pl-smi">string</span> {
<span class="pl-k">return</span> <span class="pl-s1">fmt</span>.<span class="pl-en">Sprintf</span>(<span class="pl-s">"file %q not found"</span>, <span class="pl-s1">e</span>.<span class="pl-c1">file</span>)
}
<span class="pl-k">func</span> <span class="pl-en">IsNotFoundError</span>(<span class="pl-s1">err</span> <span class="pl-smi">error</span>) <span class="pl-smi">bool</span> {
<span class="pl-k">return</span> <span class="pl-s1">errors</span>.<span class="pl-en">Is</span>(<span class="pl-s1">err</span>, <span class="pl-s1">errNotFound</span>)
}
<span class="pl-k">func</span> <span class="pl-en">Open</span>(<span class="pl-s1">file</span> <span class="pl-smi">string</span>) <span class="pl-smi">error</span> {
<span class="pl-k">return</span> <span class="pl-smi">errNotFound</span>{<span class="pl-c1">file</span>: <span class="pl-s1">file</span>}
}
<span class="pl-c">// package bar</span>
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">foo</span>.<span class="pl-en">Open</span>(<span class="pl-s">"foo"</span>); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">if</span> <span class="pl-s1">foo</span>.<span class="pl-en">IsNotFoundError</span>(<span class="pl-s1">err</span>) {
<span class="pl-c">// handle</span>
} <span class="pl-k">else</span> {
<span class="pl-en">panic</span>(<span class="pl-s">"unknown error"</span>)
}
}</pre></div>
<h3 dir="auto"><a id="user-content-error-wrapping" class="anchor" aria-hidden="true" href="#error-wrapping"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Error Wrapping</h3>
<p dir="auto">There are three main options for propagating errors if a call fails:</p>
<ul dir="auto">
<li>Return the original error if there is no additional context to add and you want to maintain the original error type.</li>
<li>Use <a href="https://golang.org/pkg/fmt/#Errorf" rel="nofollow"><code>fmt.Errorf</code></a> if the callers do not need to detect or handle that specific error case.</li>
<li>Use a custom type where you can detail the failure reason.</li>
</ul>
<p dir="auto">It is recommended to add context where possible so that instead of a vague error such as "connection refused", you get more useful errors such as "call service foo: connection refused". When adding context to returned errors, keep the context succinct by avoiding phrases like "failed to", which state the obvious and pile up as the error percolates up through the stack:</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="s, err := store.New()
if err != nil {
return fmt.Errorf("failed to create new store: %v", err)
}"><pre><span class="pl-s1">s</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">store</span>.<span class="pl-en">New</span>()
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">return</span> <span class="pl-s1">fmt</span>.<span class="pl-en">Errorf</span>(<span class="pl-s">"failed to create new store: %v"</span>, <span class="pl-s1">err</span>)
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="s, err := store.New()
if err != nil {
return fmt.Errorf("new store: %v", err)
}"><pre><span class="pl-s1">s</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">store</span>.<span class="pl-en">New</span>()
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">return</span> <span class="pl-s1">fmt</span>.<span class="pl-en">Errorf</span>(<span class="pl-s">"new store: %v"</span>, <span class="pl-s1">err</span>)
}</pre></div>
</td></tr><tr><td>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="failed to x: failed to y: failed to create new store: the error"><pre class="notranslate"><code>failed to x: failed to y: failed to create new store: the error
</code></pre></div>
</td><td>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="x: y: new store: the error
"><pre class="notranslate"><code>x: y: new store: the error
</code></pre></div>
</td></tr>
</tbody></table>
<p dir="auto">However once the error is sent to another system, it should be clear the message is an error (e.g. an <code>err</code> tag or "Failed" prefix in logs).</p>
<p dir="auto">See also <a href="https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully" rel="nofollow">Don't just check errors, handle them gracefully</a>.</p>
<h3 dir="auto"><a id="user-content-handle-type-assertion-failures" class="anchor" aria-hidden="true" href="#handle-type-assertion-failures"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Handle Type Assertion Failures</h3>
<p dir="auto">The single return value form of a <a href="https://golang.org/ref/spec#Type_assertions" rel="nofollow">type assertion</a> will panic on an incorrect type. Therefore, always use the "comma ok" idiom.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="t := i.(string)
"><pre><span class="pl-s1">t</span> <span class="pl-c1">:=</span> <span class="pl-s1">i</span>.(<span class="pl-smi">string</span>)
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="t, ok := i.(string)
if !ok {
// handle the error gracefully
}"><pre><span class="pl-s1">t</span>, <span class="pl-s1">ok</span> <span class="pl-c1">:=</span> <span class="pl-s1">i</span>.(<span class="pl-smi">string</span>)
<span class="pl-k">if</span> <span class="pl-c1">!</span><span class="pl-s1">ok</span> {
<span class="pl-c">// handle the error gracefully</span>
}</pre></div>
</td></tr>
</tbody></table>
<h3 dir="auto"><a id="user-content-panic-with-care" class="anchor" aria-hidden="true" href="#panic-with-care"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Panic with care</h3>
<p dir="auto">Code running in production must avoid panics. Panics are a major source of <a href="https://en.wikipedia.org/wiki/Cascading_failure" rel="nofollow">cascading failures</a>. If an error occurs, the function must return an error and allow the caller to decide how to handle it.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func run(args []string) {
if len(args) == 0 {
panic("an argument is required")
}
// ...
}
func main() {
run(os.Args[1:])
}
"><pre><span class="pl-k">func</span> <span class="pl-en">run</span>(<span class="pl-s1">args</span> []<span class="pl-smi">string</span>) {
<span class="pl-k">if</span> <span class="pl-en">len</span>(<span class="pl-s1">args</span>) <span class="pl-c1">==</span> <span class="pl-c1">0</span> {
<span class="pl-en">panic</span>(<span class="pl-s">"an argument is required"</span>)
}
<span class="pl-c">// ...</span>
}
<span class="pl-k">func</span> <span class="pl-en">main</span>() {
<span class="pl-en">run</span>(<span class="pl-s1">os</span>.<span class="pl-c1">Args</span>[<span class="pl-c1">1</span>:])
}
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func run(args []string) error {
if len(args) == 0 {
return errors.New("an argument is required")
}
// ...
return nil
}
func main() {
if err := run(os.Args[1:]); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}"><pre><span class="pl-k">func</span> <span class="pl-en">run</span>(<span class="pl-s1">args</span> []<span class="pl-smi">string</span>) <span class="pl-smi">error</span> {
<span class="pl-k">if</span> <span class="pl-en">len</span>(<span class="pl-s1">args</span>) <span class="pl-c1">==</span> <span class="pl-c1">0</span> {
<span class="pl-k">return</span> <span class="pl-s1">errors</span>.<span class="pl-en">New</span>(<span class="pl-s">"an argument is required"</span>)
}
<span class="pl-c">// ...</span>
<span class="pl-k">return</span> <span class="pl-c1">nil</span>
}
<span class="pl-k">func</span> <span class="pl-en">main</span>() {
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-en">run</span>(<span class="pl-s1">os</span>.<span class="pl-c1">Args</span>[<span class="pl-c1">1</span>:]); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-s1">fmt</span>.<span class="pl-en">Fprintln</span>(<span class="pl-s1">os</span>.<span class="pl-c1">Stderr</span>, <span class="pl-s1">err</span>)
<span class="pl-s1">os</span>.<span class="pl-en">Exit</span>(<span class="pl-c1">1</span>)
}
}</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">Panic/recover is not an error handling strategy. A program must panic only when something irrecoverable happens such as a nil dereference. An exception to this is program initialization: bad things at program startup that should abort the program may cause panic.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var _statusTemplate = template.Must(template.New("name").Parse("_statusHTML"))"><pre><span class="pl-k">var</span> <span class="pl-s1">_statusTemplate</span> <span class="pl-c1">=</span> <span class="pl-s1">template</span>.<span class="pl-en">Must</span>(<span class="pl-s1">template</span>.<span class="pl-en">New</span>(<span class="pl-s">"name"</span>).<span class="pl-en">Parse</span>(<span class="pl-s">"_statusHTML"</span>))</pre></div>
<p dir="auto">Even in tests, prefer <code>t.Fatal</code> or <code>t.FailNow</code> over panics to ensure that the test is marked as failed.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// func TestFoo(t *testing.T)
f, err := os.CreateTemp("", "test")
if err != nil {
panic("failed to set up test")
}"><pre><span class="pl-c">// func TestFoo(t *testing.T)</span>
<span class="pl-s1">f</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">CreateTemp</span>(<span class="pl-s">""</span>, <span class="pl-s">"test"</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-en">panic</span>(<span class="pl-s">"failed to set up test"</span>)
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// func TestFoo(t *testing.T)
f, err := os.CreateTemp("", "test")
if err != nil {
t.Fatal("failed to set up test")
}"><pre><span class="pl-c">// func TestFoo(t *testing.T)</span>
<span class="pl-s1">f</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">CreateTemp</span>(<span class="pl-s">""</span>, <span class="pl-s">"test"</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-s1">t</span>.<span class="pl-en">Fatal</span>(<span class="pl-s">"failed to set up test"</span>)
}</pre></div>
</td></tr>
</tbody></table>
<h2 dir="auto"><a id="user-content-avoid-mutable-globals" class="anchor" aria-hidden="true" href="#avoid-mutable-globals"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Avoid Mutable Globals</h2>
<p dir="auto">Avoid mutating global variables, instead opting for dependency injection. This applies to function pointers as well as other kinds of values.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// sign.go
var _timeNow = time.Now
func sign(msg string) string {
now := _timeNow()
return signWithTime(msg, now)
}
"><pre><span class="pl-c">// sign.go</span>
<span class="pl-k">var</span> <span class="pl-s1">_timeNow</span> <span class="pl-c1">=</span> <span class="pl-s1">time</span>.<span class="pl-c1">Now</span>
<span class="pl-k">func</span> <span class="pl-en">sign</span>(<span class="pl-s1">msg</span> <span class="pl-smi">string</span>) <span class="pl-smi">string</span> {
<span class="pl-s1">now</span> <span class="pl-c1">:=</span> <span class="pl-en">_timeNow</span>()
<span class="pl-k">return</span> <span class="pl-en">signWithTime</span>(<span class="pl-s1">msg</span>, <span class="pl-s1">now</span>)
}
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// sign.go
type signer struct {
now func() time.Time
}
func newSigner() *signer {
return &signer{
now: time.Now,
}
}
func (s *signer) Sign(msg string) string {
now := s.now()
return signWithTime(msg, now)
}"><pre><span class="pl-c">// sign.go</span>
<span class="pl-k">type</span> <span class="pl-smi">signer</span> <span class="pl-k">struct</span> {
<span class="pl-c1">now</span> <span class="pl-k">func</span>() time.<span class="pl-smi">Time</span>
}
<span class="pl-k">func</span> <span class="pl-en">newSigner</span>() <span class="pl-c1">*</span><span class="pl-smi">signer</span> {
<span class="pl-k">return</span> <span class="pl-c1">&</span><span class="pl-smi">signer</span>{
<span class="pl-c1">now</span>: <span class="pl-s1">time</span>.<span class="pl-c1">Now</span>,
}
}
<span class="pl-k">func</span> (<span class="pl-s1">s</span> <span class="pl-c1">*</span><span class="pl-smi">signer</span>) <span class="pl-en">Sign</span>(<span class="pl-s1">msg</span> <span class="pl-smi">string</span>) <span class="pl-smi">string</span> {
<span class="pl-s1">now</span> <span class="pl-c1">:=</span> <span class="pl-s1">s</span>.<span class="pl-en">now</span>()
<span class="pl-k">return</span> <span class="pl-en">signWithTime</span>(<span class="pl-s1">msg</span>, <span class="pl-s1">now</span>)
}</pre></div>
</td></tr>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// sign_test.go
func TestSign(t *testing.T) {
oldTimeNow := _timeNow
_timeNow = func() time.Time {
return someFixedTime
}
defer func() { _timeNow = oldTimeNow }()
assert.Equal(t, want, sign(give))
}"><pre><span class="pl-c">// sign_test.go</span>
<span class="pl-k">func</span> <span class="pl-en">TestSign</span>(<span class="pl-s1">t</span> <span class="pl-c1">*</span>testing.<span class="pl-smi">T</span>) {
<span class="pl-s1">oldTimeNow</span> <span class="pl-c1">:=</span> <span class="pl-s1">_timeNow</span>
<span class="pl-s1">_timeNow</span> <span class="pl-c1">=</span> <span class="pl-k">func</span>() time.<span class="pl-smi">Time</span> {
<span class="pl-k">return</span> <span class="pl-s1">someFixedTime</span>
}
<span class="pl-k">defer</span> <span class="pl-k">func</span>() { <span class="pl-s1">_timeNow</span> <span class="pl-c1">=</span> <span class="pl-s1">oldTimeNow</span> }()
<span class="pl-s1">assert</span>.<span class="pl-en">Equal</span>(<span class="pl-s1">t</span>, <span class="pl-s1">want</span>, <span class="pl-en">sign</span>(<span class="pl-s1">give</span>))
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// sign_test.go
func TestSigner(t *testing.T) {
s := newSigner()
s.now = func() time.Time {
return someFixedTime
}
assert.Equal(t, want, s.Sign(give))
}
"><pre><span class="pl-c">// sign_test.go</span>
<span class="pl-k">func</span> <span class="pl-en">TestSigner</span>(<span class="pl-s1">t</span> <span class="pl-c1">*</span>testing.<span class="pl-smi">T</span>) {
<span class="pl-s1">s</span> <span class="pl-c1">:=</span> <span class="pl-en">newSigner</span>()
<span class="pl-s1">s</span>.<span class="pl-c1">now</span> <span class="pl-c1">=</span> <span class="pl-k">func</span>() time.<span class="pl-smi">Time</span> {
<span class="pl-k">return</span> <span class="pl-s1">someFixedTime</span>
}
<span class="pl-s1">assert</span>.<span class="pl-en">Equal</span>(<span class="pl-s1">t</span>, <span class="pl-s1">want</span>, <span class="pl-s1">s</span>.<span class="pl-en">Sign</span>(<span class="pl-s1">give</span>))
}</pre></div>
</td></tr>
</tbody></table>
<h2 dir="auto"><a id="user-content-avoid-embedding-types-in-public-structs" class="anchor" aria-hidden="true" href="#avoid-embedding-types-in-public-structs"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Avoid Embedding Types in Public Structs</h2>
<p dir="auto">These embedded types leak implementation details, inhibit type evolution, and obscure documentation. Assuming you have implemented a variety of list types using a shared <code>AbstractList</code>, avoid embedding the <code>AbstractList</code> in your concrete list implementations. Instead, hand-write only the methods to your concrete list that will delegate to the abstract list.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type AbstractList struct {}
// Add adds an entity to the list.
func (l *AbstractList) Add(e Entity) {
// ...
}
// Remove removes an entity from the list.
func (l *AbstractList) Remove(e Entity) {
// ...
}"><pre><span class="pl-k">type</span> <span class="pl-smi">AbstractList</span> <span class="pl-k">struct</span> {}
<span class="pl-c">// Add adds an entity to the list.</span>
<span class="pl-k">func</span> (<span class="pl-s1">l</span> <span class="pl-c1">*</span><span class="pl-smi">AbstractList</span>) <span class="pl-en">Add</span>(<span class="pl-s1">e</span> <span class="pl-smi">Entity</span>) {
<span class="pl-c">// ...</span>
}
<span class="pl-c">// Remove removes an entity from the list.</span>
<span class="pl-k">func</span> (<span class="pl-s1">l</span> <span class="pl-c1">*</span><span class="pl-smi">AbstractList</span>) <span class="pl-en">Remove</span>(<span class="pl-s1">e</span> <span class="pl-smi">Entity</span>) {
<span class="pl-c">// ...</span>
}</pre></div>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// ConcreteList is a list of entities.
type ConcreteList struct {
*AbstractList
}
"><pre><span class="pl-c">// ConcreteList is a list of entities.</span>
<span class="pl-k">type</span> <span class="pl-smi">ConcreteList</span> <span class="pl-k">struct</span> {
<span class="pl-c1">*</span><span class="pl-smi">AbstractList</span>
}
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// ConcreteList is a list of entities.
type ConcreteList struct {
list *AbstractList
}
// Add adds an entity to the list.
func (l *ConcreteList) Add(e Entity) {
l.list.Add(e)
}
// Remove removes an entity from the list.
func (l *ConcreteList) Remove(e Entity) {
l.list.Remove(e)
}"><pre><span class="pl-c">// ConcreteList is a list of entities.</span>
<span class="pl-k">type</span> <span class="pl-smi">ConcreteList</span> <span class="pl-k">struct</span> {
<span class="pl-c1">list</span> <span class="pl-c1">*</span><span class="pl-smi">AbstractList</span>
}
<span class="pl-c">// Add adds an entity to the list.</span>
<span class="pl-k">func</span> (<span class="pl-s1">l</span> <span class="pl-c1">*</span><span class="pl-smi">ConcreteList</span>) <span class="pl-en">Add</span>(<span class="pl-s1">e</span> <span class="pl-smi">Entity</span>) {
<span class="pl-s1">l</span>.<span class="pl-c1">list</span>.<span class="pl-en">Add</span>(<span class="pl-s1">e</span>)
}
<span class="pl-c">// Remove removes an entity from the list.</span>
<span class="pl-k">func</span> (<span class="pl-s1">l</span> <span class="pl-c1">*</span><span class="pl-smi">ConcreteList</span>) <span class="pl-en">Remove</span>(<span class="pl-s1">e</span> <span class="pl-smi">Entity</span>) {
<span class="pl-s1">l</span>.<span class="pl-c1">list</span>.<span class="pl-en">Remove</span>(<span class="pl-s1">e</span>)
}</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">Go allows <a href="https://golang.org/doc/effective_go.html#embedding" rel="nofollow">type embedding</a> as a compromise between inheritance and composition. The outer type gets implicit copies of the embedded type's methods. These methods, by default, delegate to the same method of the embedded instance.</p>
<p dir="auto">The struct also gains a field by the same name as the type. So, if the embedded type is public, the field is public. To maintain backward compatibility, every future version of the outer type must keep the embedded type.</p>
<p dir="auto">An embedded type is rarely necessary. It is a convenience that helps you avoid writing tedious delegate methods.</p>
<p dir="auto">Even embedding a compatible AbstractList <em>interface</em>, instead of the struct, would offer the developer more flexibility to change in the future, but still leak the detail that the concrete lists use an abstract implementation.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// AbstractList is a generalized implementation
// for various kinds of lists of entities.
type AbstractList interface {
Add(Entity)
Remove(Entity)
}
// ConcreteList is a list of entities.
type ConcreteList struct {
AbstractList
}
"><pre><span class="pl-c">// AbstractList is a generalized implementation</span>
<span class="pl-c">// for various kinds of lists of entities.</span>
<span class="pl-k">type</span> <span class="pl-smi">AbstractList</span> <span class="pl-k">interface</span> {
<span class="pl-c1">Add</span>(<span class="pl-smi">Entity</span>)
<span class="pl-c1">Remove</span>(<span class="pl-smi">Entity</span>)
}
<span class="pl-c">// ConcreteList is a list of entities.</span>
<span class="pl-k">type</span> <span class="pl-smi">ConcreteList</span> <span class="pl-k">struct</span> {
<span class="pl-smi">AbstractList</span>
}
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// ConcreteList is a list of entities.
type ConcreteList struct {
list AbstractList
}
// Add adds an entity to the list.
func (l *ConcreteList) Add(e Entity) {
l.list.Add(e)
}
// Remove removes an entity from the list.
func (l *ConcreteList) Remove(e Entity) {
l.list.Remove(e)
}"><pre><span class="pl-c">// ConcreteList is a list of entities.</span>
<span class="pl-k">type</span> <span class="pl-smi">ConcreteList</span> <span class="pl-k">struct</span> {
<span class="pl-c1">list</span> <span class="pl-smi">AbstractList</span>
}
<span class="pl-c">// Add adds an entity to the list.</span>
<span class="pl-k">func</span> (<span class="pl-s1">l</span> <span class="pl-c1">*</span><span class="pl-smi">ConcreteList</span>) <span class="pl-en">Add</span>(<span class="pl-s1">e</span> <span class="pl-smi">Entity</span>) {
<span class="pl-s1">l</span>.<span class="pl-c1">list</span>.<span class="pl-en">Add</span>(<span class="pl-s1">e</span>)
}
<span class="pl-c">// Remove removes an entity from the list.</span>
<span class="pl-k">func</span> (<span class="pl-s1">l</span> <span class="pl-c1">*</span><span class="pl-smi">ConcreteList</span>) <span class="pl-en">Remove</span>(<span class="pl-s1">e</span> <span class="pl-smi">Entity</span>) {
<span class="pl-s1">l</span>.<span class="pl-c1">list</span>.<span class="pl-en">Remove</span>(<span class="pl-s1">e</span>)
}</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">Either with an embedded struct or an embedded interface, the embedded type places limits on the evolution of the type.</p>
<ul dir="auto">
<li>Adding methods to an embedded interface is a breaking change.</li>
<li>Removing methods from an embedded struct is a breaking change.</li>
<li>Removing the embedded type is a breaking change.</li>
<li>Replacing the embedded type, even with an alternative that satisfies the same
interface, is a breaking change.</li>
</ul>
<p dir="auto">Although writing these delegate methods is tedious, the additional effort hides an implementation detail, leaves more opportunities for change and also eliminates indirection for discovering the full List interface in documentation.</p>
<h2 dir="auto"><a id="user-content-avoid-using-built-in-names" class="anchor" aria-hidden="true" href="#avoid-using-built-in-names"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Avoid Using Built-In Names</h2>
<p dir="auto">The Go <a href="https://golang.org/ref/spec" rel="nofollow">language specification</a> outlines several built-in, <a href="https://golang.org/ref/spec#Predeclared_identifiers" rel="nofollow">predeclared identifiers</a> that should not be used as names within Go programs.</p>
<p dir="auto">Depending on context, reusing these identifiers as names will either shadow the original within the current lexical scope (and any nested scopes) or make affected code confusing. In the best case, the compiler will complain; in the worst case, such code may introduce latent, hard-to-grep bugs.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var error string
// `error` shadows the builtin
// or
func handleErrorMessage(error string) {
// `error` shadows the builtin
}"><pre><span class="pl-k">var</span> <span class="pl-s1">error</span> <span class="pl-smi">string</span>
<span class="pl-c">// `error` shadows the builtin</span>
<span class="pl-c">// or</span>
<span class="pl-k">func</span> <span class="pl-en">handleErrorMessage</span>(<span class="pl-s1">error</span> <span class="pl-smi">string</span>) {
<span class="pl-c">// `error` shadows the builtin</span>
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var errorMessage string
// `error` refers to the builtin
// or
func handleErrorMessage(msg string) {
// `error` refers to the builtin
}"><pre><span class="pl-k">var</span> <span class="pl-s1">errorMessage</span> <span class="pl-smi">string</span>
<span class="pl-c">// `error` refers to the builtin</span>
<span class="pl-c">// or</span>
<span class="pl-k">func</span> <span class="pl-en">handleErrorMessage</span>(<span class="pl-s1">msg</span> <span class="pl-smi">string</span>) {
<span class="pl-c">// `error` refers to the builtin</span>
}</pre></div>
</td></tr>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Foo struct {
// While these fields technically don't
// constitute shadowing, grepping for
// `error` or `string` strings is now
// ambiguous.
error error
string string
}
func (f Foo) Error() error {
// `error` and `f.error` are
// visually similar
return f.error
}
func (f Foo) String() string {
// `string` and `f.string` are
// visually similar
return f.string
}"><pre><span class="pl-k">type</span> <span class="pl-smi">Foo</span> <span class="pl-k">struct</span> {
<span class="pl-c">// While these fields technically don't</span>
<span class="pl-c">// constitute shadowing, grepping for</span>
<span class="pl-c">// `error` or `string` strings is now</span>
<span class="pl-c">// ambiguous.</span>
<span class="pl-c1">error</span> <span class="pl-smi">error</span>
<span class="pl-c1">string</span> <span class="pl-smi">string</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">f</span> <span class="pl-smi">Foo</span>) <span class="pl-en">Error</span>() <span class="pl-smi">error</span> {
<span class="pl-c">// `error` and `f.error` are</span>
<span class="pl-c">// visually similar</span>
<span class="pl-k">return</span> <span class="pl-s1">f</span>.<span class="pl-c1">error</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">f</span> <span class="pl-smi">Foo</span>) <span class="pl-en">String</span>() <span class="pl-smi">string</span> {
<span class="pl-c">// `string` and `f.string` are</span>
<span class="pl-c">// visually similar</span>
<span class="pl-k">return</span> <span class="pl-s1">f</span>.<span class="pl-c1">string</span>
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Foo struct {
// `error` and `string` strings are
// now unambiguous.
err error
str string
}
func (f Foo) Error() error {
return f.err
}
func (f Foo) String() string {
return f.str
}
"><pre><span class="pl-k">type</span> <span class="pl-smi">Foo</span> <span class="pl-k">struct</span> {
<span class="pl-c">// `error` and `string` strings are</span>
<span class="pl-c">// now unambiguous.</span>
<span class="pl-c1">err</span> <span class="pl-smi">error</span>
<span class="pl-c1">str</span> <span class="pl-smi">string</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">f</span> <span class="pl-smi">Foo</span>) <span class="pl-en">Error</span>() <span class="pl-smi">error</span> {
<span class="pl-k">return</span> <span class="pl-s1">f</span>.<span class="pl-c1">err</span>
}
<span class="pl-k">func</span> (<span class="pl-s1">f</span> <span class="pl-smi">Foo</span>) <span class="pl-en">String</span>() <span class="pl-smi">string</span> {
<span class="pl-k">return</span> <span class="pl-s1">f</span>.<span class="pl-c1">str</span>
}
</pre></div>
</td></tr>
</tbody></table>
<p dir="auto"><em>Note:</em> the compiler will not generate errors when using predeclared identifiers, but tools such as <code>go vet</code> should correctly point out these and other cases of shadowing.</p>
<h2 dir="auto"><a id="user-content-avoid-init" class="anchor" aria-hidden="true" href="#avoid-init"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Avoid <code>init()</code></h2>
<p dir="auto">Avoid <code>init()</code> where possible. When <code>init()</code> is unavoidable or desirable, code should attempt to:</p>
<ol dir="auto">
<li>Be completely deterministic, regardless of program environment or invocation.</li>
<li>Avoid depending on the ordering or side-effects of other <code>init()</code> functions. While <code>init()</code> ordering is well-known, code can change, and thus relationships between <code>init()</code> functions can make code brittle and error-prone.</li>
<li>Avoid accessing or manipulating global or environment state, such as machine information, environment variables, working directory, program arguments/inputs, etc.</li>
<li>Avoid I/O, including both filesystem, network, and system calls.</li>
</ol>
<p dir="auto">Code that cannot satisfy these requirements likely belongs as a helper to be called as part of <code>main()</code> (or elsewhere in a program's lifecycle), or be written as part of <code>main()</code> itself. In particular, libraries that are intended to be used by other programs should take special care to be completely deterministic and not perform "init magic".</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Foo struct {
// ...
}
var _defaultFoo Foo
func init() {
_defaultFoo = Foo{
// ...
}
}
"><pre><span class="pl-k">type</span> <span class="pl-smi">Foo</span> <span class="pl-k">struct</span> {
<span class="pl-c">// ...</span>
}
<span class="pl-k">var</span> <span class="pl-s1">_defaultFoo</span> <span class="pl-smi">Foo</span>
<span class="pl-k">func</span> <span class="pl-en">init</span>() {
<span class="pl-s1">_defaultFoo</span> <span class="pl-c1">=</span> <span class="pl-smi">Foo</span>{
<span class="pl-c">// ...</span>
}
}
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var _defaultFoo = Foo{
// ...
}
// or, better, for testability:
var _defaultFoo = defaultFoo()
func defaultFoo() Foo {
return Foo{
// ...
}
}"><pre><span class="pl-k">var</span> <span class="pl-s1">_defaultFoo</span> <span class="pl-c1">=</span> <span class="pl-smi">Foo</span>{
<span class="pl-c">// ...</span>
}
<span class="pl-c">// or, better, for testability:</span>
<span class="pl-k">var</span> <span class="pl-s1">_defaultFoo</span> <span class="pl-c1">=</span> <span class="pl-en">defaultFoo</span>()
<span class="pl-k">func</span> <span class="pl-en">defaultFoo</span>() <span class="pl-smi">Foo</span> {
<span class="pl-k">return</span> <span class="pl-smi">Foo</span>{
<span class="pl-c">// ...</span>
}
}</pre></div>
</td></tr>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Config struct {
// ...
}
var _config Config
func init() {
// Bad: based on current directory
cwd, _ := os.Getwd()
// Bad: I/O
raw, _ := os.ReadFile(
path.Join(cwd, "config", "config.yaml"),
)
yaml.Unmarshal(raw, &_config)
}"><pre><span class="pl-k">type</span> <span class="pl-smi">Config</span> <span class="pl-k">struct</span> {
<span class="pl-c">// ...</span>
}
<span class="pl-k">var</span> <span class="pl-s1">_config</span> <span class="pl-smi">Config</span>
<span class="pl-k">func</span> <span class="pl-en">init</span>() {
<span class="pl-c">// Bad: based on current directory</span>
<span class="pl-s1">cwd</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">Getwd</span>()
<span class="pl-c">// Bad: I/O</span>
<span class="pl-s1">raw</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">ReadFile</span>(
<span class="pl-s1">path</span>.<span class="pl-en">Join</span>(<span class="pl-s1">cwd</span>, <span class="pl-s">"config"</span>, <span class="pl-s">"config.yaml"</span>),
)
<span class="pl-s1">yaml</span>.<span class="pl-en">Unmarshal</span>(<span class="pl-s1">raw</span>, <span class="pl-c1">&</span><span class="pl-s1">_config</span>)
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type Config struct {
// ...
}
func loadConfig() Config {
cwd, err := os.Getwd()
// handle err
raw, err := os.ReadFile(
path.Join(cwd, "config", "config.yaml"),
)
// handle err
var config Config
yaml.Unmarshal(raw, &config)
return config
}"><pre><span class="pl-k">type</span> <span class="pl-smi">Config</span> <span class="pl-k">struct</span> {
<span class="pl-c">// ...</span>
}
<span class="pl-k">func</span> <span class="pl-en">loadConfig</span>() <span class="pl-smi">Config</span> {
<span class="pl-s1">cwd</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">Getwd</span>()
<span class="pl-c">// handle err</span>
<span class="pl-s1">raw</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">ReadFile</span>(
<span class="pl-s1">path</span>.<span class="pl-en">Join</span>(<span class="pl-s1">cwd</span>, <span class="pl-s">"config"</span>, <span class="pl-s">"config.yaml"</span>),
)
<span class="pl-c">// handle err</span>
<span class="pl-k">var</span> <span class="pl-s1">config</span> <span class="pl-smi">Config</span>
<span class="pl-s1">yaml</span>.<span class="pl-en">Unmarshal</span>(<span class="pl-s1">raw</span>, <span class="pl-c1">&</span><span class="pl-s1">config</span>)
<span class="pl-k">return</span> <span class="pl-s1">config</span>
}</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">Considering the above, some situations in which <code>init()</code> may be preferable or necessary might include:</p>
<ul dir="auto">
<li>Complex expressions that cannot be represented as single assignments.</li>
<li>Pluggable hooks, such as <code>database/sql</code> dialects, encoding type registries, etc.</li>
<li>Optimizations to <a href="https://cloud.google.com/functions/docs/bestpractices/tips#use_global_variables_to_reuse_objects_in_future_invocations" rel="nofollow">Google Cloud Functions</a> and other forms of deterministic precomputation.</li>
</ul>
<h2 dir="auto"><a id="user-content-exit-in-main" class="anchor" aria-hidden="true" href="#exit-in-main"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Exit in Main</h2>
<p dir="auto">Go programs use <a href="https://golang.org/pkg/os/#Exit" rel="nofollow"><code>os.Exit</code></a> or <a href="https://golang.org/pkg/log/#Fatal" rel="nofollow"><code>log.Fatal*</code></a> to exit immediately. (Panicking is not a good way to exit programs, please <a href="#panic-with-care">Panic with care</a>.)</p>
<p dir="auto">Call one of <code>os.Exit</code> or <code>log.Fatal*</code> <strong>only in <code>main()</code></strong>. All other functions should return errors to signal failure.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func main() {
body := readFile(path)
fmt.Println(body)
}
func readFile(path string) string {
f, err := os.Open(path)
if err != nil {
log.Fatal(err)
}
b, err := io.ReadAll(f)
if err != nil {
log.Fatal(err)
}
return string(b)
}
"><pre><span class="pl-k">func</span> <span class="pl-en">main</span>() {
<span class="pl-s1">body</span> <span class="pl-c1">:=</span> <span class="pl-en">readFile</span>(<span class="pl-s1">path</span>)
<span class="pl-s1">fmt</span>.<span class="pl-en">Println</span>(<span class="pl-s1">body</span>)
}
<span class="pl-k">func</span> <span class="pl-en">readFile</span>(<span class="pl-s1">path</span> <span class="pl-smi">string</span>) <span class="pl-smi">string</span> {
<span class="pl-s1">f</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">Open</span>(<span class="pl-s1">path</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-s1">log</span>.<span class="pl-en">Fatal</span>(<span class="pl-s1">err</span>)
}
<span class="pl-s1">b</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">io</span>.<span class="pl-en">ReadAll</span>(<span class="pl-s1">f</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-s1">log</span>.<span class="pl-en">Fatal</span>(<span class="pl-s1">err</span>)
}
<span class="pl-k">return</span> <span class="pl-en">string</span>(<span class="pl-s1">b</span>)
}
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="func main() {
body, err := readFile(path)
if err != nil {
log.Fatal(err)
}
fmt.Println(body)
}
func readFile(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", err
}
b, err := io.ReadAll(f)
if err != nil {
return "", err
}
return string(b), nil
}"><pre><span class="pl-k">func</span> <span class="pl-en">main</span>() {
<span class="pl-s1">body</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-en">readFile</span>(<span class="pl-s1">path</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-s1">log</span>.<span class="pl-en">Fatal</span>(<span class="pl-s1">err</span>)
}
<span class="pl-s1">fmt</span>.<span class="pl-en">Println</span>(<span class="pl-s1">body</span>)
}
<span class="pl-k">func</span> <span class="pl-en">readFile</span>(<span class="pl-s1">path</span> <span class="pl-smi">string</span>) (<span class="pl-smi">string</span>, <span class="pl-smi">error</span>) {
<span class="pl-s1">f</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">Open</span>(<span class="pl-s1">path</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">return</span> <span class="pl-s">""</span>, <span class="pl-s1">err</span>
}
<span class="pl-s1">b</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">io</span>.<span class="pl-en">ReadAll</span>(<span class="pl-s1">f</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">return</span> <span class="pl-s">""</span>, <span class="pl-s1">err</span>
}
<span class="pl-k">return</span> <span class="pl-en">string</span>(<span class="pl-s1">b</span>), <span class="pl-c1">nil</span>
}</pre></div>
</td></tr>
</tbody></table>
<p dir="auto">Rationale: Programs with multiple functions that exit present a few issues:</p>
<ul dir="auto">
<li>Non-obvious control flow: Any function can exit the program so it becomes difficult to reason about the control flow.</li>
<li>Difficult to test: A function that exits the program will also exit the test calling it. This makes the function difficult to test and introduces risk of skipping other tests that have not yet been run by <code>go test</code>.</li>
<li>Skipped cleanup: When a function exits the program, it skips function calls enqueued with <code>defer</code> statements. This adds risk of skipping important cleanup tasks.</li>
</ul>
<h3 dir="auto"><a id="user-content-exit-once" class="anchor" aria-hidden="true" href="#exit-once"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Exit Once</h3>
<p dir="auto">If possible, prefer to call <code>os.Exit</code> or <code>log.Fatal</code> <strong>at most once</strong> in your <code>main()</code>. If there are multiple error scenarios that halt program execution, put that logic under a separate function and return errors from it.</p>
<p dir="auto">This has the effect of shortening your <code>main()</code> function and putting all key business logic into a separate, testable function.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="package main
func main() {
args := os.Args[1:]
if len(args) != 1 {
log.Fatal("missing file")
}
name := args[0]
f, err := os.Open(name)
if err != nil {
log.Fatal(err)
}
defer f.Close()
// If we call log.Fatal after this line,
// f.Close will not be called.
b, err := io.ReadAll(f)
if err != nil {
log.Fatal(err)
}
// ...
}
"><pre><span class="pl-k">package</span> main
<span class="pl-k">func</span> <span class="pl-en">main</span>() {
<span class="pl-s1">args</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-c1">Args</span>[<span class="pl-c1">1</span>:]
<span class="pl-k">if</span> <span class="pl-en">len</span>(<span class="pl-s1">args</span>) <span class="pl-c1">!=</span> <span class="pl-c1">1</span> {
<span class="pl-s1">log</span>.<span class="pl-en">Fatal</span>(<span class="pl-s">"missing file"</span>)
}
<span class="pl-s1">name</span> <span class="pl-c1">:=</span> <span class="pl-s1">args</span>[<span class="pl-c1">0</span>]
<span class="pl-s1">f</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">Open</span>(<span class="pl-s1">name</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-s1">log</span>.<span class="pl-en">Fatal</span>(<span class="pl-s1">err</span>)
}
<span class="pl-k">defer</span> <span class="pl-s1">f</span>.<span class="pl-en">Close</span>()
<span class="pl-c">// If we call log.Fatal after this line,</span>
<span class="pl-c">// f.Close will not be called.</span>
<span class="pl-s1">b</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">io</span>.<span class="pl-en">ReadAll</span>(<span class="pl-s1">f</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-s1">log</span>.<span class="pl-en">Fatal</span>(<span class="pl-s1">err</span>)
}
<span class="pl-c">// ...</span>
}
</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="package main
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
args := os.Args[1:]
if len(args) != 1 {
return errors.New("missing file")
}
name := args[0]
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
b, err := io.ReadAll(f)
if err != nil {
return err
}
// ...
}"><pre><span class="pl-k">package</span> main
<span class="pl-k">func</span> <span class="pl-en">main</span>() {
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-en">run</span>(); <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-s1">log</span>.<span class="pl-en">Fatal</span>(<span class="pl-s1">err</span>)
}
}
<span class="pl-k">func</span> <span class="pl-en">run</span>() <span class="pl-smi">error</span> {
<span class="pl-s1">args</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-c1">Args</span>[<span class="pl-c1">1</span>:]
<span class="pl-k">if</span> <span class="pl-en">len</span>(<span class="pl-s1">args</span>) <span class="pl-c1">!=</span> <span class="pl-c1">1</span> {
<span class="pl-k">return</span> <span class="pl-s1">errors</span>.<span class="pl-en">New</span>(<span class="pl-s">"missing file"</span>)
}
<span class="pl-s1">name</span> <span class="pl-c1">:=</span> <span class="pl-s1">args</span>[<span class="pl-c1">0</span>]
<span class="pl-s1">f</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">Open</span>(<span class="pl-s1">name</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">return</span> <span class="pl-s1">err</span>
}
<span class="pl-k">defer</span> <span class="pl-s1">f</span>.<span class="pl-en">Close</span>()
<span class="pl-s1">b</span>, <span class="pl-s1">err</span> <span class="pl-c1">:=</span> <span class="pl-s1">io</span>.<span class="pl-en">ReadAll</span>(<span class="pl-s1">f</span>)
<span class="pl-k">if</span> <span class="pl-s1">err</span> <span class="pl-c1">!=</span> <span class="pl-c1">nil</span> {
<span class="pl-k">return</span> <span class="pl-s1">err</span>
}
<span class="pl-c">// ...</span>
}</pre></div>
</td></tr>
</tbody></table>
<h2 dir="auto"><a id="user-content-performance" class="anchor" aria-hidden="true" href="#performance"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Performance</h2>
<p dir="auto">Performance-specific guidelines apply only to the hot path.</p>
<h3 dir="auto"><a id="user-content-prefer-strconv-over-fmt" class="anchor" aria-hidden="true" href="#prefer-strconv-over-fmt"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Prefer strconv over fmt</h3>
<p dir="auto">When converting primitives to/from strings, <code>strconv</code> is faster than <code>fmt</code>.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="for i := 0; i < b.N; i++ {
s := fmt.Sprint(rand.Int())
}"><pre><span class="pl-k">for</span> <span class="pl-s1">i</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">i</span> <span class="pl-c1"><</span> <span class="pl-s1">b</span>.<span class="pl-c1">N</span>; <span class="pl-s1">i</span><span class="pl-c1">++</span> {
<span class="pl-s1">s</span> <span class="pl-c1">:=</span> <span class="pl-s1">fmt</span>.<span class="pl-en">Sprint</span>(<span class="pl-s1">rand</span>.<span class="pl-en">Int</span>())
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="for i := 0; i < b.N; i++ {
s := strconv.Itoa(rand.Int())
}"><pre><span class="pl-k">for</span> <span class="pl-s1">i</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">i</span> <span class="pl-c1"><</span> <span class="pl-s1">b</span>.<span class="pl-c1">N</span>; <span class="pl-s1">i</span><span class="pl-c1">++</span> {
<span class="pl-s1">s</span> <span class="pl-c1">:=</span> <span class="pl-s1">strconv</span>.<span class="pl-en">Itoa</span>(<span class="pl-s1">rand</span>.<span class="pl-en">Int</span>())
}</pre></div>
</td></tr>
<tr><td>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="BenchmarkFmtSprint-4 143 ns/op 2 allocs/op"><pre class="notranslate"><code>BenchmarkFmtSprint-4 143 ns/op 2 allocs/op
</code></pre></div>
</td><td>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="BenchmarkStrconv-4 64.2 ns/op 1 allocs/op"><pre class="notranslate"><code>BenchmarkStrconv-4 64.2 ns/op 1 allocs/op
</code></pre></div>
</td></tr>
</tbody></table>
<h3 dir="auto"><a id="user-content-avoid-string-to-byte-conversion" class="anchor" aria-hidden="true" href="#avoid-string-to-byte-conversion"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Avoid string-to-byte conversion</h3>
<p dir="auto">Do not create byte slices from a fixed string repeatedly. Instead, perform the conversion once and capture the result.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="for i := 0; i < b.N; i++ {
w.Write([]byte("Hello world"))
}
"><pre><span class="pl-k">for</span> <span class="pl-s1">i</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">i</span> <span class="pl-c1"><</span> <span class="pl-s1">b</span>.<span class="pl-c1">N</span>; <span class="pl-s1">i</span><span class="pl-c1">++</span> {
<span class="pl-s1">w</span>.<span class="pl-en">Write</span>([]<span class="pl-smi">byte</span>(<span class="pl-s">"Hello world"</span>))
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="data := []byte("Hello world")
for i := 0; i < b.N; i++ {
w.Write(data)
}"><pre><span class="pl-s1">data</span> <span class="pl-c1">:=</span> []<span class="pl-smi">byte</span>(<span class="pl-s">"Hello world"</span>)
<span class="pl-k">for</span> <span class="pl-s1">i</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">i</span> <span class="pl-c1"><</span> <span class="pl-s1">b</span>.<span class="pl-c1">N</span>; <span class="pl-s1">i</span><span class="pl-c1">++</span> {
<span class="pl-s1">w</span>.<span class="pl-en">Write</span>(<span class="pl-s1">data</span>)
}</pre></div>
</td></tr>
<tr><td>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="BenchmarkBad-4 50000000 22.2 ns/op"><pre class="notranslate"><code>BenchmarkBad-4 50000000 22.2 ns/op
</code></pre></div>
</td><td>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="BenchmarkGood-4 500000000 3.25 ns/op"><pre class="notranslate"><code>BenchmarkGood-4 500000000 3.25 ns/op
</code></pre></div>
</td></tr>
</tbody></table>
<h3 dir="auto"><a id="user-content-prefer-specifying-container-capacity" class="anchor" aria-hidden="true" href="#prefer-specifying-container-capacity"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Prefer Specifying Container Capacity</h3>
<p dir="auto">Specify container capacity where possible in order to allocate memory for the container up front. This minimizes subsequent allocations (by copying and resizing of the container) as elements are added.</p>
<h3 dir="auto"><a id="user-content-specifying-map-capacity-hints" class="anchor" aria-hidden="true" href="#specifying-map-capacity-hints"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Specifying Map Capacity Hints</h3>
<p dir="auto">Where possible, provide capacity hints when initializing maps with <code>make()</code>.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="make(map[T1]T2, hint)"><pre><span class="pl-en">make</span>(<span class="pl-k">map</span>[<span class="pl-smi">T1</span>]<span class="pl-smi">T2</span>, <span class="pl-s1">hint</span>)</pre></div>
<p dir="auto">Providing a capacity hint to <code>make()</code> tries to right-size the map at initialization time, which reduces the need for growing the map and allocations as elements are added to the map.</p>
<p dir="auto"><em>Note:</em> unlike slices, map capacity hints do not guarantee complete, preemptive allocation, but are used to approximate the number of hashmap buckets required. Consequently, allocations may still occur when adding elements to the map, even up to the specified capacity.</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="m := make(map[string]os.FileInfo)
files, _ := os.ReadDir("./files")
for _, f := range files {
m[f.Name()] = f
}
"><pre><span class="pl-s1">m</span> <span class="pl-c1">:=</span> <span class="pl-en">make</span>(<span class="pl-k">map</span>[<span class="pl-smi">string</span>]os.<span class="pl-smi">FileInfo</span>)
<span class="pl-s1">files</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">ReadDir</span>(<span class="pl-s">"./files"</span>)
<span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">f</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">files</span> {
<span class="pl-s1">m</span>[<span class="pl-s1">f</span>.<span class="pl-en">Name</span>()] <span class="pl-c1">=</span> <span class="pl-s1">f</span>
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="
files, _ := os.ReadDir("./files")
m := make(map[string]os.FileInfo, len(files))
for _, f := range files {
m[f.Name()] = f
}"><pre><span class="pl-s1">files</span>, <span class="pl-s1">_</span> <span class="pl-c1">:=</span> <span class="pl-s1">os</span>.<span class="pl-en">ReadDir</span>(<span class="pl-s">"./files"</span>)
<span class="pl-s1">m</span> <span class="pl-c1">:=</span> <span class="pl-en">make</span>(<span class="pl-k">map</span>[<span class="pl-smi">string</span>]os.<span class="pl-smi">FileInfo</span>, <span class="pl-en">len</span>(<span class="pl-s1">files</span>))
<span class="pl-k">for</span> <span class="pl-s1">_</span>, <span class="pl-s1">f</span> <span class="pl-c1">:=</span> <span class="pl-k">range</span> <span class="pl-s1">files</span> {
<span class="pl-s1">m</span>[<span class="pl-s1">f</span>.<span class="pl-en">Name</span>()] <span class="pl-c1">=</span> <span class="pl-s1">f</span>
}</pre></div>
</td></tr>
<tr><td>
<p dir="auto"><code>m</code> is created without a size hint; there may be more allocations at assignment time.</p>
</td><td>
<p dir="auto"><code>m</code> is created with a size hint; there may be fewer allocations at assignment time.</p>
</td></tr>
</tbody></table>
<h3 dir="auto"><a id="user-content-specifying-slice-capacity" class="anchor" aria-hidden="true" href="#specifying-slice-capacity"><svg class="octicon octicon-link" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a>Specifying Slice Capacity</h3>
<p dir="auto">Where possible, provide capacity hints when initializing slices with <code>make()</code>, particularly when appending.</p>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="make([]T, length, capacity)"><pre><span class="pl-en">make</span>([]<span class="pl-smi">T</span>, <span class="pl-s1">length</span>, <span class="pl-s1">capacity</span>)</pre></div>
<p dir="auto">Unlike maps, slice capacity is not a hint: the compiler will allocate enough memory for the capacity of the slice as provided to <code>make()</code>, which means that subsequent <code>append()</code> operations will incur zero allocations (until the length of the slice matches the capacity, after which any appends will require a resize to hold additional elements).</p>
<table>
<thead><tr><th>Bad</th><th>Good</th></tr></thead>
<tbody>
<tr><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="for n := 0; n < b.N; n++ {
data := make([]int, 0)
for k := 0; k < size; k++{
data = append(data, k)
}
}"><pre><span class="pl-k">for</span> <span class="pl-s1">n</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">n</span> <span class="pl-c1"><</span> <span class="pl-s1">b</span>.<span class="pl-c1">N</span>; <span class="pl-s1">n</span><span class="pl-c1">++</span> {
<span class="pl-s1">data</span> <span class="pl-c1">:=</span> <span class="pl-en">make</span>([]<span class="pl-smi">int</span>, <span class="pl-c1">0</span>)
<span class="pl-k">for</span> <span class="pl-s1">k</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">k</span> <span class="pl-c1"><</span> <span class="pl-s1">size</span>; <span class="pl-s1">k</span><span class="pl-c1">++</span>{
<span class="pl-s1">data</span> <span class="pl-c1">=</span> <span class="pl-en">append</span>(<span class="pl-s1">data</span>, <span class="pl-s1">k</span>)
}
}</pre></div>
</td><td>
<div class="highlight highlight-source-go notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="for n := 0; n < b.N; n++ {
data := make([]int, 0, size)
for k := 0; k < size; k++{
data = append(data, k)
}
}"><pre><span class="pl-k">for</span> <span class="pl-s1">n</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">n</span> <span class="pl-c1"><</span> <span class="pl-s1">b</span>.<span class="pl-c1">N</span>; <span class="pl-s1">n</span><span class="pl-c1">++</span> {
<span class="pl-s1">data</span> <span class="pl-c1">:=</span> <span class="pl-en">make</span>([]<span class="pl-smi">int</span>, <span class="pl-c1">0</span>, <span class="pl-s1">size</span>)
<span class="pl-k">for</span> <span class="pl-s1">k</span> <span class="pl-c1">:=</span> <span class="pl-c1">0</span>; <span class="pl-s1">k</span> <span class="pl-c1"><</span> <span class="pl-s1">size</span>; <span class="pl-s1">k</span><span class="pl-c1">++</span>{
<span class="pl-s1">data</span> <span class="pl-c1">=</span> <span class="pl-en">append</span>(<span class="pl-s1">data</span>, <span class="pl-s1">k</span>)
}
}</pre></div>
</td></tr>
<tr><td>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="BenchmarkBad-4 100000000 2.48s"><pre class="notranslate"><code>BenchmarkBad-4 100000000 2.48s
</code></pre></div>
</td><td>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="BenchmarkGood-4 100000000 0.21s"><pre class="notranslate"><code>BenchmarkGood-4 100000000 0.21s
</code></pre></div>
</td></tr>
</tbody></table>
</article>
</div>
</div>
</readme-toc>
<details class="details-reset details-overlay details-overlay-dark" id="jumpto-line-details-dialog">
<summary data-hotkey="l" aria-label="Jump to line"></summary>
<details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast linejump" aria-label="Jump to line">
<!-- '"` --><!-- </textarea></xmp> --></option></form><form class="js-jump-to-line-form Box-body d-flex" data-turbo="false" action="" accept-charset="UTF-8" method="get">
<input class="form-control flex-auto mr-3 linejump-input js-jump-to-line-field" type="text" placeholder="Jump to line…" aria-label="Jump to line" autofocus>
<button data-close-dialog="" type="submit" data-view-component="true" class="btn"> Go
</button>
</form> </details-dialog>
</details>
</div>
</div>
</div>
</turbo-frame>
</main>
</div>
</div>
<footer class="footer width-full container-xl p-responsive">
<h2 class='sr-only'>Footer</h2>
<div class="position-relative d-flex flex-items-center pb-2 f6 color-fg-muted border-top color-border-muted flex-column-reverse flex-lg-row flex-wrap flex-lg-nowrap mt-6 pt-6">
<div class="list-style-none d-flex flex-wrap col-0 col-lg-2 flex-justify-start flex-lg-justify-between mb-2 mb-lg-0">
<div class="mt-2 mt-lg-0 d-flex flex-items-center">
<a aria-label="Homepage" title="GitHub" class="footer-octicon mr-2" href="https://github.com">
<svg aria-hidden="true" height="24" viewBox="0 0 16 16" version="1.1" width="24" data-view-component="true" class="octicon octicon-mark-github">
<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"></path>
</svg>
</a> <span>
© 2023 GitHub, Inc.
</span>
</div>
</div>
<nav aria-label='footer' class="col-12 col-lg-8">
<h3 class='sr-only' id='sr-footer-heading'>Footer navigation</h3>
<ul class="list-style-none d-flex flex-wrap col-12 flex-justify-center flex-lg-justify-between mb-2 mb-lg-0" aria-labelledby='sr-footer-heading'>
<li class="mr-3 mr-lg-0"><a href="https://docs.github.com/en/github/site-policy/github-terms-of-service" data-analytics-event="{"category":"Footer","action":"go to terms","label":"text:terms"}">Terms</a></li>
<li class="mr-3 mr-lg-0"><a href="https://docs.github.com/site-policy/privacy-policies/github-privacy-statement" data-analytics-event="{"category":"Footer","action":"go to privacy","label":"text:privacy"}">Privacy</a></li>
<li class="mr-3 mr-lg-0"><a data-analytics-event="{"category":"Footer","action":"go to security","label":"text:security"}" href="https://github.com/security">Security</a></li>
<li class="mr-3 mr-lg-0"><a href="https://www.githubstatus.com/" data-analytics-event="{"category":"Footer","action":"go to status","label":"text:status"}">Status</a></li>
<li class="mr-3 mr-lg-0"><a data-ga-click="Footer, go to help, text:Docs" href="https://docs.github.com">Docs</a></li>
<li class="mr-3 mr-lg-0"><a href="https://support.github.com?tags=dotcom-footer" data-analytics-event="{"category":"Footer","action":"go to contact","label":"text:contact"}">Contact GitHub</a></li>
<li class="mr-3 mr-lg-0"><a href="https://github.com/pricing" data-analytics-event="{"category":"Footer","action":"go to Pricing","label":"text:Pricing"}">Pricing</a></li>
<li class="mr-3 mr-lg-0"><a href="https://docs.github.com" data-analytics-event="{"category":"Footer","action":"go to api","label":"text:api"}">API</a></li>
<li class="mr-3 mr-lg-0"><a href="https://services.github.com" data-analytics-event="{"category":"Footer","action":"go to training","label":"text:training"}">Training</a></li>
<li class="mr-3 mr-lg-0"><a href="https://github.blog" data-analytics-event="{"category":"Footer","action":"go to blog","label":"text:blog"}">Blog</a></li>
<li><a data-ga-click="Footer, go to about, text:about" href="https://github.com/about">About</a></li>
</ul>
</nav>
</div>
<div class="d-flex flex-justify-center pb-6">
<span class="f6 color-fg-muted"></span>
</div>
</footer>
<div id="ajax-error-message" class="ajax-error-message flash flash-error" hidden>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
<path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path>
</svg>
<button type="button" class="flash-close js-ajax-error-dismiss" aria-label="Dismiss error">
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
<path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path>
</svg>
</button>
You can’t perform that action at this time.
</div>
<div class="js-stale-session-flash flash flash-warn flash-banner" hidden
>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-alert">
<path fill-rule="evenodd" d="M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z"></path>
</svg>
<span class="js-stale-session-flash-signed-in" hidden>You signed in with another tab or window. <a href="">Reload</a> to refresh your session.</span>
<span class="js-stale-session-flash-signed-out" hidden>You signed out in another tab or window. <a href="">Reload</a> to refresh your session.</span>
</div>
<template id="site-details-dialog">
<details class="details-reset details-overlay details-overlay-dark lh-default color-fg-default hx_rsm" open>
<summary role="button" aria-label="Close dialog"></summary>
<details-dialog class="Box Box--overlay d-flex flex-column anim-fade-in fast hx_rsm-dialog hx_rsm-modal">
<button class="Box-btn-octicon m-0 btn-octicon position-absolute right-0 top-0" type="button" aria-label="Close dialog" data-close-dialog>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-x">
<path fill-rule="evenodd" d="M3.72 3.72a.75.75 0 011.06 0L8 6.94l3.22-3.22a.75.75 0 111.06 1.06L9.06 8l3.22 3.22a.75.75 0 11-1.06 1.06L8 9.06l-3.22 3.22a.75.75 0 01-1.06-1.06L6.94 8 3.72 4.78a.75.75 0 010-1.06z"></path>
</svg>
</button>
<div class="octocat-spinner my-6 js-details-dialog-spinner"></div>
</details-dialog>
</details>
</template>
<div class="Popover js-hovercard-content position-absolute" style="display: none; outline: none;" tabindex="0">
<div class="Popover-message Popover-message--bottom-left Popover-message--large Box color-shadow-large" style="width:360px;">
</div>
</div>
<template id="snippet-clipboard-copy-button">
<div class="zeroclipboard-container position-absolute right-0 top-0">
<clipboard-copy aria-label="Copy" class="ClipboardButton btn js-clipboard-copy m-2 p-0 tooltipped-no-delay" data-copy-feedback="Copied!" data-tooltip-direction="w">
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-copy js-clipboard-copy-icon m-2">
<path fill-rule="evenodd" d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></path><path fill-rule="evenodd" d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path>
</svg>
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-check js-clipboard-check-icon color-fg-success d-none m-2">
<path fill-rule="evenodd" d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"></path>
</svg>
</clipboard-copy>
</div>
</template>
</div>
<div id="js-global-screen-reader-notice" class="sr-only" aria-live="polite" ></div>
</body>
</html>