panic: assignment to entry in nil map

原因

Go のプリミティブ型の一つ map は値型だと勘違いしていて、こんな runtime error と出会いました。

var m map[int]int // a map variable
m[0] = 1 // panic: assignment to entry in nil map

map は参照型でした。nil 値 (nil map) があります。実体は make か literal で作ります。

var a = make(map[int]int) // make an (initialized) map value
a[0] = 1
var b = map[int]int{} // a map literal value
b[0] = 1

注意点

Java 等の参照型主体の言語を使う人にはむしろ自然に見えるかもしれないが、次のような挙動は流石にきもいので気をつけて使ったほうが良さそう。

var m = *new(map[int]int)
println(m[0]) // 0
m[0] = 1 // panic: assignment to entry in nil map

new は non-nil な値を返しますが initialized value は作られず、nil map は empty map のように振る舞い、0で呼び出すと既定動作として初期値が返ります。そのあと代入して初めて落ちます。nil map は内部表現の実体が無く要素を追加することだけができず、挙動は似ていても nil map と empty map は区別されます。

var a = map[int]int(nil)
var b = map[int]int{}
println(a[0], a == nil) // 0 true
println(b[0], b == nil) // 0 false
// println(a == b) // invalid operation: a == b (map can only be compared to nil)
a[0] = 1
b[0] = 1 // panic: assignment to entry in nil map

なお、参照型なので普通に変数や引数にコピーを取ると実体は共有されます。

var a = map[int]int{}
a[0] = 1
var b = map[int]int(a)
b[0] = 2
println(a[0]) // 2

動作は仕様 Map types にさっくりと書いてあります。他には slice や channel も参照型で、それらも変数や new の初期値は nil で、当然 empty とも区別されます。

言い訳

なぜ勘違いしたかというと、多くの参照型を扱う言語と違って、Go にはポインタ型と強い型付けがあり C のように値とポインタを区別して扱うので、参照として扱いたい部分にはポインタを使うことで実現でき、特別な理由がない限り言語としては参照型は必要ないからです。ユーザ定義型 struct, interface だけでなく array も値型です (string, function は immutable)。

特別な理由とは例えば、slice は pointer と同じく本質的に他の値を参照する機能を持っているので参照型(逆に C 等のポインタと配列型がもつ配列を参照する機能は Go の pointer にはない)。channel は、通信は普通実体を共有し、実体の違うコピーが取れるのは直感に反するので、毎回ポインタを使うコストも掛かる uncopyable な型を作るよりは参照型の方が妥当。とかたぶんそんな感じです。

一方 map は、C++ ののように連想配列自体は値型で実現できます。Go にポインタ型がある以上それなりの理由がないと参照型ににはならないと思っていたので値型だと思い込んでいました。array が値型でも map は参照型の方が良いという人はいるんでしょうか。

macのTerminalでtmuxの罫線がズレる

症状:mac 標準の Terminal で tmux を使って切り分けたとき、水平の罫線が二行に渡る破線になり、また表示とカーソル位置が一部の画面でずれる。(バージョンとかは忘れた)

とりあえず設定から↓のチェックを外すと治りました。
□Unicode East Asian Ambiguous characters are wide

原因はおそらく Terminal が認識する全角文字セットの中に tmux が使う横罫線の半角文字が入ってなかったから。対して、この設定は東アジアのよく分からん文字をコードページの領域だけで判断(?)して全角文字扱いにしてしまうハックなので、tmux と Terminal の間で齟齬が生まれたっぽい。

Upgrading debian squeeze -> wheezy

1年以上放置してたけどしょうもない作業ログでお茶を濁す。

基本、公式ガイドに従うだけ。
Debian 7.0 (wheezy) リリースノート (64 ビット PC 用) > 第4章 Debian 6.0 (squeeze) からのアップグレード

詳しいのでトラブルシュートもできるし、実際やることは少ない。実質 4.3 と 4.4 だけやればいよい。APT のソースを更新して apt-get update と apt-get dist-upgrade を実行すれば終わり。

$ lsb_release -d
Description: Debian GNU/Linux 7.1 (wheezy)

その後、問題があれば依存性の壊れた非公式パッケージを整理したり /etc 以下を調整したりするだけ。

うちの環境では http が見えなくなったので suphp.conf だけ直した。仕様変更なのかデフォルトの設定が変わったのかは知らない。

/etc/suphp/suphp.conf の以下の行を修正

docroot=/var/www:${HOME}/public_html:...  ; apacheからphpを使いうるパスを全て列挙
min_uid=33  ; apacheからphpを動かすユーザの中で最小のuid (www-data)
min_gid=33  ; 同じくgid

動いた。

emacs org-mode (beamer) -> latex -> pdf

No definition for class `beamer' in `org-export-latex-classes'

org-modeで書かれたファイルをpdf化しようとorg-export-as-latexするとこのエラーが出た。
2つのパッケージ org-mode latex-beamer を入れると幸せになった。

詳細は、emacsが変数org-export-latex-classesを参照してdocumentclassに応じたヘッダを挿入しようとするが、beamerは定義されていないということらしい。.emacsとかで定義してもいいが、org-modeを入れるとデフォルトでbeamerのテンプレも定義しておいてくれる。latex-beamerにはたぶんstyとか入ってるので、latex&dvipdfmxしてpdfできあがり。

org-export-as-pdfで一気にpdf化までしてくれた。やってる事はたぶん同じ。

Debianでコマンドからメールを送る設定

コマンドでメールを使えるようにする設定。調べてみるとmailやsendmailなどのコマンドの使い方は色々出てくるが、SMTPの設定が中々出なかったので覚書。因みにSMTPを設定しなくてもUNIX系標準のメール機能は有効で、インターネット経由のEメール配送チェインに流すためにこの設定を行う。

Debian(squeeze)のMTAはデフォルトでexim4で、TUIから設定できる。
詳しくは参考サイト http://mizupc8.bio.mie-u.ac.jp/pukiwiki/index.php?Debian/Exim4

$ sudo dpkg-reconfigure exim4-config

使い方

次のコマンドにメールを送信できる。標準入力が本文。

$ mail -s '件名' example@example.com

補足

実質的にはローカルにメールサーバを建てるのと同じで、MTAは何でもいい。

  • Postfix: 柔軟で簡単で近代的な感じで情報も多い。
  • exim: よく知らない。単一バイナリらしい。
  • qmail: セキュアで単純で高速。機能拡張は面倒。
  • sendmail: 超有名な古いMTAで設定がやたら柔軟で難解。
  • 他は全く知らない

log:virt-install

  • 大分昔にしたpythonのアップグレードができてなかった
  • Kernel Configuration弄るの忘れてた

gentooでvirt-installを実行しようとしたところ、

$ sudo virt-install --prompt
Traceback (most recent call last):
  File "/usr/bin/virt-install-2.7", line 33, in <module>
    import urlgrabber.progress as progress
ImportError: No module named urlgrabber.progress
$ qcheck urlgrabber
Checking dev-python/urlgrabber-3.9.1-r1 ...
  * 25 out of 25 files are good
$ qlist urlgrabber
/usr/bin/urlgrabber-2.6
/usr/bin/urlgrabber
/usr/lib/python2.6/site-packages/urlgrabber-3.9.1-py2.6.egg-info
/usr/lib/python2.6/site-packages/urlgrabber/mirror.py
/usr/lib/python2.6/site-packages/urlgrabber/progress.py
/usr/lib/python2.6/site-packages/urlgrabber/grabber.py
/usr/lib/python2.6/site-packages/urlgrabber/__init__.py
/usr/lib/python2.6/site-packages/urlgrabber/byterange.py
/usr/share/doc/urlgrabber-3.9.1/TODO
/usr/share/doc/urlgrabber-3.9.1/LICENSE
/usr/share/doc/urlgrabber-3.9.1/README
/usr/share/doc/urlgrabber-3.9.1/ChangeLog
/usr/share/doc/urlgrabber-3.9.1-r1/TODO.bz2
/usr/share/doc/urlgrabber-3.9.1-r1/README.bz2
/usr/share/doc/urlgrabber-3.9.1-r1/ChangeLog.bz2
$ head -n1 /usr/bin/virt-install-2.7
#!/usr/bin/python2.7 -tt
$ python -V
Python 2.7.2

モジュールが無いのかと思ったけど、python2.7を使ってるのに2.6にしか入ってないということらしい。
pythonのバージョン変えてからモジュールの更新とかしてなかったのかも(かなり昔だったような…)
名前的にそれらしいもの(python-updater)があったのでとりあえず実行してみる。

$ sudo emerge python-updater
...
$ sudo python-updater
... # 時間かかる
$ sudo virt-install --prompt
ERROR    Virtual network 'default' has not been started.
$ sudo virsh net-start default
エラー: ネットワーク default の起動に失敗しました
エラー: ブリッジ'virbr0' を作成できません: パッケージはインストールされていません
$ qcheck bridge-utils
Checking net-misc/bridge-utils-1.4 ...
  * 25 out of 25 files are good

python の問題は解決した。
続いて Kernel Configuration から Bridge を有効にして reboot したら動いたので終わり。

Symbol: BRIDGE
Type  : tristate
Prompt: 802.1d Ethernet Bridging
  Defined at net/bridge/Kconfig:5
  Depends on: NET [=y] && (IPV6 [=n] || IPV6 [=n]=n)
  Location:
    -> Networking support (NET [=y])
      -> Networking options
  Selects: LLC [=m] && STP [=m]

Win32アプリケーションにVisual Styleを適用する

Visual Studio 2005以降でWin32アプリケーションを作ったときに、コモンコントロールの Visual Style を適用する簡単な方法をまとめる。(.NETからだと何もしなくても使えるのに…)

コモンコントロールの比較(左:旧,右:新)

Visual Style の仕組み

コモンコントロールが実装されている ComCtrl32.dll の読み込むバージョンに6以降を指定すればその見た目を変えられるらしい。Side by Side (SxS) というインスタンス(exe)によって違うバージョンのdllを読み込む機能に指示を与えるためにマニフェスト(manifest)を埋め込む。 マニフェストはxmlで書かれており、VCのリンカーからある程度自動生成できる。(マニフェストツールから追加情報を与えることもできるが、ファイルを新たに作るのが面倒だったので、今回はリンカーオプションから設定した。)また、Vistaから増えた新しいコモンコントロールも使えるようになる。

設定方法

プロジェクトのプロパティ ≫ 構成プロパティ > リンカー > マニフェスト ファイル ≫ 追加のマニフェスト依存関係

に次の行を貼り付ける。(x86の場合)

type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'

複数のアーキテクチャを設定する場合は x86 を amd64 などに変更するか、VS2010ならマクロ $(ProcessorArchitecture) が使えるのでまとめて設定できる。

プロパティページから設定

ref:

『ふしぎな塔と古代の宝』を頒布しました

表紙コミケ初参加の事後報告です。

8/13(土)、C80二日目東X-01aにてCheeseCakeGamesから新作同人ゲーム『ふしぎな塔と古代の宝』を頒布しました。私はサブプログラマとして製作に参加しています。

ゲーム内容は「ローグライク×3Dアクション」で、主人公が剣と魔法を駆使して行く手を阻む敵を倒しながら立体迷路を攻略し塔を登ってゆき宝を集めハイスコアを目指します。

あまり事前告知をしておらず直前に各SNS等で流した程度でしたが、知名度もないのに予想外の盛況で1時半くらいには完売しました。お求めいただいた皆様、ありがとうございます。持っていける限り用意したつもりの約120本でしたが早々に品切れになってしまい、興味を持って頂いたのに提供できなかった方々は申し訳ありませんでした。

公式サイトはただ今準備中です。ゲームの詳しい紹介やプレイ情報共有、またフィードバックを受け付ける予定です。

DebianにDropboxを入れる

検証環境:debian-squeeze(amd64), nautilus-dropbox_0.6.8_amd64.deb
予想対象:linux, nautilus-dropbox

公式dropboxクライアントのLinux版(GUIのみ)はUbuntu,Fedora,Archive版を配布してる。今使ってるDebianはGNOME仕様なのでUbuntu版をそのまま使えそうな気がする。ビルドするのも若干面倒なのでとりあえず試してみる。

$ sudo dpkg -i nautilus-dropbox_0.6.8_amd64.deb
...
dpkg: dependency problems prevent configuration of nautilus-dropbox:
 nautilus-dropbox depends on libnautilus-extension1 (>= 1:2.22.2); however:
  Version of libnautilus-extension1 on system is 2.30.1-2squeeze1.
...

libnautilus-extension1のバージョンが2.30.1-2squeeze1で1:2.22.2以上じゃないと言われるが、どう見ても2.30.1>=2.22.2で大丈夫そうに見える。頭の1:が悪さしてる気がするので消してみる。

# dropboxというディレクトリに展開して再アーカイブする
$ dpkg -x nautilus-dropbox_0.6.8_amd64.deb dropbox
$ dpkg -e nautilus-dropbox_0.6.8_amd64.deb dropbox/DEBIAN
$ nano dropbox/DEBIAN/control    # お好きなエディタで

# 次のように修正(libnautilus-extension1の"1:"を消す)
Depends: libatk1.0-0 (>= 1.20.0), libc6 (>= 2.4), libcairo2 (>= 1.6.0), libglib2.0-0 (>= 2.16.0), libgtk2.0-0 (>= 2.12.0), libnautilus-extension1 (>= 2.22.2), libpango1.0-0 (>= 1.20.1), python (>= 2.5), python-gtk2 (>= 2.12)

$ dpkg -b dropbox
# dropbox.debが作成される
$ sudo dpkg -i dropbox.deb

入った。

ブラウザからsvnの特定のリビジョンを見る

svnをwebdav経由で使える事が前提。

ブラウザからリポジトリのURLにアクセスすると常に最新版が表示されるが、次のように下線部をURLに挿入してアクセスすると特定のリビジョンのファイルツリーが同じように表示される。

http://<repository>/!svn/bc/<revision>/<path>

svnコマンドを打ったときのapacheのログから見つけただけなので、正しい使い方なのかは分からない。(そもそもドキュメントを読んだことが無い)

logとかannotationもブラウザから簡単に見れたらいいのに。